Compare commits

..

29 Commits

Author SHA1 Message Date
92028a7e44 JVXETable提供更便捷的三级联动机制,解决联动展示与选择BUG #2867 2021-08-20 11:13:42 +08:00
8ce40fa3d4 升级2.4.6后Online表单开发无法使用“一对多”的“ERP主题” issues/I468JY 2021-08-18 20:23:53 +08:00
05c7f76484 列表点击详情,出现id,好难看 #2922 2021-08-18 16:25:10 +08:00
71a1e9a63b oline在线内嵌子表主表与附表,设置扩展参数限制宽度不起作用 #2881
online java 增强当设置的增强过多时,显示异常 #2880
文本太长时,会遮挡页面issues/I44F0R
2021-08-18 15:59:44 +08:00
0606aa560a mybatisPlus升级,拦截器排除写法修改 2021-08-17 13:48:26 +08:00
1bbca48ba8 sqlserver同步数据库报错,SQL to parse以后与sqlserver不兼容 #2915
【升级mybatisPlus导致,多租户插件SqlServer下给添加字段sql多加了一个column】
2021-08-17 13:35:30 +08:00
d0c15f2302 解决新版IE11兼容问题 2021-08-17 11:50:00 +08:00
c9ff5d51b4 解决2.4.6新版问题:js增强点击无效 #2912 2021-08-16 16:38:00 +08:00
cce5d785e4 解决 升级到2.4.6后button type='danger'时的样式 #2909 2021-08-15 10:14:25 +08:00
234022d905 修复低级bug #2906 2021-08-14 19:39:42 +08:00
ab529aaf6c 2.4.6版本发布 2021-08-13 20:36:27 +08:00
07c6d1a23d 2.4.6 版本发布 2021-08-13 20:35:59 +08:00
8f780e180e JeecgBoot 2.4.6版本发布 2021-08-13 16:55:43 +08:00
87f17b9fc5 JeecgBoot 2.4.6版本发布 2021-08-13 16:18:52 +08:00
5a93d001b4 JeecgBoot 2.4.6版本发布(gateway默认采用database中的路由配置) 2021-08-13 16:18:31 +08:00
d822552e0c JeecgBoot 2.4.6版本发布(调整nacos配置) 2021-08-13 16:18:03 +08:00
832fa30cc9 JeecgBoot 2.4.6版本发布 2021-08-13 15:28:22 +08:00
3af1b390f1 JeecgBoot 2.4.6版本发布 2021-08-13 15:27:53 +08:00
a3695151dc JeecgBoot 2.4.6版本发布 2021-08-13 15:27:32 +08:00
c269d7637f JeecgBoot 2.4.6版本发布 2021-08-13 15:26:35 +08:00
664413e5d7 JeecgBoot 2.4.6版本发布 2021-08-13 15:26:20 +08:00
986f909628 issue格式要求 2021-08-09 16:25:21 +08:00
221940cc5f issue格式要求,发issue请详细 2021-08-09 16:13:32 +08:00
2a392fb738 issue格式要求 2021-08-02 18:06:14 +08:00
2b47cd0c34 升级下说明 2021-07-28 17:28:51 +08:00
1900f3fe77 Sign 签名校验失败 #2728 2021-07-01 12:45:17 +08:00
37fe6fea69 表字典接口存在SQL注入漏洞,增加签名拦截器 自定义组件验签失败 issues/I3XNK1 2021-06-25 15:30:47 +08:00
3fbb5ee4ad 登录账号提示修改 2021-06-25 13:41:33 +08:00
b1958fd295 HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器 2021-06-25 11:50:34 +08:00
200 changed files with 15363 additions and 8511 deletions

View File

@ -9,4 +9,9 @@
友情提示: 未按格式要求发帖,会直接删掉。 #### 友情提示为了提高issue处理效率
- 未按格式要求发帖,会被直接删掉;
- 请自己初判问题描述是否清楚,是否方便我们调查处理;
- 针对问题请说明是Online在线功能(需说明用的主题模板),还是生成的代码功能;
- 描述过于简单或模糊,导致无法处理的,会被直接删掉;

View File

@ -7,12 +7,12 @@
JEECG BOOT 低代码开发平台(前后端分离版本) JEECG BOOT 低代码开发平台(前后端分离版本)
=============== ===============
当前最新版本: 2.4.5发布日期2021-06-07 当前最新版本: 2.4.6发布日期2021-08-16
[![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-北京国炬软件-orange.svg)](http://www.jeecg.com) [![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/version-2.4.5-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot) [![](https://img.shields.io/badge/version-2.4.6-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot) [![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot) [![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -44,14 +44,14 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
- 技术官网: [http://www.jeecg.com](http://www.jeecg.com) - 技术官网: [http://www.jeecg.com](http://www.jeecg.com)
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
- 微服务启动: [单体升级为微服务启动文档2.4+](http://doc.jeecg.com/2043906)
- 在线演示 [http://boot.jeecg.com](http://boot.jeecg.com) - 在线演示 [http://boot.jeecg.com](http://boot.jeecg.com)
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
- 视频教程 [JeecgBoot入门视频](http://www.jeecg.com/doc/video) - 视频教程 [JeecgBoot入门视频](http://www.jeecg.com/doc/video)
- 微服务启动: [单体升级为微服务启动文档2.4+](http://doc.jeecg.com/2043906)
- 常见问题: [入门常见问题Q&A](http://jeecg.com/doc/qa) - 常见问题: [入门常见问题Q&A](http://jeecg.com/doc/qa)
- 更新日志: [版本日志](http://www.jeecg.com/doc/log) - 更新日志: [版本日志](http://www.jeecg.com/doc/log)
@ -139,7 +139,9 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
- 微服务框架: Spring Cloud Alibaba 2.2.3.RELEASE - 微服务框架: Spring Cloud Alibaba 2.2.3.RELEASE
- 持久层框架Mybatis-plus 3.4.1 - 持久层框架Mybatis-plus 3.4.3.1、Minidao
- 报表工具: jimureport 1.3.78
- 安全框架Apache Shiro 1.7.0Jwt 3.11.0 - 安全框架Apache Shiro 1.7.0Jwt 3.11.0

View File

@ -1,7 +1,7 @@
Ant Design Jeecg Vue Ant Design Jeecg Vue
==== ====
当前最新版本: 2.4.5发布日期20210607 当前最新版本: 2.4.6发布日期20210816
Overview Overview
---- ----

View File

@ -1,6 +1,6 @@
{ {
"name": "vue-antd-jeecg", "name": "vue-antd-jeecg",
"version": "2.4.5", "version": "2.4.6",
"private": true, "private": true,
"scripts": { "scripts": {
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ", "pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
@ -11,7 +11,7 @@
}, },
"dependencies": { "dependencies": {
"ant-design-vue": "^1.7.2", "ant-design-vue": "^1.7.2",
"@jeecg/antd-online-mini": "2.4.5-RC", "@jeecg/antd-online-mini": "2.4.6-beta5",
"@antv/data-set": "^0.11.4", "@antv/data-set": "^0.11.4",
"viser-vue": "^2.4.8", "viser-vue": "^2.4.8",
"axios": "^0.18.0", "axios": "^0.18.0",
@ -39,7 +39,7 @@
"tinymce": "^5.3.2", "tinymce": "^5.3.2",
"@toast-ui/editor": "^2.1.2", "@toast-ui/editor": "^2.1.2",
"vue-area-linkage": "^5.1.0", "vue-area-linkage": "^5.1.0",
"area-data": "^5.0.6", "china-area-data": "^5.0.1",
"dom-align": "1.12.0", "dom-align": "1.12.0",
"xe-utils": "2.4.8", "xe-utils": "2.4.8",
"vxe-table": "2.9.13", "vxe-table": "2.9.13",

View File

@ -12,9 +12,9 @@
<span style="margin-left:5px">{{ ellipsisFileName }}</span> <span style="margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip> </a-tooltip>
<a-tooltip v-else :title="file.name"> <a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="paper-clip" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
<span style="color:red;margin-left:5px">{{ ellipsisFileName }}</span> <span style="margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip> </a-tooltip>
<template style="width: 30px"> <template style="width: 30px">
@ -179,8 +179,19 @@
value['responseName'] = file.response[this.responseName] value['responseName'] = file.response[this.responseName]
} }
if (file.status === 'done') { if (file.status === 'done') {
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[this.responseName] value['path'] = file.response[this.responseName]
this.handleChangeCommon(value) this.handleChangeCommon(value)
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
}
} else if (file.status === 'error') { } else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误' value['message'] = file.response.message || '未知错误'
} }

View File

@ -10,20 +10,9 @@
<template v-else-if="file['path']"> <template v-else-if="file['path']">
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/> <img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
</template> </template>
<template v-else> <a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError">
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError"/>
</template>
<template slot="addonBefore" style="width: 30px">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-icon type="exclamation-circle" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip> </a-tooltip>
</template>
<template style="width: 30px"> <template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;"> <a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
@ -196,8 +185,19 @@
value['responseName'] = file.response[this.responseName] value['responseName'] = file.response[this.responseName]
} }
if (file.status === 'done') { if (file.status === 'done') {
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[this.responseName] value['path'] = file.response[this.responseName]
this.handleChangeCommon(value) this.handleChangeCommon(value)
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
}
} else if (file.status === 'error') { } else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误' value['message'] = file.response.message || '未知错误'
} }

View File

@ -1,5 +1,3 @@
import { pcaa } from 'area-data'
/** /**
* 省市区 * 省市区
*/ */
@ -8,7 +6,7 @@ export default class Area {
* 构造器 * 构造器
* @param express * @param express
*/ */
constructor() { constructor(pcaa) {
let arr = [] let arr = []
const province = pcaa['86'] const province = pcaa['86']
Object.keys(province).map(key=>{ Object.keys(province).map(key=>{
@ -17,9 +15,11 @@ export default class Area {
Object.keys(city).map(key2=>{ Object.keys(city).map(key2=>{
arr.push({id:key2, text:city[key2], pid:key, index:2}); arr.push({id:key2, text:city[key2], pid:key, index:2});
const qu = pcaa[key2]; const qu = pcaa[key2];
if(qu){
Object.keys(qu).map(key3=>{ Object.keys(qu).map(key3=>{
arr.push({id:key3, text:qu[key3], pid:key2, index:3}); arr.push({id:key3, text:qu[key3], pid:key2, index:3});
}) })
}
}) })
}) })
this.all = arr; this.all = arr;

View File

@ -33,3 +33,14 @@ export const cutStrByFullLength = (str = '', maxLength) => {
return pre return pre
}, '') }, '')
} }
// 下划线转换驼峰
export function underLinetoHump(name) {
return name.replace(/\_(\w)/g, function(all, letter){
return letter.toUpperCase();
});
}
// 驼峰转换下划线
export function humptoUnderLine(name) {
return name.replace(/([A-Z])/g,"_$1").toLowerCase();
}

View File

@ -29,7 +29,6 @@
</template> </template>
<script> <script>
import { pcaa } from 'area-data'
import Area from '@/components/_util/Area' import Area from '@/components/_util/Area'
export default { export default {
@ -53,7 +52,7 @@
}, },
data() { data() {
return { return {
pcaa, pcaa: this.$Jpcaa,
innerValue: [], innerValue: [],
usedListeners: ['change'], usedListeners: ['change'],
enums: { enums: {
@ -114,7 +113,7 @@
/** 通过地区code获取子级 */ /** 通过地区code获取子级 */
loadDataByCode(value) { loadDataByCode(value) {
let options = [] let options = []
let data = pcaa[value] let data = this.pcaa[value]
if (data) { if (data) {
for (let key in data) { for (let key in data) {
if (data.hasOwnProperty(key)) { if (data.hasOwnProperty(key)) {
@ -139,7 +138,7 @@
}, },
initAreaData(){ initAreaData(){
if(!this.areaData){ if(!this.areaData){
this.areaData = new Area(); this.areaData = new Area(this.$Jpcaa);
} }
}, },

View File

@ -400,6 +400,10 @@
.null-tip-hidden{ .null-tip-hidden{
display: none; display: none;
} }
/**选中样式偶然出现高度不够的情况*/
.CodeMirror-selected{
min-height: 19px !important;
}
} }
/* 全屏样式 */ /* 全屏样式 */

View File

@ -8,7 +8,7 @@
:showTime="showTime" :showTime="showTime"
:format="dateFormat" :format="dateFormat"
:getCalendarContainer="getCalendarContainer" :getCalendarContainer="getCalendarContainer"
/> v-bind="$attrs"/>
</template> </template>
<script> <script>
import moment from 'moment' import moment from 'moment'

View File

@ -158,7 +158,49 @@ export default {
cronValue_c(newVal, oldVal) { cronValue_c(newVal, oldVal) {
this.calTriggerList() this.calTriggerList()
this.$emit('change', newVal) this.$emit('change', newVal)
this.assignInput()
},
minute() {
if (this.second === '*') {
this.second = '0'
}
},
hour() {
if (this.minute === '*') {
this.minute = '0'
}
},
day(day) {
if (day !== '?' && this.hour === '*') {
this.hour = '0'
}
},
week(week) {
if (week !== '?' && this.hour === '*') {
this.hour = '0'
}
},
month() {
if (this.day === '?' && this.week === '*') {
this.week = '1'
} else if (this.week === '?' && this.day === '*') {
this.day = '1'
}
},
year() {
if (this.month === '*') {
this.month = '1'
}
},
},
created() {
this.formatValue()
this.$nextTick(() => {
this.calTriggerListInner()
})
},
methods: {
assignInput() {
Object.assign(this.inputValues, { Object.assign(this.inputValues, {
second: this.second, second: this.second,
minute: this.minute, minute: this.minute,
@ -169,15 +211,7 @@ export default {
year: this.year, year: this.year,
cron: this.cronValue_c, cron: this.cronValue_c,
}) })
}
}, },
created() {
this.formatValue()
this.$nextTick(() => {
this.calTriggerListInner()
})
},
methods: {
formatValue() { formatValue() {
if (!this.cronValue) return if (!this.cronValue) return
const values = this.cronValue.split(' ').filter(item => !!item) const values = this.cronValue.split(' ').filter(item => !!item)
@ -190,6 +224,7 @@ export default {
if (values.length > i) this.month = values[i++] if (values.length > i) this.month = values[i++]
if (values.length > i) this.week = values[i++] if (values.length > i) this.week = values[i++]
if (values.length > i) this.year = values[i] if (values.length > i) this.year = values[i]
this.assignInput()
}, },
calTriggerList: simpleDebounce(function () { calTriggerList: simpleDebounce(function () {
this.calTriggerListInner() this.calTriggerListInner()

View File

@ -38,8 +38,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i of specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>

View File

@ -89,6 +89,9 @@ export default {
result.push('L') result.push('L')
break break
case TYPE_SPECIFY: case TYPE_SPECIFY:
if (this.valueList.length === 0) {
this.valueList.push(this.minValue)
}
result.push(this.valueList.join(',')) result.push(this.valueList.join(','))
break break
default: default:
@ -96,7 +99,15 @@ export default {
break break
} }
return result.length > 0 ? result.join('') : this.DEFAULT_VALUE return result.length > 0 ? result.join('') : this.DEFAULT_VALUE
},
// 指定值范围区间,介于最小值和最大值之间
specifyRange() {
let range = []
for (let i = this.minValue; i <= this.maxValue; i++) {
range.push(i)
} }
return range
},
}, },
methods: { methods: {
parseProp (value) { parseProp (value) {

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i of specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>

View File

@ -36,8 +36,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio> <a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list"> <div class="list">
<a-checkbox-group v-model="valueList"> <a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1"> <template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox> <a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template> </template>
</a-checkbox-group> </a-checkbox-group>
</div> </div>
@ -51,13 +51,14 @@ import mixin from './mixin'
import { replaceWeekName, WEEK_MAP_EN } from './const.js' import { replaceWeekName, WEEK_MAP_EN } from './const.js'
const WEEK_MAP = { const WEEK_MAP = {
'周日': 0,
'周一': 1, '周一': 1,
'周二': 2, '周二': 2,
'周三': 3, '周三': 3,
'周四': 4, '周四': 4,
'周五': 5, '周五': 5,
'周六': 6 '周六': 6,
// 按照国人习惯,将周日放到每周的最后一天
'周日': 7,
} }
export default { export default {
@ -101,10 +102,10 @@ export default {
created() { created() {
this.DEFAULT_VALUE = '*' this.DEFAULT_VALUE = '*'
// 0,7表示周日 1表示周一 // 0,7表示周日 1表示周一
this.minValue = 0 this.minValue = 1
this.maxValue = 6 this.maxValue = 7
this.valueRange.start = 0 this.valueRange.start = 1
this.valueRange.end = 6 this.valueRange.end = 7
this.valueLoop.start = 2 this.valueLoop.start = 2
this.valueLoop.interval = 1 this.valueLoop.interval = 1
this.parseProp(this.prop) this.parseProp(this.prop)

View File

@ -1,5 +1,5 @@
<!-- JEditableTable --> <!-- JEditableTable -->
<!-- @version 1.6.1 --> <!-- @version 1.6.2 -->
<!-- @author sjlei --> <!-- @author sjlei -->
<template> <template>
<a-spin :spinning="loading"> <a-spin :spinning="loading">
@ -11,7 +11,33 @@
<a-col> <a-col>
<!-- 操作按钮 --> <!-- 操作按钮 -->
<div v-if="actionButton" class="action-button"> <div v-if="actionButton" class="action-button">
<a-button v-if="buttonPermission('add')" type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button> <a-button-group v-if="buttonPermission('add')">
<a-button type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button>
<a-popover v-if="addButtonSettings" placement="right" overlayClassName="j-add-btn-settings">
<a-row slot="title">
<a-col :span="12">选项</a-col>
<a-col :span="12" style="text-align: right;">
<a-tooltip title="保存为默认值">
<a-button type="link" icon="save" size="small" style="position: relative;left:4px;" @click="onAddButtonSettingsSave"/>
</a-tooltip>
</a-col>
</a-row>
<template slot="content">
<a-form-model layout="horizontal" :labelCol="{span:8}" :wrapperCol="{span:16}">
<a-form-model-item label="添加行数">
<a-input-number v-model="settings.addRowNum" :min="1"/>
</a-form-model-item>
<a-form-model-item label="添加位置">
<a-input-number v-model="settings.addIndex" :min="0" :max="rows.length"/>
<p style="font-size: 12px;color:#aaa;line-height: 14px;text-align: right;margin: 0;">0 = 最底部</p>
</a-form-model-item>
<a-divider style="margin: 8px 0;"/>
<a-checkbox v-model="settings.addScrollToBottom">添加后滚动到底部</a-checkbox>
</a-form-model>
</template>
<a-button icon="setting" type="primary"></a-button>
</a-popover>
</a-button-group>
<span class="gap"></span> <span class="gap"></span>
<template v-if="selectedRowIds.length>0"> <template v-if="selectedRowIds.length>0">
<a-popconfirm <a-popconfirm
@ -318,7 +344,7 @@
<a-tooltip v-else-if="file.status==='done'" title="上传完成"> <a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/> <a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip> </a-tooltip>
<a-tooltip v-else title="上传失败"> <a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip> </a-tooltip>
</template> </template>
@ -409,9 +435,9 @@
<span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span> <span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
</a-tooltip> </a-tooltip>
<a-tooltip v-else :title="file.name"> <a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="paper-clip" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
<span style="color:red;margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span> <span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
</a-tooltip> </a-tooltip>
<template style="width: 30px"> <template style="width: 30px">
@ -464,20 +490,9 @@
<template v-else-if="uploadValues[id]['path']"> <template v-else-if="uploadValues[id]['path']">
<img class="j-editable-image" :src="getCellImageView(id)" alt="无图片" @click="handleMoreOperation(id,'img',col)"/> <img class="j-editable-image" :src="getCellImageView(id)" alt="无图片" @click="handleMoreOperation(id,'img',col)"/>
</template> </template>
<template v-else> <a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError(id)">
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError(id)"/>
</template>
<template slot="addonBefore" style="width: 30px">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-icon type="exclamation-circle" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip> </a-tooltip>
</template>
<template style="width: 30px"> <template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" :getPopupContainer="getParentContainer" style="margin-left: 10px;"> <a-dropdown :trigger="['click']" placement="bottomRight" :getPopupContainer="getParentContainer" style="margin-left: 10px;">
@ -738,6 +753,11 @@
type: Boolean, type: Boolean,
default: false default: false
}, },
// 是否显示添加按钮选项
addButtonSettings: {
type: Boolean,
default: false
},
// 是否显示行号 // 是否显示行号
rowNumber: { rowNumber: {
type: Boolean, type: Boolean,
@ -866,7 +886,16 @@
lastPushTimeMap: new Map(), lastPushTimeMap: new Map(),
number:0, number:0,
//不显示的按钮编码 //不显示的按钮编码
excludeCode:[] excludeCode:[],
// 选项配置
settings: {
// 添加行数
addRowNum: 1,
// 添加位置下标0 = 最底部
addIndex: 0,
// 添加后滚动到底部
addScrollToBottom: false,
},
} }
}, },
created() { created() {
@ -881,6 +910,7 @@
event.stopPropagation() event.stopPropagation()
} }
} }
this.getSavedAddButtonSettings()
}, },
// 计算属性 // 计算属性
computed: { computed: {
@ -1412,22 +1442,18 @@
let tbody = this.getElement('tbody') let tbody = this.getElement('tbody')
let offsetHeight = tbody.offsetHeight let offsetHeight = tbody.offsetHeight
let realScrollTop = tbody.scrollTop + offsetHeight let realScrollTop = tbody.scrollTop + offsetHeight
if (forceScrollToBottom === false) { if (forceScrollToBottom) {
// 只有滚动条在底部的时候才自动滚动
if (!((tbody.scrollHeight - realScrollTop) <= 10)) {
return
}
}
this.$nextTick(() => { this.$nextTick(() => {
tbody.scrollTop = tbody.scrollHeight this.resetScrollTop(this.$refs.scrollView.scrollHeight)
}) })
}
}, },
/** /**
* 在指定位置添加一行 * 在指定位置添加一行
* @param insertIndex 添加位置下标 * @param insertIndex 添加位置下标
* @param num 添加的行数默认1 * @param num 添加的行数默认1
*/ */
insert(insertIndex, num = 1) { insert(insertIndex, num = 1, forceScrollToBottom = false) {
if (this.checkTooFastClick('insert', 1500)) { if (this.checkTooFastClick('insert', 1500)) {
return return
} }
@ -1455,6 +1481,12 @@
num, insertIndex, num, insertIndex,
target: this target: this
}) })
// 设置滚动条位置
if (forceScrollToBottom) {
this.$nextTick(() => {
this.resetScrollTop(this.$refs.scrollView.scrollHeight)
})
}
}, },
/** 删除被选中的行 */ /** 删除被选中的行 */
removeSelectedRows() { removeSelectedRows() {
@ -2095,7 +2127,12 @@
}, },
handleClickAdd() { handleClickAdd() {
this.add() let {addRowNum, addIndex, addScrollToBottom} = this.settings
if (addIndex <= 0) {
this.add(addRowNum, addScrollToBottom)
} else {
this.insert(addIndex, addRowNum, addScrollToBottom)
}
}, },
handleConfirmDelete() { handleConfirmDelete() {
this.removeSelectedRows() this.removeSelectedRows()
@ -2353,7 +2390,21 @@
value['responseName'] = file.response[column.responseName] value['responseName'] = file.response[column.responseName]
} }
if (file.status === 'done') { if (file.status === 'done') {
if (typeof file.response.success === 'boolean') {
// 如果文件上传被拦截器拦下还会返回最外层的status = done
// 但是内部的success会返回false并携带异常信息
// 整个上传操作还是失败的
// https://github.com/zhangdaiscott/jeecg-boot/issues/2691
if (file.response.success) {
value['path'] = file.response[column.responseName] value['path'] = file.response[column.responseName]
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[column.responseName]
}
} else if (file.status === 'error') { } else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误' value['message'] = file.response.message || '未知错误'
} }
@ -2415,6 +2466,25 @@
}) })
} }
}, },
/** 添加按钮设置保存为默认值 */
onAddButtonSettingsSave() {
let obj = {
addRowNum: this.settings.addRowNum,
addIndex: this.settings.addIndex,
addScrollToBottom: this.settings.addScrollToBottom,
}
this.$ls.set('jet-add-btn-settings', obj)
this.$message.success('保存成功')
},
/** 获取保存的添加按钮默认值 */
getSavedAddButtonSettings() {
let obj= this.$ls.get('jet-add-btn-settings')
if (obj) {
Object.assign(this.settings, obj)
}
},
/** 记录用到数据绑定的组件的值 */ /** 记录用到数据绑定的组件的值 */
bindValuesChange(value, id, key) { bindValuesChange(value, id, key) {
this.$set(this[key], id, value) this.$set(this[key], id, value)
@ -3280,3 +3350,19 @@
} }
</style> </style>
<style lang="less">
// 新增按钮配置气泡的样式
.j-add-btn-settings {
width: 240px;
.ant-form {
.ant-form-item {
margin-bottom: 0;
.ant-input-number {
width: 100%;
}
}
}
}
</style>

View File

@ -134,9 +134,17 @@
}else{ }else{
//update--begin--autor:wangshuai-----date:20200724------for富文本编辑器切换tab无法修改------ //update--begin--autor:wangshuai-----date:20200724------for富文本编辑器切换tab无法修改------
let tabLayout = getVmParentByName(this, 'TabLayout') let tabLayout = getVmParentByName(this, 'TabLayout')
//update--begin--autor:liusq-----date:20210713------for处理特殊情况excuteCallback不能使用------
try {
tabLayout.excuteCallback(() => { tabLayout.excuteCallback(() => {
this.reload() this.reload()
}) })
} catch (error) {
if (tabLayout) {
this.reload()
}
}
//update--end--autor:liusq-----date:20210713------for处理特殊情况excuteCallback不能使用------
//update--begin--autor:wangshuai-----date:20200724------for文本编辑器切换tab无法修改------ //update--begin--autor:wangshuai-----date:20200724------for文本编辑器切换tab无法修改------
} }
}, },

View File

@ -0,0 +1,242 @@
<template>
<a-modal
ref="modal"
:class="getClass(modalClass)"
:style="getStyle(modalStyle)"
:visible="visible"
v-bind="_attrs"
v-on="$listeners"
@ok="handleOk"
@cancel="handleCancel"
destroyOnClose
>
<slot></slot>
<!--有设置标题-->
<template v-if="!isNoTitle" slot="title">
<a-row class="j-modal-title-row" type="flex">
<a-col class="left">
<slot name="title">{{ title }}</slot>
</a-col>
<a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen">
<a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/>
</a-col>
</a-row>
</template>
<!--没有设置标题-->
<template v-else slot="title">
<a-row class="j-modal-title-row" type="flex">
<a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen">
<a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/>
</a-col>
</a-row>
</template>
<!-- 处理 scopedSlots -->
<template v-for="slotName of scopedSlotsKeys" :slot="slotName">
<slot :name="slotName"></slot>
</template>
<!-- 处理 slots -->
<template v-for="slotName of slotsKeys" v-slot:[slotName]>
<slot :name="slotName"></slot>
</template>
</a-modal>
</template>
<script>
import { getClass, getStyle } from '@/utils/props-util'
import { triggerWindowResizeEvent } from '@/utils/util'
export default {
name: 'JModal',
props: {
title: String,
// 可使用 .sync 修饰符
visible: Boolean,
// 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符
fullscreen: {
type: Boolean,
default: false
},
// 是否允许切换全屏(允许后右上角会出现一个按钮)
switchFullscreen: {
type: Boolean,
default: false
},
// 点击确定按钮的时候是否关闭弹窗
okClose: {
type: Boolean,
default: true
},
},
data() {
return {
// 内部使用的 slots ,不再处理
usedSlots: ['title'],
// 实际控制是否全屏的参数
innerFullscreen: this.fullscreen,
}
},
computed: {
// 一些未处理的参数或特殊处理的参数绑定到 a-modal 上
_attrs() {
let attrs = { ...this.$attrs }
// 如果全屏就将宽度设为 100%
if (this.innerFullscreen) {
attrs['width'] = '100%'
}
return attrs
},
modalClass() {
return {
'j-modal-box': true,
'fullscreen': this.innerFullscreen,
'no-title': this.isNoTitle,
'no-footer': this.isNoFooter,
}
},
modalStyle() {
let style = {}
// 如果全屏就将top设为 0
if (this.innerFullscreen) {
style['top'] = '0'
}
return style
},
isNoTitle() {
return !this.title && !this.allSlotsKeys.includes('title')
},
isNoFooter() {
return this._attrs['footer'] === null
},
slotsKeys() {
return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))
},
scopedSlotsKeys() {
return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
},
allSlotsKeys() {
return Object.keys(this.$slots).concat(Object.keys(this.$scopedSlots))
},
// 切换全屏的按钮图标
fullscreenButtonIcon() {
return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'
},
},
watch: {
visible() {
if (this.visible) {
this.innerFullscreen = this.fullscreen
}
},
innerFullscreen(val) {
this.$emit('update:fullscreen', val)
},
},
methods: {
getClass(clazz) {
return { ...getClass(this), ...clazz }
},
getStyle(style) {
return { ...getStyle(this), ...style }
},
close() {
this.$emit('update:visible', false)
},
handleOk() {
if (this.okClose) {
this.close()
}
},
handleCancel() {
this.close()
},
/** 切换全屏 */
toggleFullscreen() {
this.innerFullscreen = !this.innerFullscreen
triggerWindowResizeEvent()
},
}
}
</script>
<style lang="less">
.j-modal-box {
&.fullscreen {
top: 0;
left: 0;
padding: 0;
// 兼容1.6.2版本的antdv
& .ant-modal {
top: 0;
padding: 0;
height: 100vh;
}
& .ant-modal-content {
height: 100vh;
border-radius: 0;
& .ant-modal-body {
/* title 和 footer 各占 55px */
height: calc(100% - 55px - 55px);
overflow: auto;
}
}
&.no-title, &.no-footer {
.ant-modal-body {
height: calc(100% - 55px);
}
}
&.no-title.no-footer {
.ant-modal-body {
height: 100%;
}
}
}
.j-modal-title-row {
.left {
width: calc(100% - 56px - 56px);
}
.right {
width: 56px;
position: inherit;
.ant-modal-close {
right: 56px;
color: rgba(0, 0, 0, 0.45);
&:hover {
color: rgba(0, 0, 0, 0.75);
}
}
}
}
&.no-title{
.ant-modal-header {
padding: 0px 24px;
border-bottom: 0px !important;
}
}
}
@media (max-width: 767px) {
.j-modal-box.fullscreen {
margin: 0;
max-width: 100vw;
}
}
</style>

View File

@ -0,0 +1,124 @@
<template>
<j-modal :visible="visible" :confirmLoading="loading" :after-close="afterClose" v-bind="modalProps" @ok="onOk" @cancel="onCancel">
<a-spin :spinning="loading">
<div v-html="content"></div>
<a-form-model ref="form" :model="model" :rules="rules">
<a-form-model-item prop="input">
<a-input ref="input" v-model="model.input" v-bind="inputProps" @pressEnter="onInputPressEnter"/>
</a-form-model-item>
</a-form-model>
</a-spin>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
export default {
name: 'JPrompt',
data() {
return {
visible: false,
loading: false,
content: '',
// 弹窗参数
modalProps: {
title: '',
},
inputProps: {
placeholder: '',
},
// form model
model: {
input: '',
},
// 校验
rule: [],
// 回调函数
callback: {},
}
},
computed: {
rules() {
return {
input: this.rule
}
},
},
methods: {
show(options) {
this.content = options.content
if (Array.isArray(options.rule)) {
this.rule = options.rule
}
if (options.defaultValue != null) {
this.model.input = options.defaultValue
}
// 取出常用的弹窗参数
let pickModalProps = pick(options, 'title', 'centered', 'cancelText', 'closable', 'mask', 'maskClosable', 'okText', 'okType', 'okButtonProps', 'cancelButtonProps', 'width', 'wrapClassName', 'zIndex', 'dialogStyle', 'dialogClass')
this.modalProps = Object.assign({}, pickModalProps, options.modalProps)
// 取出常用的input参数
let pickInputProps = pick(options, 'placeholder', 'allowClear')
this.inputProps = Object.assign({}, pickInputProps, options.inputProps)
// 回调函数
this.callback = pick(options, 'onOk', 'onOkAsync', 'onCancel')
this.visible = true
this.$nextTick(() => this.$refs.input.focus())
},
onOk() {
this.$refs.form.validate((ok, err) => {
if (ok) {
let event = {value: this.model.input, target: this}
// 异步方法优先级高于同步方法
if (typeof this.callback.onOkAsync === 'function') {
this.callback.onOkAsync(event)
} else if (typeof this.callback.onOk === 'function') {
this.callback.onOk(event)
this.close()
} else {
this.close()
}
}
})
},
onCancel() {
if (typeof this.callback.onCancel === 'function') {
this.callback.onCancel(this.model.input)
}
this.close()
},
onInputPressEnter() {
this.onOk()
},
close() {
this.visible = this.loading ? this.visible : false
},
forceClose() {
this.visible = false
},
showLoading() {
this.loading = true
},
hideLoading() {
this.loading = false
},
afterClose(e) {
if (typeof this.modalProps.afterClose === 'function') {
this.modalProps.afterClose(e)
}
this.$emit('after-close', e)
},
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,18 @@
import JModal from './JModal'
import JPrompt from './JPrompt'
export default {
install(Vue) {
Vue.component(JModal.name, JModal)
const JPromptExtend = Vue.extend(JPrompt)
Vue.prototype.$JPrompt = function (options = {}) {
// 创建prompt实例
const vm = new JPromptExtend().$mount()
vm.show(options)
// 关闭后销毁
vm.$on('after-close', () => vm.$destroy())
return vm
}
},
}

View File

@ -173,9 +173,11 @@
let tempDestArr = [] let tempDestArr = []
for(let rw of rows){ for(let rw of rows){
let val = rw[orgFieldsArr[i]] let val = rw[orgFieldsArr[i]]
if(!val){ // update--begin--autor:liusq-----date:20210713------for处理val等于0的情况issues/I3ZL4T------
if(typeof val=='undefined'|| val==null || val.toString()==""){
val = "" val = ""
} }
// update--end--autor:liusq-----date:20210713------for处理val等于0的情况issues/I3ZL4T------
tempDestArr.push(val) tempDestArr.push(val)
} }
res[destFieldsArr[i]] = tempDestArr.join(",") res[destFieldsArr[i]] = tempDestArr.join(",")

View File

@ -1,7 +1,7 @@
<template> <template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder"> <a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
<a-select-option <a-select-option
v-for="(item,index) in options" v-for="(item,index) in selectOptions"
:key="index" :key="index"
:getPopupContainer="getParentContainer" :getPopupContainer="getParentContainer"
:value="item.value"> :value="item.value">
@ -12,6 +12,8 @@
<script> <script>
//option {label:,value:} //option {label:,value:}
import { getAction } from '@api/manage'
export default { export default {
name: 'JSelectMultiple', name: 'JSelectMultiple',
props: { props: {
@ -31,7 +33,8 @@
}, },
options:{ options:{
type: Array, type: Array,
required: true default:()=>[],
required: false
}, },
triggerChange:{ triggerChange:{
type: Boolean, type: Boolean,
@ -48,12 +51,22 @@
default:'', default:'',
required:false required:false
}, },
dictCode:{
type:String,
required:false
},
}, },
data(){ data(){
return { return {
arrayValue:!this.value?[]:this.value.split(this.spliter) arrayValue:!this.value?[]:this.value.split(this.spliter),
dictOptions: [],
} }
}, },
computed:{
selectOptions(){
return this.dictOptions.length > 0 ? this.dictOptions : this.options
},
},
watch:{ watch:{
value (val) { value (val) {
if(!val){ if(!val){
@ -63,6 +76,11 @@
} }
} }
}, },
mounted(){
if (this.dictCode) {
this.loadDictOptions()
}
},
methods:{ methods:{
onChange (selectedValue) { onChange (selectedValue) {
if(this.triggerChange){ if(this.triggerChange){
@ -77,7 +95,18 @@
}else{ }else{
return document.querySelector(this.popContainer) return document.querySelector(this.popContainer)
} }
},
// 根据字典code查询字典项
loadDictOptions(){
getAction(`/sys/dict/getDictItems/${this.dictCode}`,{}).then(res=>{
if (res.success) {
this.dictOptions = res.result.map(item => ({value: item.value, label: item.text}))
} else {
console.error('getDictItems error: : ', res)
this.dictOptions = []
} }
})
},
}, },
} }

View File

@ -412,8 +412,9 @@
this.queryParamsModel.splice(index, 1) this.queryParamsModel.splice(index, 1)
}, },
handleSelected(node, item) { handleSelected(node, item) {
let { type, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef let { type, dbType, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef
item['type'] = type item['type'] = type
item['dbType'] = dbType
item['options'] = options item['options'] = options
item['dictCode'] = dictCode item['dictCode'] = dictCode
item['dictTable'] = dictTable item['dictTable'] = dictTable

View File

@ -17,7 +17,7 @@
:headers="headers" :headers="headers"
:data="{'biz':bizPath}" :data="{'biz':bizPath}"
:fileList="fileList" :fileList="fileList"
:beforeUpload="beforeUpload" :beforeUpload="doBeforeUpload"
@change="handleChange" @change="handleChange"
:disabled="disabled" :disabled="disabled"
:returnUrl="returnUrl" :returnUrl="returnUrl"
@ -139,6 +139,9 @@
type: Boolean, type: Boolean,
default: true default: true
}, },
beforeUpload: {
type: Function
},
}, },
watch:{ watch:{
value:{ value:{
@ -242,7 +245,7 @@
} }
this.$emit('change', path); this.$emit('change', path);
}, },
beforeUpload(file){ doBeforeUpload(file){
this.uploadGoOn=true this.uploadGoOn=true
var fileType = file.type; var fileType = file.type;
if(this.fileType===FILE_TYPE_IMG){ if(this.fileType===FILE_TYPE_IMG){
@ -252,7 +255,10 @@
return false; return false;
} }
} }
//TODO 扩展功能验证文件大小 // 扩展 beforeUpload 验证
if (typeof this.beforeUpload === 'function') {
return this.beforeUpload(file)
}
return true return true
}, },
handleChange(info) { handleChange(info) {

View File

@ -98,6 +98,8 @@ export default {
// 是否一直显示组件如果为false则只有点击的时候才出现组件 // 是否一直显示组件如果为false则只有点击的时候才出现组件
// 注:该参数不能动态修改;如果行、列字段多的情况下,会根据机器性能造成不同程度的卡顿。 // 注:该参数不能动态修改;如果行、列字段多的情况下,会根据机器性能造成不同程度的卡顿。
alwaysEdit: PropTypes.bool.def(false), alwaysEdit: PropTypes.bool.def(false),
// 联动配置,数组,详情配置见文档
linkageConfig: PropTypes.array.def(() => []),
}, },
data() { data() {
return { return {
@ -151,7 +153,10 @@ export default {
// 允许执行刷新特效的行ID // 允许执行刷新特效的行ID
reloadEffectRowKeysMap: {}, reloadEffectRowKeysMap: {},
//配置了但是没有授权的按钮和列 集合 //配置了但是没有授权的按钮和列 集合
excludeCode:[] excludeCode:[],
// 联动下拉选项(用于隔离不同的下拉选项)
// 内部联动配置map
_innerLinkageConfig: null,
} }
}, },
computed: { computed: {
@ -178,6 +183,18 @@ export default {
renderOptions.target = this renderOptions.target = this
} }
} }
// 处理联动列,联动列只能作用于 select 组件
if (column.$type === JVXETypes.select && this._innerLinkageConfig != null) {
// 判断当前列是否是联动列
if (this._innerLinkageConfig.has(column.key)) {
renderOptions.linkage = {
config: this._innerLinkageConfig.get(column.key),
getLinkageOptionsSibling: this.getLinkageOptionsSibling,
getLinkageOptionsAsync: this.getLinkageOptionsAsync,
linkageSelectChange: this.linkageSelectChange,
}
}
}
if (column.editRender) { if (column.editRender) {
Object.assign(column.editRender, renderOptions) Object.assign(column.editRender, renderOptions)
} }
@ -278,15 +295,21 @@ export default {
immediate: true, immediate: true,
async handler() { async handler() {
let vxe = await getRefPromise(this, 'vxe') let vxe = await getRefPromise(this, 'vxe')
// 阻断vue监听大数据提高性能
this.dataSource.forEach((data, idx) => {
// 开启了排序就自动计算排序值 // 开启了排序就自动计算排序值
if (this.dragSort) { if (this.dragSort) {
this.dataSource.forEach((data, idx) => {
this.$set(data, this.dragSortKey, idx + 1) this.$set(data, this.dragSortKey, idx + 1)
})
} }
// 处理联动回显数据
if (this._innerLinkageConfig != null) {
for (let configItem of this._innerLinkageConfig.values()) {
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
}
}
})
// 阻断vue监听大数据提高性能
vxe.loadData(this.dataSource) vxe.loadData(this.dataSource)
// TODO 解析disabledRows // TODO 解析disabledRows
@ -469,7 +492,40 @@ export default {
this._innerColumns = _innerColumns this._innerColumns = _innerColumns
this._innerEditRules = _innerEditRules this._innerEditRules = _innerEditRules
} }
},
// watch linkageConfig
// 整理多级联动配置
linkageConfig: {
immediate: true,
handler() {
if (Array.isArray(this.linkageConfig) && this.linkageConfig.length > 0) {
// 获取联动的key顺序
let getLcKeys = (key, arr) => {
let col = this._innerColumns.find(col => col.key === key)
if (col) {
arr.push(col.key)
if (col.linkageKey) {
return getLcKeys(col.linkageKey, arr)
} }
}
return arr
}
let configMap = new Map()
this.linkageConfig.forEach(lc => {
let keys = getLcKeys(lc.key, [])
// 多个key共享一个引用地址
let configItem = {
...lc, keys,
optionsMap: new Map()
}
keys.forEach(k => configMap.set(k, configItem))
})
this._innerLinkageConfig = configMap
} else {
this._innerLinkageConfig = null
}
}
},
}, },
created() { created() {
}, },
@ -668,7 +724,24 @@ export default {
async loadNewData(dataSource) { async loadNewData(dataSource) {
if (Array.isArray(dataSource)) { if (Array.isArray(dataSource)) {
let {xTable} = this.$refs.vxe.$refs let {xTable} = this.$refs.vxe.$refs
return await xTable.loadData(dataSource) // issues/2784
// 先清空所有数据
xTable.loadData([])
dataSource.forEach((data, idx) => {
// 开启了排序就自动计算排序值
if (this.dragSort) {
this.$set(data, this.dragSortKey, idx + 1)
}
// 处理联动回显数据
if (this._innerLinkageConfig != null) {
for (let configItem of this._innerLinkageConfig.values()) {
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
}
}
})
// 再新增
return xTable.insertAt(dataSource)
} }
return [] return []
}, },
@ -793,6 +866,7 @@ export default {
* 添加一行或多行 * 添加一行或多行
* *
* @param rows * @param rows
* @param isOnlJs 是否是onlineJS增强触发的
* @return * @return
*/ */
async addRows(rows = {}, isOnlJs) { async addRows(rows = {}, isOnlJs) {
@ -892,6 +966,89 @@ export default {
this.$emit(name, event) this.$emit(name, event)
}, },
/** 【多级联动】获取同级联动下拉选项 */
getLinkageOptionsSibling(row, col, config, request) {
// 如果当前列不是顶级列
let key = ''
if (col.key !== config.key) {
// 就找出联动上级列
let idx = config.keys.findIndex(k => col.key === k)
let parentKey = config.keys[idx - 1]
key = row[parentKey]
// 如果联动上级列没有选择数据,就直接返回空数组
if (key === '' || key == null) {
return []
}
} else {
key = 'root'
}
let options = config.optionsMap.get(key)
if (!Array.isArray(options)) {
if (request) {
let parent = key === 'root' ? '' : key
return this.getLinkageOptionsAsync(config, parent)
} else {
options = []
}
}
return options
},
/** 【多级联动】获取联动下拉选项(异步) */
getLinkageOptionsAsync(config, parent) {
return new Promise(resolve => {
let key = parent ? parent : 'root'
let options
if (config.optionsMap.has(key)) {
options = config.optionsMap.get(key)
if (options instanceof Promise) {
options.then(opt => {
config.optionsMap.set(key, opt)
resolve(opt)
})
} else {
resolve(options)
}
} else if (typeof config.requestData === 'function') {
// 调用requestData方法通过传入parent来获取子级
let promise = config.requestData(parent)
config.optionsMap.set(key, promise)
promise.then(opt => {
config.optionsMap.set(key, opt)
resolve(opt)
})
} else {
resolve([])
}
})
},
// 【多级联动】 用于回显数据,自动填充 optionsMap
autoSetLinkageOptionsByData(data, parent, config, level) {
if (level === 0) {
this.getLinkageOptionsAsync(config, '')
} else {
this.getLinkageOptionsAsync(config, parent)
}
if (config.keys.length - 1 > level) {
let value = data[config.keys[level]]
if (value) {
this.autoSetLinkageOptionsByData(data, value, config, level + 1)
}
}
},
// 【多级联动】联动组件change时清空下级组件
linkageSelectChange(row, col, config, value) {
if (col.linkageKey) {
this.getLinkageOptionsAsync(config, value)
let idx = config.keys.findIndex(k => k === col.key)
let values = {}
for (let i = idx; i < config.keys.length; i++) {
values[config.keys[i]] = ''
}
// 清空后几列的数据
this.setValues([{rowKey: row.id, values}])
}
},
/** 加载数据字典并合并到 options */ /** 加载数据字典并合并到 options */
_loadDictConcatToOptions(column) { _loadDictConcatToOptions(column) {
initDictOptions(column.dictCode).then((res) => { initDictOptions(column.dictCode).then((res) => {
@ -1084,6 +1241,15 @@ export default {
let createValue = getEnhancedMixins(col.$type || col.type, 'createValue') let createValue = getEnhancedMixins(col.$type || col.type, 'createValue')
record[col.key] = createValue({row: record, column, $table: xTable}) record[col.key] = createValue({row: record, column, $table: xTable})
} }
// update-begin--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
if (col.$type === JVXETypes.select && this._innerLinkageConfig != null) {
// 判断当前列是否是联动列
if (this._innerLinkageConfig.has(col.key)) {
let configItem = this._innerLinkageConfig.get(col.key)
this.getLinkageOptionsAsync(configItem, '')
}
}
// update-end--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
}) })
return record return record
}, },

View File

@ -7,11 +7,16 @@
v-bind="selectProps" v-bind="selectProps"
style="width: 100%;" style="width: 100%;"
@blur="handleBlur" @blur="handleBlur"
@change="handleChangeCommon" @change="handleChange"
@search="handleSearchSelect" @search="handleSearchSelect"
> >
<template v-for="option of originColumn.options"> <div v-if="loading" slot="notFoundContent">
<a-icon type="loading" />
<span>&nbsp;加载中</span>
</div>
<template v-for="option of selectOptions">
<a-select-option :key="option.value" :value="option.value" :disabled="option.disabled"> <a-select-option :key="option.value" :value="option.value" :disabled="option.disabled">
<span>{{option.text || option.label || option.title|| option.value}}</span> <span>{{option.text || option.label || option.title|| option.value}}</span>
</a-select-option> </a-select-option>
@ -23,10 +28,18 @@
<script> <script>
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins' import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
import { JVXETypes } from '@comp/jeecg/JVxeTable/index' import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
import { filterDictText } from '@comp/dict/JDictSelectUtil'
export default { export default {
name: 'JVxeSelectCell', name: 'JVxeSelectCell',
mixins: [JVxeCellMixins], mixins: [JVxeCellMixins],
data(){
return {
loading: false,
// 异步加载的options用于多级联动
asyncOptions: null,
}
},
computed: { computed: {
selectProps() { selectProps() {
let props = {...this.cellProps} let props = {...this.cellProps}
@ -37,6 +50,32 @@
} }
return props return props
}, },
// 下拉选项
selectOptions() {
if (this.asyncOptions) {
return this.asyncOptions
}
let {linkage} = this.renderOptions
if (linkage) {
let {getLinkageOptionsSibling, config} = linkage
let res = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
// 当返回Promise时说明是多级联动
if (res instanceof Promise) {
this.loading = true
res.then(opt => {
this.asyncOptions = opt
this.loading = false
}).catch(e => {
console.error(e)
this.loading = false
})
} else {
this.asyncOptions = null
return res
}
}
return this.originColumn.options
},
}, },
created() { created() {
let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi] let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
@ -54,6 +93,16 @@
}, },
methods: { methods: {
handleChange(value) {
debugger
// 处理下级联动
let linkage = this.renderOptions.linkage
if (linkage) {
linkage.linkageSelectChange(this.row, this.originColumn, linkage.config, value)
}
this.handleChangeCommon(value)
},
/** 处理blur失去焦点事件 */ /** 处理blur失去焦点事件 */
handleBlur(value) { handleBlur(value) {
let {allowInput, options} = this.originColumn let {allowInput, options} = this.originColumn
@ -120,7 +169,28 @@
dispatchEvent.call(this, event, 'ant-select') dispatchEvent.call(this, event, 'ant-select')
}, },
}, },
translate: {enabled: true}, translate: {
enabled: true,
async handler(value,) {
let options
let {linkage} = this.renderOptions
// 判断是否是多级联动,如果是就通过接口异步翻译
if (linkage) {
let {getLinkageOptionsSibling, config} = linkage
options = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
if (options instanceof Promise) {
return new Promise(resolve => {
options.then(opt => {
resolve(filterDictText(opt, value))
})
})
}
} else {
options = this.column.own.options
}
return filterDictText(options, value)
},
},
getValue(value) { getValue(value) {
if (Array.isArray(value)) { if (Array.isArray(value)) {
return value.join(',') return value.join(',')

View File

@ -14,7 +14,7 @@
<a-tooltip v-else-if="file.status === 'done'" title="上传完成"> <a-tooltip v-else-if="file.status === 'done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/> <a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip> </a-tooltip>
<a-tooltip v-else title="上传失败"> <a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/> <a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip> </a-tooltip>
</template> </template>
@ -118,8 +118,17 @@
value['responseName'] = file.response[col.responseName] value['responseName'] = file.response[col.responseName]
} }
if (file.status === 'done') { if (file.status === 'done') {
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[col.responseName] value['path'] = file.response[col.responseName]
this.handleChangeCommon(value) } else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[col.responseName]
}
} else if (file.status === 'error') { } else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误' value['message'] = file.response.message || '未知错误'
} }

View File

@ -102,7 +102,13 @@ export default {
// 判断是否启用翻译 // 判断是否启用翻译
if (this.renderType === JVXERenderType.spaner && this.enhanced.translate.enabled) { if (this.renderType === JVXERenderType.spaner && this.enhanced.translate.enabled) {
this.innerValue = this.enhanced.translate.handler.call(this, value) let res = this.enhanced.translate.handler.call(this, value)
// 异步翻译,目前仅【多级联动】使用
if (res instanceof Promise) {
res.then(value => this.innerValue = value)
} else {
this.innerValue = res
}
} }
}, },
}, },

View File

@ -26,19 +26,24 @@ import JSlider from './JSlider.vue'
import JSwitch from './JSwitch.vue' import JSwitch from './JSwitch.vue'
import JTime from './JTime.vue' import JTime from './JTime.vue'
import JTreeTable from './JTreeTable.vue' import JTreeTable from './JTreeTable.vue'
import JEasyCron from "@/components/jeecg/JEasyCron"; import JEasyCron from '@/components/jeecg/JEasyCron'
//jeecgbiz //jeecgbiz
import JSelectDepart from '../jeecgbiz/JSelectDepart.vue' import JSelectDepart from '../jeecgbiz/JSelectDepart.vue'
import JSelectMultiUser from '../jeecgbiz/JSelectMultiUser.vue' import JSelectMultiUser from '../jeecgbiz/JSelectMultiUser.vue'
import JSelectPosition from '../jeecgbiz/JSelectPosition.vue' import JSelectPosition from '../jeecgbiz/JSelectPosition.vue'
import JSelectRole from '../jeecgbiz/JSelectRole.vue' import JSelectRole from '../jeecgbiz/JSelectRole.vue'
import JSelectUserByDep from '../jeecgbiz/JSelectUserByDep.vue' import JSelectUserByDep from '../jeecgbiz/JSelectUserByDep.vue'
//引入需要全局注册的js函数和变量
import { Modal, notification,message } from 'ant-design-vue'
import lodash_object from 'lodash'
import debounce from 'lodash/debounce'
import pick from 'lodash.pick'
import data from 'china-area-data'
export default { export default {
install(Vue) { install(Vue) {
Vue.use(JModal)
Vue.component('JMarkdownEditor', JMarkdownEditor) Vue.component('JMarkdownEditor', JMarkdownEditor)
Vue.component(JModal.name, JModal)
Vue.component('JPopupOnlReport', JPopupOnlReport) Vue.component('JPopupOnlReport', JPopupOnlReport)
Vue.component('JFilePop', JFilePop) Vue.component('JFilePop', JFilePop)
Vue.component('JInputPop', JInputPop) Vue.component('JInputPop', JInputPop)
@ -73,5 +78,14 @@ export default {
Vue.component('JSelectRole', JSelectRole) Vue.component('JSelectRole', JSelectRole)
Vue.component('JSelectUserByDep', JSelectUserByDep) Vue.component('JSelectUserByDep', JSelectUserByDep)
Vue.component(JEasyCron.name, JEasyCron) Vue.component(JEasyCron.name, JEasyCron)
//注册全局js函数和变量
Vue.prototype.$Jnotification = notification
Vue.prototype.$Jmodal = Modal
Vue.prototype.$Jmessage = message
Vue.prototype.$Jlodash = lodash_object
Vue.prototype.$Jdebounce= debounce
Vue.prototype.$Jpick = pick
Vue.prototype.$Jpcaa = data
} }
} }

View File

@ -137,8 +137,12 @@
param:{ param:{
deep:true, deep:true,
handler(){ handler(){
// update--begin--autor:liusq-----date:20210706------forJPopup组件在modal中使用报错#2729------
if(this.visible){
this.dynamicParamHandler() this.dynamicParamHandler()
this.loadData(); this.loadData();
}
// update--begin--autor:liusq-----date:20210706------forJPopup组件在modal中使用报错#2729------
}, },
}, },
sorter: { sorter: {

View File

@ -1,9 +1,9 @@
<template> <template>
<div class="components-input-demo-presuffix"> <div class="components-input-demo-presuffix">
<!----> <!---->
<a-input @click="openModal" placeholder="请点击选择部门" v-model="departNames" readOnly :disabled="disabled"> <a-input @click="openModal" placeholder="请点击选择部门" v-model="textVals" readOnly :disabled="disabled">
<a-icon slot="prefix" type="cluster" title="部门选择控件"/> <a-icon slot="prefix" type="cluster" title="部门选择控件"/>
<a-icon v-if="departIds" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/> <a-icon v-if="storeVals" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input> </a-input>
<j-select-depart-modal <j-select-depart-modal
@ -11,7 +11,10 @@
:modal-width="modalWidth" :modal-width="modalWidth"
:multi="multi" :multi="multi"
:rootOpened="rootOpened" :rootOpened="rootOpened"
:depart-id="departIds" :depart-id="value"
:store="storeField"
:text="textField"
:treeOpera="treeOpera"
@ok="handleOK" @ok="handleOK"
@initComp="initComp"/> @initComp="initComp"/>
</div> </div>
@ -19,6 +22,7 @@
<script> <script>
import JSelectDepartModal from './modal/JSelectDepartModal' import JSelectDepartModal from './modal/JSelectDepartModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default { export default {
name: 'JSelectDepart', name: 'JSelectDepart',
components:{ components:{
@ -52,56 +56,70 @@
// 自定义返回字段,默认返回 id // 自定义返回字段,默认返回 id
customReturnField: { customReturnField: {
type: String, type: String,
default: 'id' default: ''
}, },
backDepart: { backDepart: {
type: Boolean, type: Boolean,
default: false, default: false,
required: false required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'id',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'departName',
required: false
},
treeOpera: {
type: Boolean,
default: false,
required: false
} }
}, },
data(){ data(){
return { return {
visible:false, visible:false,
confirmLoading:false, confirmLoading:false,
departNames:"", storeVals: '', //[key values]
departIds:'' textVals: '' //[label values]
}
},
computed:{
storeField(){
let field = this.customReturnField
if(!field){
field = this.store;
}
return underLinetoHump(field)
},
textField(){
return underLinetoHump(this.text)
} }
}, },
mounted(){ mounted(){
this.departIds = this.value this.storeVals = this.value
}, },
watch:{ watch:{
value(val){ value(val){
//update-begin-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2 this.storeVals = val
// if (this.customReturnField === 'id') {
this.departIds = val
// }
//update-end-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2
} }
}, },
methods:{ methods:{
initComp(departNames){ initComp(textVals){
this.departNames = departNames this.textVals = textVals
//update-begin-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
//TODO 当返回字段为部门名称时会有问题,因为部门名称不唯一
//返回字段不为id时根据返回字段获取id
if(this.customReturnField !== 'id' && this.value){
const dataList = this.$refs.innerDepartSelectModal.dataList;
console.log('this.value',this.value)
this.departIds = this.value.split(',').map(item => {
const data = dataList.filter(d=>d[this.customReturnField] === item)
return data.length > 0 ? data[0].id : ''
}).join(',')
}
//update-end-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
}, },
//返回选中的部门信息 //返回选中的部门信息
backDeparInfo(){ backDeparInfo(){
if(this.backDepart===true){ if(this.backDepart===true){
if(this.departIds && this.departIds.length>0){ if(this.departIds && this.departIds.length>0){
let arr1 = this.departIds.split(',') let arr1 = this.storeVals.split(',')
let arr2 = this.departNames.split(',') let arr2 = this.textVals.split(',')
let info = [] let info = []
for(let i=0;i<arr1.length;i++){ for(let i=0;i<arr1.length;i++){
info.push({ info.push({
@ -116,17 +134,21 @@
openModal(){ openModal(){
this.$refs.innerDepartSelectModal.show() this.$refs.innerDepartSelectModal.show()
}, },
handleOK(rows, idstr) { handleOK(rows) {
let value = ''
if (!rows && rows.length <= 0) { if (!rows && rows.length <= 0) {
this.departNames = '' this.textVals = ''
this.departIds = '' this.storeVals = ''
} else { } else {
value = rows.map(row => row[this.customReturnField]).join(',') let arr1 = []
this.departNames = rows.map(row => row['departName']).join(',') let arr2 = []
this.departIds = idstr for(let dep of rows){
arr1.push(dep[this.storeField])
arr2.push(dep[this.textField])
} }
this.$emit("change", value) this.storeVals = arr1.join(',')
this.textVals = arr2.join(',')
}
this.$emit("change", this.storeVals)
this.backDeparInfo() this.backDeparInfo()
}, },
getDepartNames(){ getDepartNames(){

View File

@ -1,19 +1,28 @@
<template> <template>
<div> <div>
<a-input-search <a-input-search
v-model="userNames" v-model="textVals"
placeholder="请先选择用户" placeholder="请先选择用户"
readOnly readOnly
unselectable="on" unselectable="on"
@search="onSearchDepUser"> @search="onSearchDepUser">
<a-button slot="enterButton" :disabled="disabled">选择用户</a-button> <a-button slot="enterButton" :disabled="disabled">选择用户</a-button>
</a-input-search> </a-input-search>
<j-select-user-by-dep-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" @ok="selectOK" :user-ids="value" @initComp="initComp"/> <j-select-user-by-dep-modal
ref="selectModal"
:modal-width="modalWidth"
:multi="multi"
@ok="selectOK"
:user-ids="value"
:store="storeField"
:text="textField"
@initComp="initComp"/>
</div> </div>
</template> </template>
<script> <script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal' import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default { export default {
name: 'JSelectUserByDep', name: 'JSelectUserByDep',
@ -42,20 +51,44 @@
type: Boolean, type: Boolean,
default: false, default: false,
required: false required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'username',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'realname',
required: false
} }
}, },
data() { data() {
return { return {
userIds: "", storeVals: '', //[key values]
userNames: "" textVals: '' //[label values]
}
},
computed:{
storeField(){
let field = this.customReturnField
if(!field){
field = this.store;
}
return underLinetoHump(field)
},
textField(){
return underLinetoHump(this.text)
} }
}, },
mounted() { mounted() {
this.userIds = this.value this.storeVals = this.value
}, },
watch: { watch: {
value(val) { value(val) {
this.userIds = val this.storeVals = val
} }
}, },
model: { model: {
@ -63,15 +96,15 @@
event: 'change' event: 'change'
}, },
methods: { methods: {
initComp(userNames) { initComp(textVals) {
this.userNames = userNames this.textVals = textVals
}, },
//返回选中的用户信息 //返回选中的用户信息
backDeparInfo(){ backDeparInfo(){
if(this.backUser===true){ if(this.backUser===true){
if(this.userIds && this.userIds.length>0){ if(this.storeVals && this.storeVals.length>0){
let arr1 = this.userIds.split(',') let arr1 = this.storeVals.split(',')
let arr2 = this.userNames.split(',') let arr2 = this.textVals.split(',')
let info = [] let info = []
for(let i=0;i<arr1.length;i++){ for(let i=0;i<arr1.length;i++){
info.push({ info.push({
@ -86,21 +119,22 @@
onSearchDepUser() { onSearchDepUser() {
this.$refs.selectModal.showModal() this.$refs.selectModal.showModal()
}, },
selectOK(rows, idstr) { selectOK(rows) {
console.log("当前选中用户", rows) console.log("当前选中用户", rows)
console.log("当前选中用户ID", idstr)
if (!rows) { if (!rows) {
this.userNames = '' this.storeVals = ''
this.userIds = '' this.textVals = ''
} else { } else {
let temp = '' let temp1 = []
let temp2 = []
for (let item of rows) { for (let item of rows) {
temp += ',' + item.realname temp1.push(item[this.storeField])
temp2.push(item[this.textField])
} }
this.userNames = temp.substring(1) this.storeVals = temp1.join(',')
this.userIds = idstr this.textVals = temp2.join(',')
} }
this.$emit("change", this.userIds) this.$emit("change", this.storeVals)
} }
} }
} }

View File

@ -6,6 +6,7 @@
:confirmLoading="confirmLoading" :confirmLoading="confirmLoading"
@ok="handleSubmit" @ok="handleSubmit"
@cancel="handleCancel" @cancel="handleCancel"
@update:fullscreen="isFullscreen"
wrapClassName="j-depart-select-modal" wrapClassName="j-depart-select-modal"
switchFullscreen switchFullscreen
cancelText="关闭"> cancelText="关闭">
@ -13,9 +14,9 @@
<a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" /> <a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" />
<a-tree <a-tree
checkable checkable
class="my-dept-select-tree" :class="treeScreenClass"
:treeData="treeData" :treeData="treeData"
:checkStrictly="true" :checkStrictly="checkStrictly"
@check="onCheck" @check="onCheck"
@select="onSelect" @select="onSelect"
@expand="onExpand" @expand="onExpand"
@ -32,8 +33,23 @@
<span v-else>{{title}}</span> <span v-else>{{title}}</span>
</template> </template>
</a-tree> </a-tree>
</a-spin> </a-spin>
<!--底部父子关联操作和确认取消按钮-->
<template slot="footer" v-if="treeOpera && multi">
<div class="drawer-bootom-button">
<a-dropdown style="float: left" :trigger="['click']" placement="topCenter">
<a-menu slot="overlay">
<a-menu-item key="1" @click="switchCheckStrictly(1)">父子关联</a-menu-item>
<a-menu-item key="2" @click="switchCheckStrictly(2)">取消关联</a-menu-item>
</a-menu>
<a-button>
树操作 <a-icon type="up" />
</a-button>
</a-dropdown>
<a-button @click="handleCancel" type="primary" style="margin-right: 0.8rem">关闭</a-button>
<a-button @click="handleSubmit" type="primary" >确认</a-button>
</div>
</template>
</j-modal> </j-modal>
</template> </template>
@ -41,7 +57,7 @@
import { queryDepartTreeList } from '@/api/api' import { queryDepartTreeList } from '@/api/api'
export default { export default {
name: 'JSelectDepartModal', name: 'JSelectDepartModal',
props:['modalWidth','multi','rootOpened','departId'], props:['modalWidth','multi','rootOpened','departId', 'store', 'text','treeOpera'],
data(){ data(){
return { return {
visible:false, visible:false,
@ -52,7 +68,9 @@
dataList:[], dataList:[],
checkedKeys:[], checkedKeys:[],
checkedRows:[], checkedRows:[],
searchValue:"" searchValue:"",
checkStrictly: true,
fullscreen:false
} }
}, },
created(){ created(){
@ -64,15 +82,18 @@
}, },
visible: { visible: {
handler() { handler() {
if (this.departId) { this.initDepartComponent(true)
this.checkedKeys = this.departId.split(",");
// console.log('this.departId', this.departId)
} else {
this.checkedKeys = [];
} }
} }
},
computed:{
treeScreenClass() {
return {
'my-dept-select-tree': true,
'fullscreen': this.fullscreen,
} }
}, },
},
methods:{ methods:{
show(){ show(){
this.visible=true this.visible=true
@ -80,6 +101,7 @@
this.checkedKeys=[] this.checkedKeys=[]
}, },
loadDepart(){ loadDepart(){
// 这个方法是找到所有的部门信息
queryDepartTreeList().then(res=>{ queryDepartTreeList().then(res=>{
if(res.success){ if(res.success){
let arr = [...res.result] let arr = [...res.result]
@ -92,20 +114,23 @@
} }
}) })
}, },
initDepartComponent(){ initDepartComponent(flag){
let names = '' let arr = []
//该方法两个地方用 1.visible改变事件重新设置选中项 2.组件编辑页面回显
let fieldName = flag==true?'key':this.text
if(this.departId){ if(this.departId){
let currDepartId = this.departId let arr2 = this.departId.split(',')
for(let item of this.dataList){ for(let item of this.dataList){
if(currDepartId.indexOf(item.key)>=0){ if(arr2.indexOf(item[this.store])>=0){
names+=","+item.title arr.push(item[fieldName])
} }
} }
if(names){
names = names.substring(1)
} }
if(flag==true){
this.checkedKeys = [...arr]
}else{
this.$emit("initComp", arr.join(','))
} }
this.$emit("initComp",names)
}, },
reWriterWithSlot(arr){ reWriterWithSlot(arr){
for(let item of arr){ for(let item of arr){
@ -129,8 +154,11 @@
} }
} }
this.expandedKeys=[...keys] this.expandedKeys=[...keys]
//全部keys
//this.allTreeKeys = [...keys]
}else{ }else{
this.expandedKeys=[] this.expandedKeys=[]
//this.allTreeKeys = []
} }
}, },
onCheck (checkedKeys,info) { onCheck (checkedKeys,info) {
@ -139,11 +167,17 @@
this.checkedKeys = [...arr] this.checkedKeys = [...arr]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef] this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
}else{ }else{
if(this.checkStrictly){
this.checkedKeys = checkedKeys.checked this.checkedKeys = checkedKeys.checked
}else{
this.checkedKeys = checkedKeys
}
this.checkedRows = this.getCheckedRows(this.checkedKeys) this.checkedRows = this.getCheckedRows(this.checkedKeys)
} }
}, },
onSelect(selectedKeys,info) { onSelect(selectedKeys,info) {
//取消关联的情况下才走onSelect的逻辑
if(this.checkStrictly){
let keys = [] let keys = []
keys.push(selectedKeys[0]) keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){ if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
@ -158,6 +192,7 @@
} }
} }
this.checkedRows = this.getCheckedRows(this.checkedKeys) this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
}, },
onExpand (expandedKeys) { onExpand (expandedKeys) {
this.expandedKeys = expandedKeys this.expandedKeys = expandedKeys
@ -235,6 +270,16 @@
} }
} }
return rows return rows
},
switchCheckStrictly (v) {
if(v==1){
this.checkStrictly = false
}else if(v==2){
this.checkStrictly = true
}
},
isFullscreen(val){
this.fullscreen=val
} }
} }
} }
@ -245,7 +290,21 @@
// 限制部门选择树高度,避免部门太多时点击确定不便 // 限制部门选择树高度,避免部门太多时点击确定不便
.my-dept-select-tree{ .my-dept-select-tree{
height:350px; height:350px;
&.fullscreen{
height: calc(100vh - 250px);
}
overflow-y: scroll; overflow-y: scroll;
} }
.drawer-bootom-button {
position: absolute;
bottom: 0;
width: 100%;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
left: 0;
background: #fff;
border-radius: 0 0 2px 2px;
}
</style> </style>

View File

@ -63,7 +63,7 @@
export default { export default {
name: 'JSelectUserByDepModal', name: 'JSelectUserByDepModal',
components: {}, components: {},
props: ['modalWidth', 'multi', 'userIds'], props: ['modalWidth', 'multi', 'userIds', 'store', 'text'],
data() { data() {
return { return {
queryParam: { queryParam: {
@ -159,27 +159,27 @@
if (this.userIds) { if (this.userIds) {
// 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确 // 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
let values = this.userIds.split(',') + ',' let values = this.userIds.split(',') + ','
getUserList({ let param = {[this.store]: values}
username: values, getAction('/sys/user/getMultiUser', param).then((list)=>{
pageNo: 1,
pageSize: values.length
}).then((res) => {
if (res.success) {
this.selectionRows = [] this.selectionRows = []
let selectedRowKeys = [] let selectedRowKeys = []
let realNames = [] let textArray = []
res.result.records.forEach(user => { if(list && list.length>0){
realNames.push(user['realname']) for(let user of list){
textArray.push(user[this.text])
selectedRowKeys.push(user['id']) selectedRowKeys.push(user['id'])
this.selectionRows.push(user) this.selectionRows.push(user)
})
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', realNames.join(','))
} }
}
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', textArray.join(','))
}) })
} else { } else {
// JSelectUserByDep组件bug issues/I16634 // JSelectUserByDep组件bug issues/I16634
this.$emit('initComp', '') this.$emit('initComp', '')
// 前端用户选择单选无法置空的问题 #2610
this.selectedRowKeys = []
} }
}, },
async loadData(arg) { async loadData(arg) {
@ -254,7 +254,7 @@
handleSubmit() { handleSubmit() {
let that = this; let that = this;
this.getSelectUserRows(); this.getSelectUserRows();
that.$emit('ok', that.selectUserRows, that.selectUserIds); that.$emit('ok', that.selectUserRows);
that.searchReset(0) that.searchReset(0)
that.close(); that.close();
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="logo"> <div class="logo">
<router-link :to="{name:'dashboard'}"> <router-link :to="routerLinkTo">
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 --> <!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
<img v-if="navTheme === 'dark'" src="~@/assets/logo-white.png" alt="logo"> <img v-if="navTheme === 'dark'" src="~@/assets/logo-white.png" alt="logo">
@ -28,7 +28,12 @@
type: Boolean, type: Boolean,
default: true, default: true,
required: false required: false
} },
// 点击Logo跳转地址
routerLinkTo: {
type: Object,
default: () => ({name: 'dashboard'}),
},
} }
} }
</script> </script>

View File

@ -347,6 +347,21 @@ export const constantRouterMap = [
// ] // ]
// }, // },
{
// OAuth2 APP页面路由
path: '/oauth2-app',
component: BlankLayout,
redirect: '/oauth2-app/login',
children: [
{
// OAuth2 登录路由
path: 'login',
name: 'login',
component: () => import(/* webpackChunkName: "oauth2-app.login" */ '@/views/user/oauth2/OAuth2Login')
},
]
},
{ {
path: '/test', path: '/test',
component: BlankLayout, component: BlankLayout,

View File

@ -8,7 +8,6 @@ import { deleteAction, getAction,downFile,getFileAccessHttpUrl } from '@/api/man
import Vue from 'vue' import Vue from 'vue'
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types" import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
import store from '@/store' import store from '@/store'
import {Modal} from 'ant-design-vue'
export const JeecgListMixin = { export const JeecgListMixin = {
data(){ data(){
@ -94,11 +93,11 @@ export const JeecgListMixin = {
this.ipagination.total = 0; this.ipagination.total = 0;
} }
//update-end---author:zhangyafei Date:20201118 for适配不分页的数据列表------------ //update-end---author:zhangyafei Date:20201118 for适配不分页的数据列表------------
} }else{
if(res.code===510){
this.$message.warning(res.message) this.$message.warning(res.message)
} }
this.loading = false; }).finally(() => {
this.loading = false
}) })
}, },
initDictConfig(){ initDictConfig(){
@ -296,10 +295,12 @@ export const JeecgListMixin = {
}, },
/* 导入 */ /* 导入 */
handleImportExcel(info){ handleImportExcel(info){
this.loading = true;
if (info.file.status !== 'uploading') { if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList); console.log(info.file, info.fileList);
} }
if (info.file.status === 'done') { if (info.file.status === 'done') {
this.loading = false;
if (info.file.response.success) { if (info.file.response.success) {
// this.$message.success(`${info.file.name} 文件上传成功`); // this.$message.success(`${info.file.name} 文件上传成功`);
if (info.file.response.code === 201) { if (info.file.response.code === 201) {
@ -321,11 +322,12 @@ export const JeecgListMixin = {
this.$message.error(`${info.file.name} ${info.file.response.message}.`); this.$message.error(`${info.file.name} ${info.file.response.message}.`);
} }
} else if (info.file.status === 'error') { } else if (info.file.status === 'error') {
this.loading = false;
if (info.file.response.status === 500) { if (info.file.response.status === 500) {
let data = info.file.response let data = info.file.response
const token = Vue.ls.get(ACCESS_TOKEN) const token = Vue.ls.get(ACCESS_TOKEN)
if (token && data.message.includes("Token失效")) { if (token && data.message.includes("Token失效")) {
Modal.error({ this.$error({
title: '登录已过期', title: '登录已过期',
content: '很抱歉登录已过期请重新登录', content: '很抱歉登录已过期请重新登录',
okText: '重新登录', okText: '重新登录',

View File

@ -1,14 +1,16 @@
import { formatDate } from '@/utils/util' import { formatDate } from '@/utils/util'
import Area from '@/components/_util/Area' import Area from '@/components/_util/Area'
import { postAction } from '@/api/manage'
const onlUtil = { const onlUtil = {
data(){ data(){
return { return {
mixin_pca:'' mixin_pca:'',
flowCodePre: 'onl_'
} }
}, },
created(){ created(){
this.mixin_pca = new Area() this.mixin_pca = new Area(this.$Jpcaa)
}, },
methods:{ methods:{
simpleDateFormat(millisecond, format){ simpleDateFormat(millisecond, format){

View File

@ -4,19 +4,20 @@ import store from './store'
import NProgress from 'nprogress' // progress bar import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style import 'nprogress/nprogress.css' // progress bar style
import notification from 'ant-design-vue/es/notification' import notification from 'ant-design-vue/es/notification'
import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH } from '@/store/mutation-types' import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH, OAUTH2_LOGIN_PAGE_PATH } from '@/store/mutation-types'
import { generateIndexRouter } from "@/utils/util" import { generateIndexRouter, isOAuth2AppEnv } from '@/utils/util'
NProgress.configure({ showSpinner: false }) // NProgress Configuration NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/user/login', '/user/register', '/user/register-result','/user/alteration'] // no redirect whitelist const whiteList = ['/user/login', '/user/register', '/user/register-result','/user/alteration'] // no redirect whitelist
whiteList.push(OAUTH2_LOGIN_PAGE_PATH)
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar NProgress.start() // start progress bar
if (Vue.ls.get(ACCESS_TOKEN)) { if (Vue.ls.get(ACCESS_TOKEN)) {
/* has token */ /* has token */
if (to.path === '/user/login') { if (to.path === '/user/login' || to.path === OAUTH2_LOGIN_PAGE_PATH) {
next({ path: INDEX_MAIN_PAGE_PATH }) next({ path: INDEX_MAIN_PAGE_PATH })
NProgress.done() NProgress.done()
} else { } else {
@ -59,10 +60,18 @@ router.beforeEach((to, from, next) => {
} }
} else { } else {
if (whiteList.indexOf(to.path) !== -1) { if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单如果进入的页面是login页面并且当前是OAuth2app环境就进入OAuth2登录页面
if (to.path === '/user/login' && isOAuth2AppEnv()) {
next({path: OAUTH2_LOGIN_PAGE_PATH})
} else {
// 在免登录白名单,直接进入 // 在免登录白名单,直接进入
next() next()
}
NProgress.done()
} else { } else {
next({ path: '/user/login', query: { redirect: to.fullPath } }) // 如果当前是在OAuth2APP环境就跳转到OAuth2登录页面
let path = isOAuth2AppEnv() ? OAUTH2_LOGIN_PAGE_PATH : '/user/login'
next({ path: path, query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
} }
} }

View File

@ -3,9 +3,9 @@ import Vuex from 'vuex'
import app from './modules/app' import app from './modules/app'
import user from './modules/user' import user from './modules/user'
import permission from './modules/permission'
import enhance from './modules/enhance' import enhance from './modules/enhance'
import online from './modules/online' import online from './modules/online'
import permission from './modules/permission'
import getters from './getters' import getters from './getters'
Vue.use(Vuex) Vue.use(Vuex)
@ -16,7 +16,7 @@ export default new Vuex.Store({
user, user,
permission, permission,
enhance, enhance,
online online,
}, },
state: { state: {

View File

@ -159,6 +159,7 @@ const user = {
Vue.ls.remove(USER_NAME) Vue.ls.remove(USER_NAME)
Vue.ls.remove(UI_CACHE_DB_DICT_DATA) Vue.ls.remove(UI_CACHE_DB_DICT_DATA)
Vue.ls.remove(CACHE_INCLUDED_ROUTES) Vue.ls.remove(CACHE_INCLUDED_ROUTES)
Vue.ls.remove(TENANT_ID)
//console.log('logoutToken: '+ logoutToken) //console.log('logoutToken: '+ logoutToken)
logout(logoutToken).then(() => { logout(logoutToken).then(() => {
if (process.env.VUE_APP_SSO == 'true') { if (process.env.VUE_APP_SSO == 'true') {

View File

@ -17,6 +17,7 @@ export const ENCRYPTED_STRING = 'ENCRYPTED_STRING'
export const ENHANCE_PRE = 'enhance_' export const ENHANCE_PRE = 'enhance_'
export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA' export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA'
export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis' export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis'
export const OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login'
export const TENANT_ID = 'TENANT_ID' export const TENANT_ID = 'TENANT_ID'
export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS' export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS'
//路由缓存问题关闭了tab页时再打开就不刷新 #842 //路由缓存问题关闭了tab页时再打开就不刷新 #842

View File

@ -49,12 +49,14 @@ export default class signMd5Utils {
result = {}; result = {};
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username // 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
//【这边条件没有encode】带条件参数例子/sys/dict/getDictItems/sys_user,realname,id,username!='admin'%20order%20by%20create_time
let lastpathVariable = url.substring(url.lastIndexOf('/') + 1); let lastpathVariable = url.substring(url.lastIndexOf('/') + 1);
if(lastpathVariable.includes(",")){ if(lastpathVariable.includes(",")){
if(lastpathVariable.includes("?")){ if(lastpathVariable.includes("?")){
lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?")); lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?"));
} }
result["x-path-variable"] = lastpathVariable; //解决Sign 签名校验失败 #2728
result["x-path-variable"] = decodeURI(lastpathVariable);
} }
if (urlArray && urlArray[1]) { if (urlArray && urlArray[1]) {
let paramString = urlArray[1], paramResult; let paramString = urlArray[1], paramResult;

View File

@ -2,7 +2,7 @@ import Vue from 'vue'
import axios from 'axios' import axios from 'axios'
import store from '@/store' import store from '@/store'
import { VueAxios } from './axios' import { VueAxios } from './axios'
import {Modal, notification} from 'ant-design-vue' import router from '@/router/index'
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types" import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
/** /**
@ -28,7 +28,7 @@ const err = (error) => {
console.log("------异常响应------",error.response.status) console.log("------异常响应------",error.response.status)
switch (error.response.status) { switch (error.response.status) {
case 403: case 403:
notification.error({ message: '系统提示', description: '拒绝访问',duration: 4}) Vue.prototype.$Jnotification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
break break
case 500: case 500:
console.log("------error.response------",error.response) console.log("------error.response------",error.response)
@ -39,10 +39,12 @@ const err = (error) => {
break; break;
} }
// update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况---- // update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
//notification.error({ message: '系统提示', description:'Token失效请重新登录!',duration: 4})
if(token && data.message.includes("Token失效")){ if(token && data.message.includes("Token失效")){
// update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转---- // update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转----
Modal.error({ if (/wxwork|dingtalk/i.test(navigator.userAgent)) {
Vue.prototype.$Jmessage.loading('登录已过期正在重新登陆', 0)
} else {
Vue.prototype.$Jmodal.error({
title: '登录已过期', title: '登录已过期',
content: '很抱歉登录已过期请重新登录', content: '很抱歉登录已过期请重新登录',
okText: '重新登录', okText: '重新登录',
@ -52,8 +54,8 @@ const err = (error) => {
Vue.ls.remove(ACCESS_TOKEN) Vue.ls.remove(ACCESS_TOKEN)
try { try {
let path = window.document.location.pathname let path = window.document.location.pathname
console.log("location pathname -> "+path) console.log('location pathname -> ' + path)
if(path!="/" && path.indexOf('/user/login')==-1){ if (path != '/' && path.indexOf('/user/login') == -1) {
window.location.reload() window.location.reload()
} }
} catch (e) { } catch (e) {
@ -62,17 +64,18 @@ const err = (error) => {
}) })
} }
}) })
}
// update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转---- // update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转----
} }
break break
case 404: case 404:
notification.error({ message: '系统提示', description:'很抱歉资源未找到!',duration: 4}) Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉资源未找到!',duration: 4})
break break
case 504: case 504:
notification.error({ message: '系统提示', description: '网络超时'}) Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时'})
break break
case 401: case 401:
notification.error({ message: '系统提示', description:'未授权请重新登录',duration: 4}) Vue.prototype.$Jnotification.error({ message: '系统提示', description:'未授权请重新登录',duration: 4})
if (token) { if (token) {
store.dispatch('Logout').then(() => { store.dispatch('Logout').then(() => {
setTimeout(() => { setTimeout(() => {
@ -82,13 +85,19 @@ const err = (error) => {
} }
break break
default: default:
notification.error({ Vue.prototype.$Jnotification.error({
message: '系统提示', message: '系统提示',
description: data.message, description: data.message,
duration: 4 duration: 4
}) })
break break
} }
} else if (error.message) {
if (error.message.includes('timeout')) {
Vue.prototype.$Jnotification.error({message: '系统提示', description: '网络超时'})
} else {
Vue.prototype.$Jnotification.error({message: '系统提示', description: error.message})
}
} }
return Promise.reject(error) return Promise.reject(error)
}; };
@ -99,6 +108,14 @@ service.interceptors.request.use(config => {
if (token) { if (token) {
config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改 config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
} }
// update-begin--author:sunjianlei---date:20200723---for 如果当前在low-app环境并且携带了appId就向Header里传递appId
const $route = router.currentRoute
if ($route && $route.name && $route.name.startsWith('low-app') && $route.params.appId) {
config.headers['X-Low-App-ID'] = $route.params.appId
}
// update-end--author:sunjianlei---date:20200723---for 如果当前在low-app环境并且携带了appId就向Header里传递appId
//update-begin-author:taoyan date:2020707 for:多租户 //update-begin-author:taoyan date:2020707 for:多租户
let tenantid = Vue.ls.get(TENANT_ID) let tenantid = Vue.ls.get(TENANT_ID)
if (!tenantid) { if (!tenantid) {

View File

@ -1,5 +1,7 @@
import Vue from 'vue'
import * as api from '@/api/api' import * as api from '@/api/api'
import { isURL } from '@/utils/validate' import { isURL } from '@/utils/validate'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import onlineCommons from '@jeecg/antd-online-mini' import onlineCommons from '@jeecg/antd-online-mini'
export function timeFix() { export function timeFix() {
@ -145,6 +147,7 @@ function generateChildRouters (data) {
component: componentPath, component: componentPath,
//component: resolve => require(['@/' + component+'.vue'], resolve), //component: resolve => require(['@/' + component+'.vue'], resolve),
hidden:item.hidden, hidden:item.hidden,
//component:()=> import(`@/views/${item.component}.vue`),
meta: { meta: {
title:item.meta.title , title:item.meta.title ,
icon: item.meta.icon, icon: item.meta.icon,
@ -560,3 +563,31 @@ export function removeArrayElement(array, prod, value) {
array.splice(index, 1); array.splice(index, 1);
} }
} }
/** 判断是否是OAuth2APP环境 */
export function isOAuth2AppEnv() {
return /wxwork|dingtalk/i.test(navigator.userAgent)
}
/**
* 获取积木报表打印地址
* @param url
* @param id
* @param open 是否自动打开
* @returns {*}
*/
export function getReportPrintUrl(url, id, open) {
// URL支持{{ window.xxx }}占位符变量
url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => eval(s2))
if (url.includes('?')) {
url += '&'
} else {
url += '?'
}
url += `id=${id}`
url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
if (open) {
window.open(url)
}
return url
}

View File

@ -1,5 +1,5 @@
<template> <template>
<a-modal :visible="visible" title="修改头像" :maskClosable="false" :confirmLoading="confirmLoading" :width="800"> <a-modal :visible="visible" title="修改头像" :maskClosable="false" :confirmLoading="confirmLoading" :width="800" @cancel="cancelHandel">
<a-row> <a-row>
<a-col :xs="24" :md="12" :style="{height: '350px'}"> <a-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper <vue-cropper

View File

@ -8,52 +8,94 @@
:height="484" :height="484"
:dataSource="dataSource" :dataSource="dataSource"
:columns="columns" :columns="columns"
@valueChange="handleValueChange" :linkage-config="linkageConfig"
/> />
</template> </template>
<script> <script>
import moment from 'moment'
import { randomNumber, randomUUID } from '@/utils/util'
import { JVXETypes } from '@/components/jeecg/JVxeTable' import { JVXETypes } from '@/components/jeecg/JVxeTable'
export default { export default {
name: 'JVxeDemo2', name: 'JVxeDemo2',
data() { data() {
return { return {
// 联动配置
linkageConfig: [
{requestData: this.requestData, key: 's1'},
// 可配置多个联动
{requestData: this.loadData, key: 'level1',},
],
columns: [ columns: [
{
title: '性别',
key: 'sex',
type: JVXETypes.select,
dictCode: 'sex',
width: '180px',
placeholder: '请选择${title}',
},
{ {
title: '/直辖市/自治区', title: '/直辖市/自治区',
key: 's1', key: 's1',
type: JVXETypes.select, type: JVXETypes.select,
width: '240px', width: '180px',
options: [], placeholder: '请选择${title}',
placeholder: '请选择${title}' // 联动字段(即下一级的字段)
linkageKey: 's2',
}, },
{ {
title: '市', title: '市',
key: 's2', key: 's2',
type: JVXETypes.select, type: JVXETypes.select,
width: '240px', width: '180px',
options: [], placeholder: '请选择${title}',
placeholder: '请选择${title}' // 联动字段(即下一级的字段)
linkageKey: 's3',
}, },
{ {
title: '/', title: '/',
key: 's3', key: 's3',
type: JVXETypes.select, type: JVXETypes.select,
width: '240px', width: '180px',
options: [], options: [],
placeholder: '请选择${title}' placeholder: '请选择${title}',
},
{
title: '一级',
key: 'level1',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'level2',
},
{
title: '二级',
key: 'level2',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'level3',
},
{
title: '三级',
key: 'level3',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
} }
], ],
dataSource: [], dataSource: [
{sex: '1', s1: '110000', s2: '110100', s3: '110101', level1: '1', level2: '3', level3: '7'},
{sex: '2', s1: '130000', s2: '130300', s3: '130303', level1: '2', level2: '6', level3: '14'},
],
// 模拟数据
mockData: [ mockData: [
{ text: '北京市', value: '110000', parent: null }, {text: '北京市', value: '110000', parent: ''},
{ text: '天津市', value: '120000', parent: null }, {text: '天津市', value: '120000', parent: ''},
{ text: '河北省', value: '130000', parent: null }, {text: '河北省', value: '130000', parent: ''},
{ text: '上海市', value: '310000', parent: null }, {text: '上海市', value: '310000', parent: ''},
{text: '北京市', value: '110100', parent: '110000'}, {text: '北京市', value: '110100', parent: '110000'},
{text: '天津市市', value: '120100', parent: '120000'}, {text: '天津市市', value: '120100', parent: '120000'},
@ -80,49 +122,57 @@
{text: '海港区', value: '130302', parent: '130300'}, {text: '海港区', value: '130302', parent: '130300'},
{text: '山海关区', value: '130303', parent: '130300'}, {text: '山海关区', value: '130303', parent: '130300'},
{text: '北戴河区', value: '130304', parent: '130300'}, {text: '北戴河区', value: '130304', parent: '130300'},
] ],
} mockData1: [
{id: '1', name: '图书馆', parentId: '0'},
{id: '2', name: '电影院', parentId: '0'},
}, {id: '3', name: '一楼', parentId: '1'},
created() { {id: '4', name: '二楼', parentId: '1'},
// 初始化数据 {id: '5', name: '中影星美', parentId: '2'},
this.columns[0].options = this.request(null) {id: '6', name: '万达国际', parentId: '2'},
{id: '7', name: '技术图书', parentId: '3'},
{id: '8', name: '财务图书', parentId: '3'},
{id: '9', name: '儿童图书', parentId: '4'},
{id: '10', name: '励志图书', parentId: '4'},
{id: '11', name: '1号厅', parentId: '5'},
{id: '12', name: '2号厅', parentId: '5'},
{id: '13', name: 'I-MAX厅', parentId: '6'},
{id: '14', name: '3D厅', parentId: '6'},
],
}
}, },
methods: { methods: {
/**
request(parentId) { * 模拟从后台查询数据
return this.mockData.filter(i => i.parent === parentId) */
requestData(parent) {
return new Promise((resolve, reject) => {
let data = this.mockData.filter(i => i.parent === parent)
setTimeout(() => {
resolve(data)
}, 500)
})
}, },
/** 当选项被改变时,联动其他组件 */ // 模拟加载数据,模拟数据格式不同的情况下如何组装数据
handleValueChange(event) { async loadData(parent) {
const { type, row, column, value, target } = event return new Promise((resolve, reject) => {
console.log("event",event) let parentId = parent === '' ? '0' : parent
if (type === JVXETypes.select) { let data = this.mockData1.filter(i => i.parentId === parentId)
data = data.map(item => {
// 第一列 return {
if (column.key === 's1') { // 必须包含以下两个字段
// 设置第二列的 options value: item.id,
console.log('this.request(value)::',this.request(value)) text: item.name,
target.$refs.vxe.columns[3].options = this.request(value)
// 清空后两列的数据
target.setValues([{
rowKey: row.id,
values: { s2: '', s3: '' }
}])
target.$refs.vxe.columns[4].options = []
} else
// 第二列
if (column.key === 's2') {
target.$refs.vxe.columns[4].options = this.request(value)
target.setValues([{
rowKey: row.id,
values: { s3: '' }
}])
}
}
} }
})
setTimeout(() => {
resolve(data)
}, 500)
})
},
} }
} }
</script> </script>

View File

@ -305,7 +305,7 @@
<a-row :gutter="24"> <a-row :gutter="24">
<a-col :span="12"> <a-col :span="12">
<a-form-model-item label="cron表达式" prop="cronExpression"> <a-form-model-item label="cron表达式" prop="cronExpression">
<j-cron v-model="formData.cronExpression"></j-cron> <j-easy-cron v-model="formData.cronExpression"></j-easy-cron>
</a-form-model-item> </a-form-model-item>
</a-col> </a-col>
</a-row> </a-row>
@ -457,6 +457,7 @@
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 JCron from "@/components/jeecg/JCron.vue"; import JCron from "@/components/jeecg/JCron.vue";
import JEasyCron from "@/components/jeecg/JEasyCron";
import JTreeSelect from '@/components/jeecg/JTreeSelect' import JTreeSelect from '@/components/jeecg/JTreeSelect'
import JSuperQuery from '@/components/jeecg/JSuperQuery' import JSuperQuery from '@/components/jeecg/JSuperQuery'
import JUpload from '@/components/jeecg/JUpload' import JUpload from '@/components/jeecg/JUpload'
@ -489,7 +490,7 @@
JCheckbox, JCheckbox,
JCodeEditor, JCodeEditor,
JDate, JEditor, JEllipsis, JSlider, JSelectMultiple, JDate, JEditor, JEllipsis, JSlider, JSelectMultiple,
JCron, JTreeSelect, JSuperQuery, JMultiSelectTag, JCron, JEasyCron,JTreeSelect, JSuperQuery, JMultiSelectTag,
JSearchSelectTag JSearchSelectTag
}, },
data() { data() {

View File

@ -214,7 +214,12 @@
this.isorter.order = "ascend" == sorter.order ? "asc" : "desc" this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
} }
//这种筛选方式只支持单选 //这种筛选方式只支持单选
// update-begin-author:liusq date:20210624 for:前台定时任务无法翻页 #2666
if(filters && Object.keys(filters).length>0 && filters.status){
this.filters.status = filters.status[0]; this.filters.status = filters.status[0];
}
// update-end-author:liusq date:20210624 for:前台定时任务无法翻页 #2666
this.ipagination = pagination; this.ipagination = pagination;
this.loadData(); this.loadData();
}, },

View File

@ -122,11 +122,6 @@
align: 'center', align: 'center',
customRender: (t, r, index) => index + 1 customRender: (t, r, index) => index + 1
}, },
{
title: '数据源编码',
align: 'center',
dataIndex: 'code'
},
{ {
title: '数据源名称', title: '数据源名称',
align: 'center', align: 'center',
@ -149,11 +144,6 @@
dataIndex: 'dbUrl', dataIndex: 'dbUrl',
customRender: (t) => ellipsis(t) customRender: (t) => ellipsis(t)
}, },
{
title: '数据库名称',
align: 'center',
dataIndex: 'dbName'
},
{ {
title: '用户名', title: '用户名',
align: 'center', align: 'center',

View File

@ -70,7 +70,7 @@
import {getFileAccessHttpUrl} from '@/api/manage'; import {getFileAccessHttpUrl} from '@/api/manage';
export default { export default {
name: "SysOnlineList", name: "SysUserOnlineList",
mixins:[JeecgListMixin, mixinDevice], mixins:[JeecgListMixin, mixinDevice],
components: {}, components: {},
data () { data () {

View File

@ -291,7 +291,7 @@
superQueryFieldList: [ superQueryFieldList: [
{ type: 'input', value: 'username', text: '用户账号', }, { type: 'input', value: 'username', text: '用户账号', },
{ type: 'input', value: 'realname', text: '用户姓名', }, { type: 'input', value: 'realname', text: '用户姓名', },
{ type: 'select', value: 'sex', text: '性别', dictCode: 'sex' }, { type: 'select', value: 'sex', dbType: 'int', text: '性别', dictCode: 'sex' },
], ],
url: { url: {
syncUser: "/act/process/extActProcess/doSyncUser", syncUser: "/act/process/extActProcess/doSyncUser",

View File

@ -111,7 +111,8 @@
httpAction(httpurl,this.model,method).then((res)=>{ httpAction(httpurl,this.model,method).then((res)=>{
if(res.success){ if(res.success){
that.$message.success(res.message); that.$message.success(res.message);
that.submitSuccess(this.model) // close的时候清空了表单的值 导致model为空 修改值在列表页没有变 此处需要复制一下model
that.submitSuccess({...this.model})
}else{ }else{
that.$message.warning(res.message); that.$message.warning(res.message);
} }

View File

@ -41,12 +41,12 @@
label="数据源地址"> label="数据源地址">
<a-input placeholder="请输入数据源地址" v-decorator="['dbUrl', validatorRules.dbUrl]"/> <a-input placeholder="请输入数据源地址" v-decorator="['dbUrl', validatorRules.dbUrl]"/>
</a-form-item> </a-form-item>
<a-form-item <!-- <a-form-item
:labelCol="labelCol" :labelCol="labelCol"
:wrapperCol="wrapperCol" :wrapperCol="wrapperCol"
label="数据库名称"> label="数据库名称">
<a-input placeholder="请输入数据库名称" v-decorator="['dbName', validatorRules.dbName]"/> <a-input placeholder="请输入数据库名称" v-decorator="['dbName', validatorRules.dbName]"/>
</a-form-item> </a-form-item>-->
<a-form-item <a-form-item
:labelCol="labelCol" :labelCol="labelCol"
:wrapperCol="wrapperCol" :wrapperCol="wrapperCol"
@ -124,7 +124,7 @@
dbUrl: { rules: [{ required: true, message: '请输入数据源地址!' }] }, dbUrl: { rules: [{ required: true, message: '请输入数据源地址!' }] },
dbName: { rules: [{ required: true, message: '请输入数据库名称!' }] }, dbName: { rules: [{ required: true, message: '请输入数据库名称!' }] },
dbUsername: { rules: [{ required: true, message: '请输入用户名!' }] }, dbUsername: { rules: [{ required: true, message: '请输入用户名!' }] },
dbPassword: { rules: [{ required: true, message: '请输入密码!' }] } dbPassword: { rules: [{ required: false, message: '请输入密码!' }] }
}, },
url: { url: {
add: '/sys/dataSource/add', add: '/sys/dataSource/add',
@ -142,7 +142,25 @@
// marialDB 数据库 // marialDB 数据库
'5': { dbDriver: 'org.mariadb.jdbc.Driver' }, '5': { dbDriver: 'org.mariadb.jdbc.Driver' },
// postgresql 数据库 // postgresql 数据库
'6': { dbDriver: 'org.postgresql.Driver' } '6': { dbDriver: 'org.postgresql.Driver' },
// 达梦 数据库
'7': { dbDriver: 'dm.jdbc.driver.DmDriver' },
// 人大金仓 数据库
'8': { dbDriver: 'com.kingbase8.Driver' },
// 神通 数据库
'9': { dbDriver: 'com.oscar.Driver' },
// SQLite 数据库
'10': { dbDriver: 'org.sqlite.JDBC' },
// DB2 数据库
'11': { dbDriver: 'com.ibm.db2.jcc.DB2Driver' },
// Hsqldb 数据库
'12': { dbDriver: 'org.hsqldb.jdbc.JDBCDriver' },
// Derby 数据库
'13': { dbDriver: 'org.apache.derby.jdbc.ClientDriver' },
// H2 数据库
'14': { dbDriver: 'org.h2.Driver' },
// 其他数据库
'15': { dbDriver: '' }
}, },
dbUrlMap: { dbUrlMap: {
// MySQL 数据库 // MySQL 数据库
@ -153,10 +171,28 @@
'2': { dbUrl: 'jdbc:oracle:thin:@127.0.0.1:1521:ORCL' }, '2': { dbUrl: 'jdbc:oracle:thin:@127.0.0.1:1521:ORCL' },
// SQLServer 数据库 // SQLServer 数据库
'3': { dbUrl: 'jdbc:sqlserver://127.0.0.1:1433;SelectMethod=cursor;DatabaseName=jeecgboot' }, '3': { dbUrl: 'jdbc:sqlserver://127.0.0.1:1433;SelectMethod=cursor;DatabaseName=jeecgboot' },
// SQLServer 数据库 // Mariadb 数据库
'5': { dbUrl: 'jdbc:mariadb://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useSSL=false' }, '5': { dbUrl: 'jdbc:mariadb://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useSSL=false' },
// SQLServer 数据库 // Postgresql 数据库
'6': { dbUrl: 'jdbc:postgresql://127.0.0.1:5432/jeecg-boot' } '6': { dbUrl: 'jdbc:postgresql://127.0.0.1:5432/jeecg-boot' },
// 达梦 数据库
'7': { dbUrl: 'jdbc:dm://127.0.0.1:5236/?jeecg-boot&zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf-8' },
// 人大金仓 数据库
'8': { dbUrl: 'jdbc:kingbase8://127.0.0.1:54321/jeecg-boot' },
// 神通 数据库
'9': { dbUrl: 'jdbc:oscar://192.168.1.125:2003/jeecg-boot' },
// SQLite 数据库
'10': { dbUrl: 'jdbc:sqlite://opt/test.db' },
// DB2 数据库
'11': { dbUrl: 'jdbc:db2://127.0.0.1:50000/jeecg-boot' },
// Hsqldb 数据库
'12': { dbUrl: 'jdbc:hsqldb:hsql://127.0.0.1/jeecg-boot' },
// Derby 数据库
'13': { dbUrl: 'jdbc:derby://127.0.0.1:1527/jeecg-boot' },
// H2 数据库
'14': { dbUrl: 'jdbc:h2:tcp://127.0.0.1:8082/jeecg-boot' },
// 其他数据库
'15': { dbUrl: '' }
} }
} }
}, },

View File

@ -59,7 +59,7 @@
<!--部门分配--> <!--部门分配-->
<a-form-model-item label="部门分配" :labelCol="labelCol" :wrapperCol="wrapperCol" v-show="!departDisabled"> <a-form-model-item label="部门分配" :labelCol="labelCol" :wrapperCol="wrapperCol" v-show="!departDisabled">
<j-select-depart v-model="model.selecteddeparts" :multi="true" @back="backDepartInfo" :backDepart="true"></j-select-depart> <j-select-depart v-model="model.selecteddeparts" :multi="true" @back="backDepartInfo" :backDepart="true" :treeOpera="true">></j-select-depart>
</a-form-model-item> </a-form-model-item>
<!--租户分配--> <!--租户分配-->

View File

@ -36,12 +36,12 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import { ACCESS_TOKEN ,ENCRYPTED_STRING} from "@/store/mutation-types" import { ACCESS_TOKEN, ENCRYPTED_STRING } from '@/store/mutation-types'
import ThirdLogin from './third/ThirdLogin' import ThirdLogin from './third/ThirdLogin'
import LoginSelectTenant from "./LoginSelectTenant" import LoginSelectTenant from './LoginSelectTenant'
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha' import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { encryption , getEncryptedString } from '@/utils/encryption/aesEncrypt' import { getEncryptedString } from '@/utils/encryption/aesEncrypt'
import { timeFix } from "@/utils/util" import { timeFix } from '@/utils/util'
import LoginAccount from './LoginAccount' import LoginAccount from './LoginAccount'
import LoginPhone from './LoginPhone' import LoginPhone from './LoginPhone'

View File

@ -2,7 +2,7 @@
<div> <div>
<a-form-model ref="form" :model="model" :rules="validatorRules"> <a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-model-item required prop="username"> <a-form-model-item required prop="username">
<a-input v-model="model.username" size="large" placeholder="请输入帐户名 / jeecg"> <a-input v-model="model.username" size="large" placeholder="请输入帐户名 / admin">
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/> <a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input> </a-input>
</a-form-model-item> </a-form-model-item>

View File

@ -51,9 +51,8 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import { getAction,putAction } from '@/api/manage' import { putAction } from '@/api/manage'
import { USER_INFO } from "@/store/mutation-types" import { USER_INFO } from '@/store/mutation-types'
import store from './Login'
export default { export default {
name: 'LoginSelectTenant', name: 'LoginSelectTenant',
@ -111,18 +110,19 @@
this.isMultiDepart = false this.isMultiDepart = false
} }
}, },
bizTenant(ids){ bizTenantList(loginResult) {
if(!ids || ids.length==0){ let tenantList = loginResult.tenantList
if (Array.isArray(tenantList)) {
if (tenantList.length === 0) {
this.isMultiTenant = false this.isMultiTenant = false
} else if(ids.indexOf(',')<0){ } else if (tenantList.length === 1) {
this.tenant_id = ids; this.tenant_id = tenantList[0].id
this.isMultiTenant = false this.isMultiTenant = false
} else { } else {
this.visible = true this.visible = true
this.isMultiTenant = true this.isMultiTenant = true
getAction('/sys/tenant/queryList', {ids: ids}).then(res=>{ this.tenantList = tenantList
this.tenantList = res.result }
})
} }
}, },
show(loginResult){ show(loginResult){
@ -131,8 +131,7 @@
let user = Vue.ls.get(USER_INFO) let user = Vue.ls.get(USER_INFO)
this.username = user.username this.username = user.username
let ids = user.relTenantIds this.bizTenantList(loginResult);
this.bizTenant(ids);
if(this.visible===false){ if(this.visible===false){
this.$store.dispatch('saveTenant', this.tenant_id); this.$store.dispatch('saveTenant', this.tenant_id);

View File

@ -0,0 +1,130 @@
<template>
<div>
<div id="loader-wrapper">
<div id="loader"></div>
<div class="loader-section section-left"></div>
<div class="loader-section section-right"></div>
<div class="load_title">正在登录 JeecgBoot 低代码平台,请耐心等待</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { isOAuth2AppEnv, timeFix } from '@/utils/util'
import { INDEX_MAIN_PAGE_PATH } from '@/store/mutation-types'
export default {
name: 'OAuth2Login',
data() {
return {
env: {
thirdApp: false,
wxWork: false,
dingtalk: false,
},
}
},
beforeCreate() {
// 如果当前 不是 OAuth2APP环境就重定向到 /user/login 页面
if (!isOAuth2AppEnv()) {
this.$router.replace({path: '/user/login'})
}
},
created() {
this.checkEnv()
this.doOAuth2Login()
},
methods: {
...mapActions(['ThirdLogin']),
/** 检测当前的环境 */
checkEnv() {
// 判断当时是否是企业微信环境
if (/wxwork/i.test(navigator.userAgent)) {
this.env.thirdApp = true
this.env.wxWork = true
}
// 判断当时是否是钉钉环境
if (/dingtalk/i.test(navigator.userAgent)) {
this.env.thirdApp = true
this.env.dingtalk = true
}
},
/** 进行OAuth2登录操作 */
doOAuth2Login() {
if (this.env.thirdApp) {
// 判断是否携带了Token是就说明登录成功
if (this.$route.query.oauth2LoginToken) {
this.thirdType = this.$route.query.thirdType
let token = this.$route.query.oauth2LoginToken
this.doThirdLogin(token)
} else if (this.env.wxWork) {
this.doWechatEnterpriseOAuth2Login()
} else if (this.env.dingtalk) {
this.doDingTalkOAuth2Login()
}
}
},
// 根据token执行登录
doThirdLogin(token) {
let param = {}
param.thirdType = this.thirdType
param.token = token
this.ThirdLogin(param).then(res => {
if (res.success) {
this.loginSuccess()
} else {
this.requestFailed(res)
}
})
},
loginSuccess() {
// 登陆成功,重定向到主页
this.$router.replace({path: INDEX_MAIN_PAGE_PATH})
// TODO 这个提示是否还需要?
this.$notification.success({
message: '欢迎',
description: `${timeFix()},欢迎回来`,
})
},
requestFailed(err) {
this.$error({
title: '登录失败',
content: ((err.response || {}).data || {}).message || err.message || '请求出现错误,请稍后再试',
okText: '重新登陆',
onOk() {
window.location.reload()
},
onCancel() {
window.location.reload()
},
})
},
/** 企业微信OAuth2登录 */
doWechatEnterpriseOAuth2Login() {
this.sysOAuth2Login('wechat_enterprise')
},
/** 钉钉OAuth2登录 */
doDingTalkOAuth2Login() {
this.sysOAuth2Login('dingtalk')
},
/** 后台构造oauth2登录地址 */
sysOAuth2Login(source) {
let url = `${window._CONFIG['domianURL']}/sys/thirdLogin/oauth2/${source}/login`
url += `?state=${encodeURIComponent(window.location.origin)}`
window.location.href = url
},
},
}
</script>
<style scoped>
</style>

View File

@ -888,10 +888,10 @@
cssnano-preset-default "^4.0.0" cssnano-preset-default "^4.0.0"
postcss "^7.0.0" postcss "^7.0.0"
"@jeecg/antd-online-mini@2.4.5-RC": "@jeecg/antd-online-mini@2.4.6-beta5":
version "2.4.5-RC" version "2.4.6-beta5"
resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-2.4.5-RC.tgz#88740572f352a8baae694a83305f289b54e603c4" resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-2.4.6-beta5.tgz#d019ddcd87838773407c3052c21ba45163bfa76b"
integrity sha512-2Wxj7wr+j3YGU5W1NtOWGM/qv1bd28UVBAxMCIZ/vY12w8+qnaNK9IKri6oJmqYiB40UA9EUqsxE9iMlRfv3Ww== integrity sha512-js+WOJdvqE31GvVkAt1VLD0L50uHYhY6gE+g8XqJoUS/JD6q4eYyHOKsGj6tG0TlNbCiAFzZBHo6bFWGVI/miQ==
"@mrmlnc/readdir-enhanced@^2.2.1": "@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1" version "2.2.1"
@ -1659,9 +1659,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
color-convert "^2.0.1" color-convert "^2.0.1"
ant-design-vue@^1.7.2: ant-design-vue@^1.7.2:
version "1.7.5" version "1.7.7"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.5.tgz#e7ed04cb358adc56be02c7453d2db026d2c405e3" resolved "https://registry.nlark.com/ant-design-vue/download/ant-design-vue-1.7.7.tgz#621cca28c89e2887ec391e5c129c34d463f3803e"
integrity sha512-QVrirCz6eetzui+Dv/ujMqUwIr8e/2H+pKr4VC3mpc+cHkw6L9cBRJK7DhfO7GTyK4EWKkEH3lfLWlD/XjUoQQ== integrity sha1-YhzKKMieKIfsOR5cEpw01GPzgD4=
dependencies: dependencies:
"@ant-design/icons" "^2.1.1" "@ant-design/icons" "^2.1.1"
"@ant-design/icons-vue" "^2.0.0" "@ant-design/icons-vue" "^2.0.0"
@ -1725,11 +1725,6 @@ arch@^2.1.1:
resolved "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf" resolved "https://registry.npmjs.org/arch/-/arch-2.1.2.tgz#0c52bbe7344bb4fa260c443d2cbad9c00ff2f0bf"
integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ== integrity sha512-NTBIIbAfkJeIletyABbVtdPgeKfDafR+1mZV/AyyfC1UkVkp9iUjV+wwmqtUgphHYajbI86jejBJp5e+jkGTiQ==
area-data@^5.0.6:
version "5.0.6"
resolved "https://registry.npmjs.org/area-data/-/area-data-5.0.6.tgz#c381c3f88ff9e1050accd07549d206fce2a28d13"
integrity sha512-QxLoA+823xXKyhw5S3750I9TToki0OS42HU9ol3rCOsCXfkjtl8RtQ/eoj0cK0Levn4//oEM05FmMumfw/HIlg==
argparse@^1.0.7: argparse@^1.0.7:
version "1.0.10" version "1.0.10"
resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@ -2897,8 +2892,8 @@ cache-loader@^2.0.1:
call-bind@^1.0.0: call-bind@^1.0.0:
version "1.0.2" version "1.0.2"
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" resolved "https://registry.nlark.com/call-bind/download/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== integrity sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
get-intrinsic "^1.0.2" get-intrinsic "^1.0.2"
@ -3089,6 +3084,11 @@ check-types@^8.0.3:
resolved "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" resolved "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552"
integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ==
china-area-data@^5.0.1:
version "5.0.1"
resolved "https://registry.npm.taobao.org/china-area-data/download/china-area-data-5.0.1.tgz#7943b83a0619f033bb5893da80cb46e52e44be66"
integrity sha1-eUO4OgYZ8DO7WJPagMtG5S5EvmY=
chokidar@^2.1.2, chokidar@^2.1.8: chokidar@^2.1.2, chokidar@^2.1.8:
version "2.1.8" version "2.1.8"
resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" resolved "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917"
@ -3670,8 +3670,8 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
cron-parser@^2.10.0: cron-parser@^2.10.0:
version "2.18.0" version "2.18.0"
resolved "https://registry.npmjs.org/cron-parser/-/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf" resolved "https://registry.nlark.com/cron-parser/download/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf"
integrity sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg== integrity sha1-3huwrVKMgVVINxmT+BpU5aCJ7c8=
dependencies: dependencies:
is-nan "^1.3.0" is-nan "^1.3.0"
moment-timezone "^0.5.31" moment-timezone "^0.5.31"
@ -5696,8 +5696,8 @@ get-caller-file@^2.0.1:
get-intrinsic@^1.0.2: get-intrinsic@^1.0.2:
version "1.1.1" version "1.1.1"
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" resolved "https://registry.nlark.com/get-intrinsic/download/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== integrity sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=
dependencies: dependencies:
function-bind "^1.1.1" function-bind "^1.1.1"
has "^1.0.3" has "^1.0.3"
@ -6618,8 +6618,8 @@ is-mobile@^2.2.1:
is-nan@^1.3.0: is-nan@^1.3.0:
version "1.3.2" version "1.3.2"
resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" resolved "https://registry.npm.taobao.org/is-nan/download/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w== integrity sha1-BDpUreoxdItVts1OCara+mm9nh0=
dependencies: dependencies:
call-bind "^1.0.0" call-bind "^1.0.0"
define-properties "^1.1.3" define-properties "^1.1.3"
@ -7672,14 +7672,14 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1:
moment-timezone@^0.5.31: moment-timezone@^0.5.31:
version "0.5.33" version "0.5.33"
resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c" resolved "https://registry.nlark.com/moment-timezone/download/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c"
integrity sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w== integrity sha1-slL9a7V/NBybWaWrYajlGnO70iw=
dependencies: dependencies:
moment ">= 2.9.0" moment ">= 2.9.0"
"moment@>= 2.9.0": "moment@>= 2.9.0":
version "2.29.1" version "2.29.1"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moment@^2.21.0: moment@^2.21.0:

View File

@ -1,13 +1,13 @@
Jeecg-Boot 低代码开发平台 Jeecg-Boot 低代码开发平台
=============== ===============
当前最新版本: 2.4.5发布日期20210607 当前最新版本: 2.4.6发布日期20210813
## 后端技术架构 ## 后端技术架构
- 基础框架Spring Boot 2.3.5.RELEASE - 基础框架Spring Boot 2.3.5.RELEASE
- 持久层框架Mybatis-plus 3.4.1 - 持久层框架Mybatis-plus 3.4.3.1
- 安全框架Apache Shiro 1.7.0Jwt 3.11.0 - 安全框架Apache Shiro 1.7.0Jwt 3.11.0
@ -29,7 +29,7 @@ Jeecg-Boot 低代码开发平台
- 依赖管理Maven - 依赖管理Maven
- 数据库MySQL5.7+ & Oracle 11g - 数据库MySQL5.7+ & Oracle 11g & SqlServer & postgresql
- 缓存Redis - 缓存Redis
@ -43,7 +43,7 @@ Jeecg-Boot 低代码开发平台
- 常见问题: [http://jeecg.com/doc/qa](http://jeecg.com/doc/qa) - 常见问题: [http://jeecg.com/doc/qa](http://jeecg.com/doc/qa)
- QQ交流群 ③816531124、①284271917、②769925425 - QQ交流群 ④774126647、③816531124、①284271917、②769925425
## 专项文档 ## 专项文档

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,67 +0,0 @@
-- online 新增部门授权 扩展字段
ALTER TABLE `onl_auth_relation`
ADD COLUMN `auth_mode` varchar(50) NULL COMMENT '授权方式role角色depart部门user人' AFTER `cgform_id`;
update onl_auth_relation set auth_mode = 'role';
-- 部门表新增 qywx_identifier 字段
ALTER TABLE `sys_depart`
ADD COLUMN `qywx_identifier` varchar(100) NULL COMMENT '对接企业微信的ID' AFTER `del_flag`;
-- sys_third_account 表新增 third_user_id 字段
ALTER TABLE `sys_third_account`
ADD COLUMN `third_user_id` varchar(100) NULL COMMENT '第三方app用户账号' AFTER `third_user_uuid`;
-- 新增第三方APP消息测试菜单
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`) VALUES ('1387612436586065922', '2a470fc0c3954d9dbb61de6d80846549', '第三方APP消息测试', '/jeecg/ThirdAppMessageTest', 'jeecg/ThirdAppMessageTest', '1', NULL, NULL, '1', NULL, '1', '3', '0', NULL, '1', '0', '0', NULL, 'admin', '2021-04-29 11:39:20', 'admin', '2021-04-29 11:39:27', '0', '0', '1', '0');
-- 定时任务一个类允许配置多个调度
-- 删除定时任务表唯一索引
ALTER TABLE `sys_quartz_job`
DROP INDEX `uniq_job_class_name`;
-- 停止所有的定时任务用于旧数据只执行一次即可执行完毕后需要重启后台项目[手动]再次打开定时任务
DELETE FROM `qrtz_cron_triggers`;
DELETE FROM `qrtz_fired_triggers`;
DELETE FROM `qrtz_triggers`;
DELETE FROM `qrtz_job_details`;
UPDATE `sys_quartz_job` SET `status` = '-1';
-- 不支持mariaDB数据库处理 issues/I3QID1
update sys_dict_item set item_text = 'MariaDB' where id ='1349250340104474626';
update sys_dict_item set item_text = 'Postgresql' where id ='1349254569766457345';
-- sys_announcement新增dt_task_id字段
ALTER TABLE `sys_announcement`
ADD COLUMN `dt_task_id` varchar(100) NULL COMMENT '钉钉task_id用于撤回消息' AFTER `msg_abstract`;
-- 模板类型注释错误
ALTER TABLE `jimu_report`
MODIFY COLUMN `template` tinyint(1) NULL DEFAULT NULL COMMENT '是否是模板 0不是,1是' AFTER `api_code`;
-- 查询支持默认值
ALTER TABLE `jimu_report_db_field`
ADD COLUMN `search_value` varchar(100) NULL COMMENT '查询默认值' AFTER `dict_code`;
-- jimu_report_db增加JSON数据字段
ALTER TABLE `jimu_report_db`
ADD COLUMN `json_data` text NULL COMMENT 'json数据直接解析json内容' AFTER `db_source_type`;
-- 连接失败次数
ALTER TABLE `jimu_report_data_source`
ADD COLUMN `connect_times` int(1) NULL COMMENT '连接失败次数' AFTER `update_time`;
-- 联动图表ID
ALTER TABLE `jimu_report_link`
ADD COLUMN `link_chart_id` varchar(50) NULL COMMENT '联动图表的ID' AFTER `api_url`;
-- 类转换器字段添加
ALTER TABLE `jimu_report_db`
ADD COLUMN `api_convert` varchar(255) NULL COMMENT 'api转换器' AFTER `json_data`;
-- 修复脚本的一些问题---
update sys_permission set url="{{ window._CONFIG['domianURL'] }}/jmreport/view/1352160857479581696?token=${token}" where id ='1352200630711652354';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:20:44', `update_time` = '2021-05-19 19:20:44', `db_code` = 'infoForReport', `db_ch_name` = '信息', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/infoForReport', `api_method` = '0', `is_list` = 1, `is_page` = '1', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = '60b3feffadc55eb49baa5a48fdf1ff0e';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:20:50', `update_time` = '2021-05-19 19:20:50', `db_code` = 'memoryForReport', `db_ch_name` = '内存', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/memoryForReport', `api_method` = '0', `is_list` = 1, `is_page` = '0', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = '6a1d22ca4c95e8fab655d3ceed43a84d';
UPDATE `jimu_report_db` SET `jimu_report_id` = '1352160857479581696', `create_by` = 'admin', `update_by` = 'admin', `create_time` = '2021-05-19 19:21:03', `update_time` = '2021-05-19 19:21:03', `db_code` = 'keysSizeForReport', `db_ch_name` = '数量', `db_type` = '1', `db_table_name` = NULL, `db_dyn_sql` = NULL, `db_key` = NULL, `tb_db_key` = NULL, `tb_db_table_name` = NULL, `java_type` = NULL, `java_value` = NULL, `api_url` = '{{ domainURL }}/sys/actuator/redis/keysSizeForReport', `api_method` = '0', `is_list` = 1, `is_page` = '0', `db_source` = '', `db_source_type` = NULL, `json_data` = NULL WHERE `id` = 'd4a29dfda94357308faf62be2b94db08';

View File

@ -0,0 +1,96 @@
-- 平台基础模块
ALTER TABLE `sys_third_account`
ADD UNIQUE INDEX `uniq_sys_third_account_third_type_third_user_id` (`third_type`, `third_user_id`) USING BTREE ;
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`, `keep_alive`, `hidden`, `description`, `status`, `del_flag`, `rule_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `internal_or_external`) VALUES ('1404684556047024130', '08e6b9dc3c04489c8e1ff2ce6f105aa4', '在线用户', '/isystem/online', 'system/SysUserOnlineList', NULL, NULL, 1, NULL, '1', NULL, 0, NULL, 1, 1, 0, 0, NULL, '1', 0, 0, 'admin', '2021-06-15 14:17:51', NULL, NULL, 0);
DELETE FROM sys_depart WHERE id = '743ba9dbdc114af8953a11022ef3096a';
alter table sys_quartz_job engine = InnoDB;
UPDATE `sys_dict_item` SET `item_value` = '6' WHERE `item_text` = 'MYSQL5.7';
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 ('1414837074500976641', '1209733563293962241', 'Postgresql', '6', '', '5', '1', 'admin', '2021-07-13 14:40:20', 'admin', '2021-07-15 13:44:15');
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 ('1415547541091504129', '1209733563293962241', 'MarialDB', '5', '', '6', '1', 'admin', '2021-07-15 13:43:28', 'admin', '2021-07-15 13:44:23');
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 ('1418050323111399425', '1209733563293962241', 'Derby', '13', '', '13', '1', 'admin', '2021-07-22 11:28:38', 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 ('1418050209823248385', '1209733563293962241', 'Hsqldb', '12', '', '12', '1', 'admin', '2021-07-22 11:28:11', 'admin', '2021-07-22 11:28:27');
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 ('1418050149475602434', '1209733563293962241', 'DB2', '11', '', '11', '1', 'admin', '2021-07-22 11:27:56', 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 ('1418050110669901826', '1209733563293962241', 'SQLite', '10', '', '10', '1', 'admin', '2021-07-22 11:27:47', 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 ('1418050075555188737', '1209733563293962241', '神通', '9', '', '9', '1', 'admin', '2021-07-22 11:27:39', 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 ('1418050017053036545', '1209733563293962241', '人大金仓', '8', '', '8', '1', 'admin', '2021-07-22 11:27:25', 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 ('1418049969003089922', '1209733563293962241', '达梦', '7', '', '7', '1', 'admin', '2021-07-22 11:27:13', 'admin', '2021-07-22 11:27:30');
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 ('1418117316707590146', '1209733563293962241', 'H2', '14', '', '14', '1', 'admin', '2021-07-22 15:54:50', 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 ('1418491604048449537', '1209733563293962241', '其他数据库', '15', '', 15, 1, 'admin', '2021-07-23 16:42:07', NULL, NULL);
ALTER TABLE demo ADD COLUMN tenant_id int(10) NULL DEFAULT 0;
-- Online模块
ALTER TABLE `onl_cgform_head`
ADD COLUMN `ext_config_json` varchar(1000) NULL COMMENT '扩展JSON' AFTER `physic_id`;
ALTER TABLE `onl_cgreport_head`
ADD COLUMN `low_app_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '关联的应用ID' AFTER `content`;
-- 积木报表模块
UPDATE `jimu_report` SET `json_str` = '{\"loopBlockList\":[],\"area\":{\"sri\":16,\"sci\":5,\"eri\":16,\"eci\":5,\"width\":147,\"height\":25},\"excel_config_id\":\"1347373863746539520\",\"printConfig\":{\"paper\":\"A4\",\"width\":210,\"height\":297,\"definition\":1,\"isBackend\":false,\"marginX\":10,\"marginY\":10,\"layout\":\"portrait\"},\"rows\":{\"0\":{\"cells\":{\"0\":{\"text\":\"\"},\"1\":{\"text\":\"\"}}},\"1\":{\"cells\":{\"0\":{\"text\":\"\"}}},\"3\":{\"cells\":{\"2\":{\"text\":\"\",\"rendered\":\"\"}}},\"5\":{\"cells\":{},\"height\":29},\"6\":{\"cells\":{\"2\":{\"text\":\"\",\"style\":2}},\"height\":34},\"7\":{\"cells\":{\"2\":{\"merge\":[0,4],\"text\":\"实习证明\",\"style\":2}},\"height\":41},\"8\":{\"cells\":{\"1\":{\"text\":\"\",\"style\":3},\"2\":{\"text\":\"\"}}},\"9\":{\"cells\":{\"1\":{\"text\":\"\",\"style\":3},\"2\":{\"text\":\"\",\"style\":3},\"3\":{\"text\":\"\"}},\"isDrag\":true,\"height\":33},\"10\":{\"cells\":{\"2\":{\"text\":\"${tt.name}\",\"style\":11},\"3\":{\"text\":\"同学在我公司与 2020年4月1日 至 2020年5月1日 实习。\",\"style\":19,\"merge\":[0,3],\"height\":34}},\"height\":34},\"11\":{\"cells\":{},\"height\":28},\"12\":{\"cells\":{\"1\":{\"text\":\"\",\"style\":6},\"2\":{\"style\":13,\"text\":\"${tt.pingjia}\",\"merge\":[3,4],\"height\":129}},\"height\":36},\"13\":{\"cells\":{},\"height\":29},\"14\":{\"cells\":{},\"height\":33},\"15\":{\"cells\":{},\"height\":31},\"16\":{\"cells\":{}},\"17\":{\"cells\":{\"1\":{\"text\":\"\"},\"2\":{\"text\":\"特此证明!\",\"style\":12}}},\"20\":{\"cells\":{\"2\":{\"text\":\"\"},\"3\":{\"text\":\"\",\"style\":3},\"4\":{\"text\":\"\"}}},\"21\":{\"cells\":{\"4\":{\"text\":\"\"}}},\"22\":{\"cells\":{\"3\":{\"text\":\"\",\"style\":3},\"4\":{\"text\":\"证明人:\",\"style\":11},\"5\":{\"text\":\"${tt.lingdao}\",\"style\":12}}},\"23\":{\"cells\":{\"4\":{\"text\":\"\"},\"5\":{\"text\":\"${tt.shijian}\",\"style\":15}}},\"len\":100},\"dbexps\":[],\"dicts\":[],\"freeze\":\"A1\",\"dataRectWidth\":576,\"displayConfig\":{},\"background\":{\"path\":\"https://static.jeecg.com/designreport/images/11_1611283832037.png\",\"repeat\":\"no-repeat\",\"width\":\"\",\"height\":\"\"},\"name\":\"sheet1\",\"autofilter\":{},\"styles\":[{\"align\":\"center\"},{\"align\":\"center\",\"font\":{\"size\":14}},{\"align\":\"center\",\"font\":{\"size\":16}},{\"align\":\"right\"},{\"align\":\"left\"},{\"align\":\"left\",\"valign\":\"top\"},{\"align\":\"left\",\"valign\":\"top\",\"textwrap\":true},{\"font\":{\"size\":16}},{\"align\":\"left\",\"valign\":\"top\",\"textwrap\":false},{\"textwrap\":false},{\"textwrap\":true},{\"align\":\"right\",\"font\":{\"size\":12}},{\"font\":{\"size\":12}},{\"align\":\"left\",\"valign\":\"top\",\"textwrap\":true,\"font\":{\"size\":12}},{\"textwrap\":true,\"font\":{\"size\":12}},{\"align\":\"left\",\"font\":{\"size\":12}},{\"font\":{\"size\":12},\"border\":{\"bottom\":[\"thin\",\"#000\"],\"top\":[\"thin\",\"#000\"],\"left\":[\"thin\",\"#000\"],\"right\":[\"thin\",\"#000\"]}},{\"font\":{\"size\":14}},{\"font\":{\"size\":10}},{\"textwrap\":false,\"font\":{\"size\":12}}],\"validations\":[],\"cols\":{\"0\":{\"width\":69},\"1\":{\"width\":41},\"4\":{\"width\":119},\"5\":{\"width\":147},\"6\":{\"width\":31},\"len\":50},\"merges\":[\"C8:G8\",\"D11:G11\",\"C13:G16\"]}' WHERE `id` = '1347373863746539520';
update jimu_report_data_source set connect_times = 0;
ALTER TABLE `jimu_report_data_source`
MODIFY COLUMN `connect_times` int(1) UNSIGNED NULL DEFAULT 0 COMMENT '连接失败次数' AFTER `update_time`;
ALTER TABLE `jimu_report_db_param`
MODIFY COLUMN `param_value` varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '参数默认值' AFTER `param_txt`;
DELETE FROM jimu_report_map
WHERE
id IN (SELECT id FROM (SELECT id FROM jimu_report_map WHERE NAME IN ( SELECT NAME FROM jimu_report_map GROUP BY NAME HAVING count(NAME) > 1)) T)
AND id NOT IN (SELECT id FROM ( SELECT min(id) id FROM jimu_report_map GROUP BY NAME HAVING count(NAME) > 1) M);
ALTER TABLE `jimu_report_map`
ADD UNIQUE INDEX `uniq_jmreport_map_name`(`name`);
update jimu_report set VIEW_COUNT = 0 WHERE VIEW_COUNT is null or VIEW_COUNT = '';
ALTER TABLE `jimu_report`
MODIFY COLUMN `view_count` bigint(15) NULL DEFAULT 0 COMMENT '浏览次数' AFTER `template`;
ALTER TABLE `jimu_report_db`
ADD INDEX `idx_jimu_report_id`(`jimu_report_id`);
ALTER TABLE `jimu_report_db`
ADD INDEX `idx_db_source_id`(`db_source`);
ALTER TABLE `jimu_report_db_field`
ADD INDEX `idx_dbfield_order_num`(`order_num`);
ALTER TABLE `jimu_report`
ADD INDEX `uniq_jmreport_createby`(`create_by`);
ALTER TABLE `jimu_report`
ADD INDEX `uniq_jmreport_delflag`(`del_flag`);
ALTER TABLE `jimu_report_link`
ADD INDEX `uniq_link_reportid`(`report_id`);
ALTER TABLE `jimu_report`
MODIFY COLUMN `json_str` longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL COMMENT 'json字符串' AFTER `type`;
ALTER TABLE `jimu_report_link`
ADD COLUMN `expression` varchar(255) NULL COMMENT '表达式' AFTER `link_chart_id`;
-- 执行下面段可能会报错,说明此部分升级过了,忽略即可
ALTER TABLE `jimu_report_db_param`
ADD COLUMN `search_flag` int(1) NULL COMMENT '查询标识0否1是 默认0' AFTER `update_time`;
update jimu_report_db_param set search_flag = 0;
create table jimu_dict like sys_dict;
insert into jimu_dict select * from sys_dict;
create table jimu_dict_item like sys_dict_item;
insert into jimu_dict_item select * from sys_dict_item;
ALTER TABLE `jimu_report_db_param`
ADD COLUMN `widget_type` varchar(50) NULL COMMENT '查询控件类型' AFTER `search_flag`,
ADD COLUMN `search_mode` int(1) NULL COMMENT '查询模式1简单2范围' AFTER `widget_type`,
ADD COLUMN `dict_code` varchar(255) NULL COMMENT '字典' AFTER `search_mode`;

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>jeecg-boot-base-api</artifactId> <artifactId>jeecg-boot-base-api</artifactId>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<version>2.4.5</version> <version>2.4.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,89 @@
package org.jeecg.common.bpm.api;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.common.online.api.factory.OnlineBaseExtAPIFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
* 流程接口
*
* @author scott
*/
@Component
@FeignClient(contextId = "bpmBaseRemoteApi", value = ServiceNameConstants.SYSTEM_SERVICE,
fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
public interface IBpmBaseExtAPI {
/**
* 23. 流程提交接口online自定义开发
*
* @param flowCode
* 流程业务关联 例如joa_leave_01
* @param id
* 表单业务数据data id
* @param formUrl
* 流程审批时附件页面默认展示的PC端表单组件地址
* @param formUrlMobile
* 流程审批时附件页面默认展示的移动端表单组件(地址)
* @param username
* 流程发起人账号
* @param jsonData
* Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
@PostMapping(value = "/act/process/extActProcess/startMutilProcess")
Result<String> startMutilProcess(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
@RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
@RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
/**
* 24. 流程提交接口(自定义表单设计器)
*
* @param flowCode
* 流程业务关联 例如joa_leave_01
* @param id
* 表单业务数据data id
* @param formUrl
* 流程审批时附件页面默认展示的PC端表单组件地址
* @param formUrlMobile
* 流程审批时附件页面默认展示的移动端表单组件(地址)
* @param username
* 流程发起人账号
* @param jsonData
* Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
@PostMapping(value = "/act/process/extActProcess/startDesFormMutilProcess")
Result<String> startDesFormMutilProcess(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
@RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
@RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
/**
* 25. 保存流程草稿箱接口自定义开发表单、online表单
*
* @param flowCode
* 流程业务关联 例如joa_leave_01
* @param id
* 表单业务数据data id
* @param formUrl
* 流程审批时附件页面默认展示的PC端表单组件地址 【非必填】
* @param formUrlMobile
* 流程审批时附件页面默认展示的移动端表单组件(地址) 【非必填】
* @param username
* 流程发起人账号
* @param jsonData
* Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
@PostMapping(value = "/act/process/extActProcess/saveMutilProcessDraft")
Result<String> saveMutilProcessDraft(@RequestParam("flowCode") String flowCode, @RequestParam("id") String id,
@RequestParam("formUrl") String formUrl, @RequestParam("formUrlMobile") String formUrlMobile,
@RequestParam("username") String username, @RequestParam("jsonData") String jsonData) throws Exception;
}

View File

@ -0,0 +1,18 @@
package org.jeecg.common.bpm.api.factory;
import org.jeecg.common.bpm.api.IBpmBaseExtAPI;
import org.jeecg.common.bpm.api.fallback.BpmBaseExtAPIFallback;
import org.springframework.stereotype.Component;
import feign.hystrix.FallbackFactory;
@Component
public class BpmBaseExtAPIFallbackFactory implements FallbackFactory<IBpmBaseExtAPI> {
@Override
public IBpmBaseExtAPI create(Throwable throwable) {
BpmBaseExtAPIFallback fallback = new BpmBaseExtAPIFallback();
fallback.setCause(throwable);
return fallback;
}
}

View File

@ -0,0 +1,42 @@
package org.jeecg.common.bpm.api.fallback;
import java.util.List;
import java.util.Map;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.bpm.api.IBpmBaseExtAPI;
import org.jeecg.common.online.api.IOnlineBaseExtAPI;
import org.jeecg.common.system.vo.DictModel;
import com.alibaba.fastjson.JSONObject;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
/**
* 进入fallback的方法 检查是否token未设置
*/
@Slf4j
public class BpmBaseExtAPIFallback implements IBpmBaseExtAPI {
@Setter
private Throwable cause;
@Override
public Result<String> startMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,
String username, String jsonData) throws Exception {
return null;
}
@Override
public Result<String> startDesFormMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,
String username, String jsonData) throws Exception {
return null;
}
@Override
public Result<String> saveMutilProcessDraft(String flowCode, String id, String formUrl, String formUrlMobile,
String username, String jsonData) throws Exception {
return null;
}
}

View File

@ -0,0 +1,72 @@
package org.jeecg.common.online.api;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.constant.ServiceNameConstants;
import org.jeecg.common.online.api.factory.OnlineBaseExtAPIFallbackFactory;
import org.jeecg.common.system.vo.DictModel;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
/**
* 【Online】Feign API接口
*/
@Component
@FeignClient(contextId = "onlineBaseRemoteApi", value = ServiceNameConstants.SYSTEM_ONLINE, fallbackFactory = OnlineBaseExtAPIFallbackFactory.class)
public interface IOnlineBaseExtAPI {
/**
* 【Online】 表单设计器专用:同步新增
*/
@PostMapping(value = "/online/api/cgform/crazyForm/{name}")
String cgformPostCrazyForm(@PathVariable("name") String tableName, @RequestBody JSONObject jsonObject) throws Exception;
/**
* 【Online】 表单设计器专用:同步编辑
*/
@PutMapping(value = "/online/api/cgform/crazyForm/{name}")
String cgformPutCrazyForm(@PathVariable("name") String tableName, @RequestBody JSONObject jsonObject) throws Exception;
/**
* 通过online表名查询数据同时查询出子表的数据
*
* @param tableName online表名
* @param dataIds online数据ID
* @return
*/
@GetMapping(value = "/online/api/cgform/queryAllDataByTableName")
JSONObject cgformQueryAllDataByTableName(@RequestParam("tableName") String tableName, @RequestParam("dataIds") String dataIds);
/**
* online表单删除数据
*
* @param cgformCode Online表单code
* @param dataIds 数据ID可逗号分割
* @return
*/
@DeleteMapping("/online/api/cgform/cgformDeleteDataByCode")
String cgformDeleteDataByCode(@RequestParam("cgformCode") String cgformCode, @RequestParam("dataIds") String dataIds);
/**
* 【cgreport】通过 head code 获取 sql语句并执行该语句返回查询数据
*
* @param code 报表Code如果没传ID就通过code查
* @param forceKey
* @param dataList
* @return
*/
@GetMapping("/online/api/cgreportGetData")
Map<String, Object> cgreportGetData(@RequestParam("code") String code, @RequestParam("forceKey") String forceKey, @RequestParam("dataList") String dataList);
/**
* 【cgreport】对 cgreportGetData 的返回值做优化,封装 DictModel 集合
*
* @return
*/
@GetMapping("/online/api/cgreportGetDataPackage")
List<DictModel> cgreportGetDataPackage(@RequestParam("code") String code, @RequestParam("dictText") String dictText, @RequestParam("dictCode") String dictCode, @RequestParam("dataList") String dataList);
}

View File

@ -0,0 +1,17 @@
package org.jeecg.common.online.api.factory;
import feign.hystrix.FallbackFactory;
import org.jeecg.common.online.api.IOnlineBaseExtAPI;
import org.jeecg.common.online.api.fallback.OnlineBaseExtAPIFallback;
import org.springframework.stereotype.Component;
@Component
public class OnlineBaseExtAPIFallbackFactory implements FallbackFactory<IOnlineBaseExtAPI> {
@Override
public IOnlineBaseExtAPI create(Throwable throwable) {
OnlineBaseExtAPIFallback fallback = new OnlineBaseExtAPIFallback();
fallback.setCause(throwable);
return fallback;
}
}

View File

@ -0,0 +1,51 @@
package org.jeecg.common.online.api.fallback;
import com.alibaba.fastjson.JSONObject;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.online.api.IOnlineBaseExtAPI;
import org.jeecg.common.system.vo.DictModel;
import java.util.List;
import java.util.Map;
/**
* 进入fallback的方法 检查是否token未设置
*/
@Slf4j
public class OnlineBaseExtAPIFallback implements IOnlineBaseExtAPI {
@Setter
private Throwable cause;
@Override
public String cgformPostCrazyForm(String tableName, JSONObject jsonObject) {
return null;
}
@Override
public String cgformPutCrazyForm(String tableName, JSONObject jsonObject) {
return null;
}
@Override
public JSONObject cgformQueryAllDataByTableName(String tableName, String dataIds) {
return null;
}
@Override
public String cgformDeleteDataByCode(String cgformCode, String dataIds) {
return null;
}
@Override
public Map<String, Object> cgreportGetData(String code, String forceKey, String dataList) {
return null;
}
@Override
public List<DictModel> cgreportGetDataPackage(String code, String dictText, String dictCode, String dataList) {
return null;
}
}

View File

@ -9,10 +9,7 @@ import org.jeecg.common.system.api.factory.SysBaseAPIFallbackFactory;
import org.jeecg.common.system.vo.*; import org.jeecg.common.system.vo.*;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -109,6 +106,14 @@ public interface ISysBaseAPI extends CommonAPI {
@GetMapping("/sys/api/queryDictItemsByCode") @GetMapping("/sys/api/queryDictItemsByCode")
List<DictModel> queryDictItemsByCode(@RequestParam("code") String code); List<DictModel> queryDictItemsByCode(@RequestParam("code") String code);
/**
* 获取有效的数据字典项
* @param code
* @return
*/
@GetMapping("/sys/api/queryEnableDictItemsByCode")
public List<DictModel> queryEnableDictItemsByCode(@RequestParam("code") String code);
/** 11查询所有的父级字典按照create_time排序 */ /** 11查询所有的父级字典按照create_time排序 */
@GetMapping("/sys/api/queryAllDict") @GetMapping("/sys/api/queryAllDict")
List<DictModel> queryAllDict(); List<DictModel> queryAllDict();
@ -385,30 +390,31 @@ public interface ISysBaseAPI extends CommonAPI {
* @return * @return
*/ */
@GetMapping("/sys/api/queryUsersByUsernames") @GetMapping("/sys/api/queryUsersByUsernames")
List<JSONObject> queryUsersByUsernames(String usernames); List<JSONObject> queryUsersByUsernames(@RequestParam("usernames") String usernames);
/** /**
* 37根据多个用户ID(逗号分隔),查询返回多个用户信息 * 37根据多个用户ID(逗号分隔),查询返回多个用户信息
* @param ids * @param ids
* @return * @return
*/ */
@GetMapping("/sys/api/queryUsersByIds") @RequestMapping("/sys/api/queryUsersByIds")
List<JSONObject> queryUsersByIds(String ids); List<JSONObject> queryUsersByIds(@RequestParam("ids") String ids);
/** /**
* 38根据多个部门编码(逗号分隔),查询返回多个部门信息 * 38根据多个部门编码(逗号分隔),查询返回多个部门信息
* @param orgCodes * @param orgCodes
* @return * @return
*/ */
@GetMapping("/sys/api/queryDepartsByOrgcodes") @RequestMapping("/sys/api/queryDepartsByOrgcodes")
List<JSONObject> queryDepartsByOrgcodes(String orgCodes); List<JSONObject> queryDepartsByOrgcodes(@RequestParam("orgCodes") String orgCodes);
/** /**
* 39根据多个部门编码(逗号分隔),查询返回多个部门信息 * 39根据多个部门编码(逗号分隔),查询返回多个部门信息
* @param ids * @param ids
* @return * @return
*/ */
@GetMapping("/sys/api/queryDepartsByOrgIds") @GetMapping("/sys/api/queryDepartsByOrgIds")
List<JSONObject> queryDepartsByOrgIds(String ids); List<JSONObject> queryDepartsByOrgIds(@RequestParam("ids") String ids);
/** /**
* 40发送邮件消息 * 40发送邮件消息
@ -424,4 +430,81 @@ public interface ISysBaseAPI extends CommonAPI {
*/ */
@GetMapping("/sys/api/getDeptUserByOrgCode") @GetMapping("/sys/api/getDeptUserByOrgCode")
List<Map> getDeptUserByOrgCode(@RequestParam("orgCode")String orgCode); List<Map> getDeptUserByOrgCode(@RequestParam("orgCode")String orgCode);
/**
* 42 查询分类字典翻译
*/
@GetMapping("/sys/api/loadCategoryDictItem")
List<String> loadCategoryDictItem(@RequestParam("ids") String ids);
/**
* 43 根据字典code加载字典text
*
* @param dictCode 顺序tableName,text,code
* @param keys 要查询的key
* @return
*/
@GetMapping("/sys/api/loadDictItem")
List<String> loadDictItem(@RequestParam("dictCode") String dictCode, @RequestParam("keys") String keys);
/**
* 44 根据字典code查询字典项
*
* @param dictCode 顺序tableName,text,code
* @param dictCode 要查询的key
* @return
*/
@GetMapping("/sys/api/getDictItems")
List<DictModel> getDictItems(@RequestParam("dictCode") String dictCode);
/**
* 45 根据多个字典code查询多个字典项
*
* @param dictCodeList
* @return key = dictCode value=对应的字典项
*/
@RequestMapping("/sys/api/getManyDictItems")
Map<String, List<DictModel>> getManyDictItems(@RequestParam("dictCodeList") List<String> dictCodeList);
/**
* 46 【JSearchSelectTag下拉搜索组件专用接口】
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
*
* @param dictCode 字典code格式table,text,code
* @param keyword 过滤关键字
* @return
*/
@GetMapping("/sys/api/loadDictItemByKeyword")
List<DictModel> loadDictItemByKeyword(@RequestParam("dictCode") String dictCode, @RequestParam("keyword") String keyword, @RequestParam(value = "pageSize", required = false) Integer pageSize);
/**
* 47 根据多个部门id(逗号分隔),查询返回多个部门信息
* @param ids
* @return
*/
@GetMapping("/sys/api/queryDepartsByIds")
List<JSONObject> queryDepartsByIds(@RequestParam("ids") String ids);
/**
* 48 普通字典的翻译根据多个dictCode和多条数据多个以逗号分割
* @param dictCodes
* @param keys
* @return
*/
@Override
@GetMapping("/sys/api/translateManyDict")
Map<String, List<DictModel>> translateManyDict(@RequestParam("dictCodes") String dictCodes, @RequestParam("keys") String keys);
/**
* 49 字典表的 翻译,可批量
* @param table
* @param text
* @param code
* @param keys 多个用逗号分割
* @return
*/
@Override
@GetMapping("/sys/api/translateDictFromTableByKeys")
List<DictModel> translateDictFromTableByKeys(@RequestParam("table") String table, @RequestParam("text") String text, @RequestParam("code") String code, @RequestParam("keys") String keys);
} }

View File

@ -72,6 +72,11 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
return null; return null;
} }
@Override
public List<DictModel> queryEnableDictItemsByCode(String code) {
return null;
}
@Override @Override
public List<DictModel> queryAllDict() { public List<DictModel> queryAllDict() {
return null; return null;
@ -255,6 +260,22 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
public List<JSONObject> queryDepartsByOrgcodes(String orgCodes) { public List<JSONObject> queryDepartsByOrgcodes(String orgCodes) {
return null; return null;
} }
@Override
public List<JSONObject> queryDepartsByIds(String ids) {
return null;
}
@Override
public Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys) {
return null;
}
@Override
public List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys) {
return null;
}
@Override @Override
public void sendEmailMsg(String email,String title,String content) { public void sendEmailMsg(String email,String title,String content) {
@ -269,4 +290,29 @@ public class SysBaseAPIFallback implements ISysBaseAPI {
public List<JSONObject> queryDepartsByOrgIds(String ids) { public List<JSONObject> queryDepartsByOrgIds(String ids) {
return null; return null;
} }
@Override
public List<String> loadCategoryDictItem(String ids) {
return null;
}
@Override
public List<String> loadDictItem(String dictCode, String keys) {
return null;
}
@Override
public List<DictModel> getDictItems(String dictCode) {
return null;
}
@Override
public Map<String, List<DictModel>> getManyDictItems(List<String> dictCodeList) {
return null;
}
@Override
public List<DictModel> loadDictItemByKeyword(String dictCode, String keyword, Integer pageSize) {
return null;
}
} }

View File

@ -1,26 +1,47 @@
package org.jeecg.config; package org.jeecg.config;
import feign.Feign; import java.io.IOException;
import feign.Logger; import java.util.ArrayList;
import feign.RequestInterceptor; import java.util.Arrays;
import feign.codec.Encoder; import java.util.List;
import feign.form.spring.SpringFormEncoder; import java.util.SortedMap;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.PathMatcherUtil;
import org.jeecg.config.sign.interceptor.SignAuthConfiguration;
import org.jeecg.config.sign.util.HttpUtils;
import org.jeecg.config.sign.util.SignUtil;
import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.cloud.openfeign.support.SpringEncoder; import org.springframework.cloud.openfeign.support.SpringEncoder;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.Scope;
import org.springframework.http.MediaType;
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.alibaba.fastjson.support.springfox.SwaggerJsonSerializer;
import feign.Feign;
import feign.Logger;
import feign.RequestInterceptor;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import lombok.extern.slf4j.Slf4j;
@ConditionalOnClass(Feign.class) @ConditionalOnClass(Feign.class)
@AutoConfigureBefore(FeignAutoConfiguration.class) @AutoConfigureBefore(FeignAutoConfiguration.class)
@ -34,14 +55,39 @@ public class FeignConfig {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (null != attributes) { if (null != attributes) {
HttpServletRequest request = attributes.getRequest(); HttpServletRequest request = attributes.getRequest();
log.info("Feign request: {}", request.getRequestURI()); log.debug("Feign request: {}", request.getRequestURI());
// 将token信息放入header中 // 将token信息放入header中
String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN); String token = request.getHeader(CommonConstant.X_ACCESS_TOKEN);
if(token==null){ if(token==null || "".equals(token)){
token = request.getParameter("token"); token = request.getParameter("token");
} }
log.info("Feign request token: {}", token); log.debug("Feign request token: {}", token);
requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token); requestTemplate.header(CommonConstant.X_ACCESS_TOKEN, token);
//根据URL地址过滤请求 【字典表参数签名验证】
if (PathMatcherUtil.matches(Arrays.asList(SignAuthConfiguration.urlList),requestTemplate.path())) {
try {
log.info("============================ [begin] fegin api url ============================");
log.info(requestTemplate.path());
log.info(requestTemplate.method());
String queryLine = requestTemplate.queryLine();
if(queryLine!=null && queryLine.startsWith("?")){
queryLine = queryLine.substring(1);
}
log.info(queryLine);
if(requestTemplate.body()!=null){
log.info(new String(requestTemplate.body()));
}
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestTemplate.path(),queryLine,requestTemplate.body(),requestTemplate.method());
String sign = SignUtil.getParamsSign(allParams);
log.info(" Feign request params sign: {}",sign);
log.info("============================ [end] fegin api url ============================");
requestTemplate.header(CommonConstant.X_SIGN, sign);
requestTemplate.header(CommonConstant.X_TIMESTAMP, DateUtils.getCurrentTimestamp().toString());
} catch (IOException e) {
e.printStackTrace();
}
}
} }
}; };
} }
@ -72,4 +118,42 @@ public class FeignConfig {
public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) { public Encoder multipartFormEncoder(ObjectFactory<HttpMessageConverters> messageConverters) {
return new SpringFormEncoder(new SpringEncoder(messageConverters)); return new SpringFormEncoder(new SpringEncoder(messageConverters));
} }
// update-begin--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
@Bean
public Encoder feignEncoder() {
return new SpringEncoder(feignHttpMessageConverter());
}
@Bean
public Decoder feignDecoder() {
return new SpringDecoder(feignHttpMessageConverter());
}
/**
* 设置解码器为fastjson
*
* @return
*/
private ObjectFactory<HttpMessageConverters> feignHttpMessageConverter() {
final HttpMessageConverters httpMessageConverters = new HttpMessageConverters(this.getFastJsonConverter());
return () -> httpMessageConverters;
}
private FastJsonHttpMessageConverter getFastJsonConverter() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<>();
MediaType mediaTypeJson = MediaType.valueOf(MediaType.APPLICATION_JSON_VALUE);
supportedMediaTypes.add(mediaTypeJson);
converter.setSupportedMediaTypes(supportedMediaTypes);
FastJsonConfig config = new FastJsonConfig();
config.getSerializeConfig().put(JSON.class, new SwaggerJsonSerializer());
config.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect);
converter.setFastJsonConfig(config);
return converter;
}
// update-end--Author:sunjianlei Date:20210604 for 给 Feign 添加 FastJson 的解析支持 ----------
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>jeecg-boot-base-api</artifactId> <artifactId>jeecg-boot-base-api</artifactId>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<version>2.4.5</version> <version>2.4.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -0,0 +1,55 @@
package org.jeecg.common.bpm.api;
import java.util.List;
import java.util.Map;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.vo.DictModel;
import com.alibaba.fastjson.JSONObject;
/**
* 流程接口
*
* @author scott
*/
public interface IBpmBaseExtAPI {
/**
* 23. 流程提交接口online自定义开发
* @param flowCode 流程业务关联 例如joa_leave_01
* @param id 表单业务数据data id
* @param formUrl 流程审批时附件页面默认展示的PC端表单组件地址
* @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址)
* @param username 流程发起人账号
* @param jsonData Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
Result<String> startMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,String username, String jsonData) throws Exception;
/**
* 24. 流程提交接口(自定义表单设计器)
* @param flowCode 流程业务关联 例如joa_leave_01
* @param id 表单业务数据data id
* @param formUrl 流程审批时附件页面默认展示的PC端表单组件地址
* @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址)
* @param username 流程发起人账号
* @param jsonData Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
Result<String> startDesFormMutilProcess(String flowCode, String id, String formUrl, String formUrlMobile,String username,String jsonData) throws Exception;
/**
* 25. 保存流程草稿箱接口自定义开发表单、online表单
* @param flowCode 流程业务关联 例如joa_leave_01
* @param id 表单业务数据data id
* @param formUrl 流程审批时附件页面默认展示的PC端表单组件地址 【非必填】
* @param formUrlMobile 流程审批时附件页面默认展示的移动端表单组件(地址) 【非必填】
* @param username 流程发起人账号
* @param jsonData Json串额外扩展的流程变量值 【非必填】
* @return
* @throws Exception
*/
Result<String> saveMutilProcessDraft(String flowCode, String id, String formUrl, String formUrlMobile,String username,String jsonData) throws Exception;
}

View File

@ -0,0 +1,61 @@
package org.jeecg.common.online.api;
import com.alibaba.fastjson.JSONObject;
import org.jeecg.common.system.vo.DictModel;
import java.util.List;
import java.util.Map;
/**
* 表单设计器【Online】翻译API接口
*
* @author sunjianlei
*/
public interface IOnlineBaseExtAPI {
/**
* 【Online】 表单设计器专用:同步新增
*/
String cgformPostCrazyForm(String tableName, JSONObject jsonObject) throws Exception;
/**
* 【Online】 表单设计器专用:同步编辑
*/
String cgformPutCrazyForm(String tableName, JSONObject jsonObject) throws Exception;
/**
* online表单删除数据
*
* @param cgformCode Online表单code
* @param dataIds 数据ID可逗号分割
* @return
*/
String cgformDeleteDataByCode(String cgformCode, String dataIds);
/**
* 通过online表名查询数据同时查询出子表的数据
*
* @param tableName online表名
* @param dataIds online数据ID
* @return
*/
JSONObject cgformQueryAllDataByTableName(String tableName, String dataIds);
/**
* 对 cgreportGetData 的返回值做优化,封装 DictModel 集合
*
* @return
*/
List<DictModel> cgreportGetDataPackage(String code, String dictText, String dictCode, String dataList);
/**
* 【cgreport】通过 head code 获取 sql语句并执行该语句返回查询数据
*
* @param code 报表Code如果没传ID就通过code查
* @param forceKey
* @param dataList
* @return
*/
Map<String, Object> cgreportGetData(String code, String forceKey, String dataList);
}

View File

@ -1,14 +1,11 @@
package org.jeecg.common.system.api; package org.jeecg.common.system.api;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.jeecg.common.api.CommonAPI; import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.dto.OnlineAuthDTO; import org.jeecg.common.api.dto.OnlineAuthDTO;
import org.jeecg.common.api.dto.message.*; import org.jeecg.common.api.dto.message.*;
import org.jeecg.common.system.vo.*; import org.jeecg.common.system.vo.*;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -289,4 +286,45 @@ public interface ISysBaseAPI extends CommonAPI {
* @param orgCode * @param orgCode
*/ */
List<Map> getDeptUserByOrgCode(String orgCode); List<Map> getDeptUserByOrgCode(String orgCode);
/**
* 查询分类字典翻译
*/
List<String> loadCategoryDictItem(String ids);
/**
* 根据字典code加载字典text
*
* @param dictCode 顺序tableName,text,code
* @param keys 要查询的key
* @return
*/
List<String> loadDictItem(String dictCode, String keys);
/**
* 根据字典code查询字典项
*
* @param dictCode 顺序tableName,text,code
* @param dictCode 要查询的key
* @return
*/
List<DictModel> getDictItems(String dictCode);
/**
* 根据多个字典code查询多个字典项
* @param dictCodeList
* @return key = dictCode value=对应的字典项
*/
Map<String, List<DictModel>> getManyDictItems(List<String> dictCodeList);
/**
* 【JSearchSelectTag下拉搜索组件专用接口】
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
*
* @param dictCode 字典code格式table,text,code
* @param keyword 过滤关键字
* @return
*/
List<DictModel> loadDictItemByKeyword(String dictCode, String keyword, Integer pageSize);
} }

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>jeecg-boot-base</artifactId> <artifactId>jeecg-boot-base</artifactId>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<version>2.4.5</version> <version>2.4.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base</artifactId> <artifactId>jeecg-boot-base</artifactId>
<version>2.4.5</version> <version>2.4.6</version>
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
@ -14,7 +14,7 @@
<repository> <repository>
<id>aliyun</id> <id>aliyun</id>
<name>aliyun Repository</name> <name>aliyun Repository</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url> <url>https://maven.aliyun.com/repository/public</url>
<snapshots> <snapshots>
<enabled>false</enabled> <enabled>false</enabled>
</snapshots> </snapshots>
@ -22,7 +22,7 @@
<repository> <repository>
<id>jeecg</id> <id>jeecg</id>
<name>jeecg Repository</name> <name>jeecg Repository</name>
<url>http://maven.jeecg.org/nexus/content/repositories/jeecg</url> <url>https://maven.jeecg.org/nexus/content/repositories/jeecg</url>
<snapshots> <snapshots>
<enabled>false</enabled> <enabled>false</enabled>
</snapshots> </snapshots>
@ -114,9 +114,10 @@
<dependency> <dependency>
<groupId>org.jeecgframework.boot</groupId> <groupId>org.jeecgframework.boot</groupId>
<artifactId>hibernate-re</artifactId> <artifactId>hibernate-re</artifactId>
<version>2.4.5-RC</version> <version>2.4.6-beta1</version>
</dependency> </dependency>
<!-- 数据库驱动 -->
<!--mysql--> <!--mysql-->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>

View File

@ -3,6 +3,7 @@ package org.jeecg.common.api;
import org.jeecg.common.system.vo.*; import org.jeecg.common.system.vo.*;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
public interface CommonAPI { public interface CommonAPI {
@ -85,6 +86,13 @@ public interface CommonAPI {
*/ */
public List<DictModel> queryDictItemsByCode(String code); public List<DictModel> queryDictItemsByCode(String code);
/**
* 获取有效的数据字典项
* @param code
* @return
*/
public List<DictModel> queryEnableDictItemsByCode(String code);
/** /**
* 13获取表数据字典 * 13获取表数据字典
* @param table * @param table
@ -94,4 +102,22 @@ public interface CommonAPI {
*/ */
List<DictModel> queryTableDictItemsByCode(String table, String text, String code); List<DictModel> queryTableDictItemsByCode(String table, String text, String code);
/**
* 14 普通字典的翻译根据多个dictCode和多条数据多个以逗号分割
* @param dictCodes 例如user_status,sex
* @param keys 例如1,2,0
* @return
*/
Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys);
/**
* 15 字典表的 翻译,可批量
* @param table
* @param text
* @param code
* @param keys 多个用逗号分割
* @return
*/
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys);
} }

View File

@ -1,5 +1,6 @@
package org.jeecg.common.aspect; package org.jeecg.common.aspect;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
@ -14,17 +15,17 @@ import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.api.vo.Result; import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.Dict; import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.constant.CommonConstant; import org.jeecg.common.constant.CommonConstant;
import org.jeecg.modules.base.service.BaseCommonService; import org.jeecg.common.system.vo.DictModel;
import org.jeecg.common.util.oConvertUtils; import org.jeecg.common.util.oConvertUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.*;
import java.util.Date; import java.util.stream.Collectors;
import java.util.List;
/** /**
* @Description: 字典aop类 * @Description: 字典aop类
@ -56,7 +57,7 @@ public class DictAspect {
long start=System.currentTimeMillis(); long start=System.currentTimeMillis();
this.parseDictText(result); this.parseDictText(result);
long end=System.currentTimeMillis(); long end=System.currentTimeMillis();
log.debug("解析注入JSON数据 耗时"+(end-start)+"ms"); log.debug("注入字典到JSON数据 耗时"+(end-start)+"ms");
return result; return result;
} }
@ -86,6 +87,12 @@ public class DictAspect {
if (result instanceof Result) { if (result instanceof Result) {
if (((Result) result).getResult() instanceof IPage) { if (((Result) result).getResult() instanceof IPage) {
List<JSONObject> items = new ArrayList<>(); List<JSONObject> items = new ArrayList<>();
//step.1 筛选出加了 Dict 注解的字段列表
List<Field> dictFieldList = new ArrayList<>();
// 字典数据列表, key = 字典codevalue=数据列表
Map<String, List<String>> dataListMap = new HashMap<>();
for (Object record : ((IPage) ((Result) result).getResult()).getRecords()) { for (Object record : ((IPage) ((Result) result).getResult()).getRecords()) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
String json="{}"; String json="{}";
@ -98,20 +105,28 @@ public class DictAspect {
JSONObject item = JSONObject.parseObject(json); JSONObject item = JSONObject.parseObject(json);
//update-begin--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------ //update-begin--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
//for (Field field : record.getClass().getDeclaredFields()) { //for (Field field : record.getClass().getDeclaredFields()) {
// 遍历所有字段把字典Code取出来放到 map 里
for (Field field : oConvertUtils.getAllFields(record)) { for (Field field : oConvertUtils.getAllFields(record)) {
String value = item.getString(field.getName());
if (oConvertUtils.isEmpty(value)) {
continue;
}
//update-end--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------ //update-end--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) { if (field.getAnnotation(Dict.class) != null) {
if (!dictFieldList.contains(field)) {
dictFieldList.add(field);
}
String code = field.getAnnotation(Dict.class).dicCode(); String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText(); String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable(); String table = field.getAnnotation(Dict.class).dictTable();
String key = String.valueOf(item.get(field.getName()));
//翻译字典值对应的txt List<String> dataList;
String textValue = translateDictValue(code, text, table, key); String dictCode = code;
if (!StringUtils.isEmpty(table)) {
log.debug(" 字典Val : "+ textValue); dictCode = String.format("%s,%s,%s", table, text, code);
log.debug(" __翻译字典字段__ "+field.getName() + CommonConstant.DICT_TEXT_SUFFIX+" "+ textValue); }
item.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue); dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
} }
//date类型默认转换string格式化日期 //date类型默认转换string格式化日期
if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){ if (field.getType().getName().equals("java.util.Date")&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
@ -121,12 +136,194 @@ public class DictAspect {
} }
items.add(item); items.add(item);
} }
//step.2 调用翻译方法,一次性翻译
Map<String, List<DictModel>> translText = this.translateAllDict(dataListMap);
//step.3 将翻译结果填充到返回结果里
for (JSONObject record : items) {
for (Field field : dictFieldList) {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
String fieldDictCode = code;
if (!StringUtils.isEmpty(table)) {
fieldDictCode = String.format("%s,%s,%s", table, text, code);
}
String value = record.getString(field.getName());
if (oConvertUtils.isNotEmpty(value)) {
List<DictModel> dictModels = translText.get(fieldDictCode);
if(dictModels==null || dictModels.size()==0){
continue;
}
String textValue = this.translDictText(dictModels, value);
log.debug(" 字典Val : " + textValue);
log.debug(" __翻译字典字段__ " + field.getName() + CommonConstant.DICT_TEXT_SUFFIX + " " + textValue);
// TODO-sun 测试输出,待删
log.debug(" ---- dictCode: " + fieldDictCode);
log.debug(" ---- value: " + value);
log.debug(" ----- text: " + textValue);
log.debug(" ---- dictModels: " + JSON.toJSONString(dictModels));
record.put(field.getName() + CommonConstant.DICT_TEXT_SUFFIX, textValue);
}
}
}
((IPage) ((Result) result).getResult()).setRecords(items); ((IPage) ((Result) result).getResult()).setRecords(items);
} }
} }
} }
/**
* list 去重添加
*/
private void listAddAllDeduplicate(List<String> dataList, List<String> addList) {
// 筛选出dataList中没有的数据
List<String> filterList = addList.stream().filter(i -> !dataList.contains(i)).collect(Collectors.toList());
dataList.addAll(filterList);
}
/**
* 一次性把所有的字典都翻译了
* 1. 所有的普通数据字典的所有数据只执行一次SQL
* 2. 表字典相同的所有数据只执行一次SQL
* @param dataListMap
* @return
*/
private Map<String, List<DictModel>> translateAllDict(Map<String, List<String>> dataListMap) {
// 翻译后的字典文本key=dictCode
Map<String, List<DictModel>> translText = new HashMap<>();
// 需要翻译的数据有些可以从redis缓存中获取就不走数据库查询
List<String> needTranslData = new ArrayList<>();
//step.1 先通过redis中获取缓存字典数据
for (String dictCode : dataListMap.keySet()) {
List<String> dataList = dataListMap.get(dictCode);
if (dataList.size() == 0) {
continue;
}
// 表字典需要翻译的数据
List<String> needTranslDataTable = new ArrayList<>();
for (String s : dataList) {
String data = s.trim();
if (data.length() == 0) {
continue; //跳过循环
}
if (dictCode.contains(",")) {
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, data);
if (redisTemplate.hasKey(keyString)) {
try {
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.add(new DictModel(data, text));
} catch (Exception e) {
log.warn(e.getMessage());
}
} else if (!needTranslDataTable.contains(data)) {
// 去重添加
needTranslDataTable.add(data);
}
} else {
String keyString = String.format("sys:cache:dict::%s:%s", dictCode, data);
if (redisTemplate.hasKey(keyString)) {
try {
String text = oConvertUtils.getString(redisTemplate.opsForValue().get(keyString));
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.add(new DictModel(data, text));
} catch (Exception e) {
log.warn(e.getMessage());
}
} else if (!needTranslData.contains(data)) {
// 去重添加
needTranslData.add(data);
}
}
}
//step.2 调用数据库翻译表字典
if (needTranslDataTable.size() > 0) {
String[] arr = dictCode.split(",");
String table = arr[0], text = arr[1], code = arr[2];
String values = String.join(",", needTranslDataTable);
log.info("translateDictFromTableByKeys.dictCode:" + dictCode);
log.info("translateDictFromTableByKeys.values:" + values);
List<DictModel> texts = commonAPI.translateDictFromTableByKeys(table, text, code, values);
log.info("translateDictFromTableByKeys.result:" + texts);
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.addAll(texts);
// 做 redis 缓存
for (DictModel dict : texts) {
String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
try {
redisTemplate.opsForValue().set(redisKey, dict.getText());
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
}
}
}
//step.3 调用数据库进行翻译普通字典
if (needTranslData.size() > 0) {
List<String> dictCodeList = Arrays.asList(dataListMap.keySet().toArray(new String[]{}));
// 将不包含逗号的字典code筛选出来因为带逗号的是表字典而不是普通的数据字典
List<String> filterDictCodes = dictCodeList.stream().filter(key -> !key.contains(",")).collect(Collectors.toList());
String dictCodes = String.join(",", filterDictCodes);
String values = String.join(",", needTranslData);
log.info("translateManyDict.dictCodes:" + dictCodes);
log.info("translateManyDict.values:" + values);
Map<String, List<DictModel>> manyDict = commonAPI.translateManyDict(dictCodes, values);
log.info("translateManyDict.result:" + manyDict);
for (String dictCode : manyDict.keySet()) {
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
List<DictModel> newList = manyDict.get(dictCode);
list.addAll(newList);
// 做 redis 缓存
for (DictModel dict : newList) {
String redisKey = String.format("sys:cache:dict::%s:%s", dictCode, dict.getValue());
try {
redisTemplate.opsForValue().set(redisKey, dict.getText());
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
}
}
}
return translText;
}
/**
* 字典值替换文本
*
* @param dictModels
* @param values
* @return
*/
private String translDictText(List<DictModel> dictModels, String values) {
List<String> result = new ArrayList<>();
// 允许多个逗号分隔,允许传数组对象
String[] splitVal = values.split(",");
for (String val : splitVal) {
String dictText = val;
for (DictModel dict : dictModels) {
if (val.equals(dict.getValue())) {
dictText = dict.getText();
break;
}
}
result.add(dictText);
}
return String.join(",", result);
}
/** /**
* 翻译字典文本 * 翻译字典文本
* @param code * @param code
@ -135,6 +332,7 @@ public class DictAspect {
* @param key * @param key
* @return * @return
*/ */
@Deprecated
private String translateDictValue(String code, String text, String table, String key) { private String translateDictValue(String code, String text, String table, String key) {
if(oConvertUtils.isEmpty(key)) { if(oConvertUtils.isEmpty(key)) {
return null; return null;

View File

@ -290,6 +290,8 @@ public interface CommonConstant {
public final static String X_ACCESS_TOKEN = "X-Access-Token"; public final static String X_ACCESS_TOKEN = "X-Access-Token";
public final static String X_SIGN = "X-Sign";
public final static String X_TIMESTAMP = "X-TIMESTAMP";
/** /**
* 多租户 请求头 * 多租户 请求头

View File

@ -11,12 +11,14 @@ public interface DataBaseConstant {
public static final String DB_TYPE_SQLSERVER = "SQLSERVER"; public static final String DB_TYPE_SQLSERVER = "SQLSERVER";
public static final String DB_TYPE_MARIADB = "MARIADB"; public static final String DB_TYPE_MARIADB = "MARIADB";
// 数据库类型,对应 database_type 字典 // // 数据库类型,对应 database_type 字典
public static final String DB_TYPE_MYSQL_NUM = "1"; // public static final String DB_TYPE_MYSQL_NUM = "1";
public static final String DB_TYPE_ORACLE_NUM = "2"; // public static final String DB_TYPE_MYSQL7_NUM = "6";
public static final String DB_TYPE_SQLSERVER_NUM = "3"; // public static final String DB_TYPE_ORACLE_NUM = "2";
public static final String DB_TYPE_POSTGRESQL_NUM = "4"; // public static final String DB_TYPE_SQLSERVER_NUM = "3";
public static final String DB_TYPE_MARIADB_NUM = "5"; // public static final String DB_TYPE_POSTGRESQL_NUM = "4";
// public static final String DB_TYPE_MARIADB_NUM = "5";
//*********系统上下文变量**************************************** //*********系统上下文变量****************************************
/** /**
* 数据-所属机构编码 * 数据-所属机构编码

View File

@ -29,6 +29,7 @@ public interface ServiceNameConstants {
* 系统管理 admin * 系统管理 admin
*/ */
String SYSTEM_SERVICE = "jeecg-system"; String SYSTEM_SERVICE = "jeecg-system";
String SYSTEM_ONLINE = "jeecg-online";
/** /**
* gateway通过header传递根路径 basePath * gateway通过header传递根路径 basePath

View File

@ -0,0 +1,82 @@
package org.jeecg.common.constant.enums;
/**
* 首页自定义
* 通过角色编码与首页组件路径配置
*/
public enum RoleIndexConfigEnum {
/**
* 管理员
*/
ADMIN("admin1", "dashboard/Analysis2"),
/**
* 测试
*/
TEST("test", "dashboard/Analysis"),
/**
* hr
*/
HR("hr", "dashboard/Analysis1");
/**
* 角色编码
*/
String roleCode;
/**
* 路由index
*/
String componentUrl;
/**
* 构造器
*
* @param roleCode 角色编码
* @param componentUrl 首页组件路径(规则跟菜单配置一样)
*/
RoleIndexConfigEnum(String roleCode, String componentUrl) {
this.roleCode = roleCode;
this.componentUrl = componentUrl;
}
/**
* 根据code找枚举
* @param roleCode 角色编码
* @return
*/
public static RoleIndexConfigEnum getEnumByCode(String roleCode) {
for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
if (e.roleCode.equals(roleCode)) {
return e;
}
}
return null;
}
/**
* 根据code找index
* @param roleCode 角色编码
* @return
*/
public static String getIndexByCode(String roleCode) {
for (RoleIndexConfigEnum e : RoleIndexConfigEnum.values()) {
if (e.roleCode.equals(roleCode)) {
return e.componentUrl;
}
}
return null;
}
public String getRoleCode() {
return roleCode;
}
public void setRoleCode(String roleCode) {
this.roleCode = roleCode;
}
public String getComponentUrl() {
return componentUrl;
}
public void setComponentUrl(String componentUrl) {
this.componentUrl = componentUrl;
}
}

View File

@ -26,6 +26,8 @@ public class JeecgElasticsearchTemplate {
/** es服务地址 */ /** es服务地址 */
private String baseUrl; private String baseUrl;
private final String FORMAT_JSON = "format=json"; private final String FORMAT_JSON = "format=json";
/** Elasticsearch 的版本号 */
private String version = null;
// ElasticSearch 最大可返回条目数 // ElasticSearch 最大可返回条目数
public static final int ES_MAX_SIZE = 10000; public static final int ES_MAX_SIZE = 10000;
@ -37,15 +39,31 @@ public class JeecgElasticsearchTemplate {
// 验证配置的ES地址是否有效 // 验证配置的ES地址是否有效
if (checkEnabled) { if (checkEnabled) {
try { try {
RestUtil.get(this.getBaseUrl().toString()); this.getElasticsearchVersion();
log.info("ElasticSearch 服务连接成功"); log.info("ElasticSearch 服务连接成功");
log.info("ElasticSearch version: " + this.version);
} catch (Exception e) { } catch (Exception e) {
this.version = "";
log.warn("ElasticSearch 服务连接失败原因配置未通过。可能是BaseURL未配置或配置有误也可能是Elasticsearch服务未启动。接下来将会拒绝执行任何方法"); log.warn("ElasticSearch 服务连接失败原因配置未通过。可能是BaseURL未配置或配置有误也可能是Elasticsearch服务未启动。接下来将会拒绝执行任何方法");
} }
} }
} }
} }
/**
* 获取 Elasticsearch 的版本号信息失败返回null
*/
private void getElasticsearchVersion() {
if (this.version == null) {
String url = this.getBaseUrl().toString();
JSONObject result = RestUtil.get(url);
if (result != null) {
JSONObject v = result.getJSONObject("version");
this.version = v.getString("number");
}
}
}
public StringBuilder getBaseUrl(String indexName, String typeName) { public StringBuilder getBaseUrl(String indexName, String typeName) {
typeName = typeName.trim().toLowerCase(); typeName = typeName.trim().toLowerCase();
return this.getBaseUrl(indexName).append("/").append(typeName); return this.getBaseUrl(indexName).append("/").append(typeName);
@ -185,6 +203,11 @@ public class JeecgElasticsearchTemplate {
*/ */
public JSONObject getIndexMapping(String indexName, String typeName) { public JSONObject getIndexMapping(String indexName, String typeName) {
String url = this.getBaseUrl(indexName, typeName).append("/_mapping?").append(FORMAT_JSON).toString(); String url = this.getBaseUrl(indexName, typeName).append("/_mapping?").append(FORMAT_JSON).toString();
// 针对 es 7.x 版本做兼容
this.getElasticsearchVersion();
if (oConvertUtils.isNotEmpty(this.version) && this.version.startsWith("7")) {
url += "&include_type_name=true";
}
log.info("getIndexMapping-url:" + url); log.info("getIndexMapping-url:" + url);
/* /*
* 参考返回JSON结构 * 参考返回JSON结构

View File

@ -7,7 +7,13 @@ public class QueryCondition implements Serializable {
private static final long serialVersionUID = 4740166316629191651L; private static final long serialVersionUID = 4740166316629191651L;
private String field; private String field;
/** 组件的类型例如input、select、radio */
private String type; private String type;
/**
* 对应的数据库字段的类型
* 支持int、bigDecimal、short、long、float、double、boolean
*/
private String dbType;
private String rule; private String rule;
private String val; private String val;
@ -27,6 +33,14 @@ public class QueryCondition implements Serializable {
this.type = type; this.type = type;
} }
public String getDbType() {
return dbType;
}
public void setDbType(String dbType) {
this.dbType = dbType;
}
public String getRule() { public String getRule() {
return rule; return rule;
} }
@ -49,7 +63,7 @@ public class QueryCondition implements Serializable {
if(field == null || "".equals(field)){ if(field == null || "".equals(field)){
return ""; return "";
} }
sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.val); sb.append(this.field).append(" ").append(this.rule).append(" ").append(this.type).append(" ").append(this.dbType).append(" ").append(this.val);
return sb.toString(); return sb.toString();
} }
} }

View File

@ -1,21 +1,5 @@
package org.jeecg.common.system.query; package org.jeecg.common.system.query;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.beanutils.PropertyUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.util.NumberUtils;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@ -28,6 +12,25 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.commons.beanutils.PropertyUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.system.util.JeecgDataAutorUtils;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.SysPermissionDataRuleModel;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.util.NumberUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class QueryGenerator { public class QueryGenerator {
public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN"; public static final String SQL_RULES_COLUMN = "SQL_RULES_COLUMN";
@ -238,12 +241,12 @@ public class QueryGenerator {
//queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column)); //queryWrapper.orderByAsc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column); String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(","); String[] columnArray = columnStr.split(",");
queryWrapper.orderByAsc(columnArray); queryWrapper.orderByAsc(Arrays.asList(columnArray));
} else { } else {
//queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column)); //queryWrapper.orderByDesc(oConvertUtils.camelToUnderline(column));
String columnStr = oConvertUtils.camelToUnderline(column); String columnStr = oConvertUtils.camelToUnderline(column);
String[] columnArray = columnStr.split(","); String[] columnArray = columnStr.split(",");
queryWrapper.orderByDesc(columnArray); queryWrapper.orderByDesc(Arrays.asList(columnArray));
} }
//update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正------- //update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
} }
@ -284,6 +287,39 @@ public class QueryGenerator {
}else if("datetime".equals(rule.getType())){ }else if("datetime".equals(rule.getType())){
queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get()); queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get());
} }
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
String dbType = rule.getDbType();
if (oConvertUtils.isNotEmpty(dbType)) {
try {
String valueStr = String.valueOf(queryValue);
switch (dbType.toLowerCase().trim()) {
case "int":
queryValue = Integer.parseInt(valueStr);
break;
case "bigdecimal":
queryValue = new BigDecimal(valueStr);
break;
case "short":
queryValue = Short.parseShort(valueStr);
break;
case "long":
queryValue = Long.parseLong(valueStr);
break;
case "float":
queryValue = Float.parseFloat(valueStr);
break;
case "double":
queryValue = Double.parseDouble(valueStr);
break;
case "boolean":
queryValue = Boolean.parseBoolean(valueStr);
break;
}
} catch (Exception e) {
log.error("高级查询值转换失败:", e);
}
}
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue); addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue);
//update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错 //update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
@ -313,13 +349,15 @@ public class QueryGenerator {
*/ */
private static QueryRuleEnum convert2Rule(Object value) { private static QueryRuleEnum convert2Rule(Object value) {
// 避免空数据 // 避免空数据
// update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
if (value == null) { if (value == null) {
return null; return QueryRuleEnum.EQ;
} }
String val = (value + "").toString().trim(); String val = (value + "").toString().trim();
if (val.length() == 0) { if (val.length() == 0) {
return null; return QueryRuleEnum.EQ;
} }
// update-end-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
QueryRuleEnum rule =null; QueryRuleEnum rule =null;
//update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284------------------- //update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
@ -820,20 +858,25 @@ public class QueryGenerator {
} }
private static String getInConditionValue(Object value,boolean isString) { private static String getInConditionValue(Object value,boolean isString) {
//update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
String[] temp = value.toString().split(",");
if(temp.length==0){
return "('')";
}
if(isString) { if(isString) {
String temp[] = value.toString().split(","); List<String> res = new ArrayList<>();
String res="";
for (String string : temp) { for (String string : temp) {
if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){ if(DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())){
res+=",N'"+string+"'"; res.add("N'"+string+"'");
}else{ }else{
res+=",'"+string+"'"; res.add("'"+string+"'");
} }
} }
return "("+res.substring(1)+")"; return "("+String.join("," ,res)+")";
}else { }else {
return "("+value.toString()+")"; return "("+value.toString()+")";
} }
//update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
} }
private static String getLikeConditionValue(Object value) { private static String getLikeConditionValue(Object value) {
@ -1003,8 +1046,6 @@ public class QueryGenerator {
/** 当前系统数据库类型 */
private static String DB_TYPE;
/** /**
* 获取系统数据库类型 * 获取系统数据库类型
*/ */

View File

@ -0,0 +1,18 @@
package org.jeecg.common.system.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 查询多个字典时用到
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class DictModelMany extends DictModel {
/**
* 字典code根据多个字段code查询时才用到用于区分不同的字典选项
*/
private String dictCode;
}

View File

@ -36,10 +36,12 @@ public class DynamicDataSourceModel {
* 数据源地址 * 数据源地址
*/ */
private java.lang.String dbUrl; private java.lang.String dbUrl;
/**
* 数据库名称 // /**
*/ // * 数据库名称
private java.lang.String dbName; // */
// private java.lang.String dbName;
/** /**
* 用户名 * 用户名
*/ */

View File

@ -1,27 +1,27 @@
package org.jeecg.common.util; package org.jeecg.common.util;
import cn.hutool.core.util.StrUtil; import java.io.*;
import cn.hutool.extra.pinyin.PinyinUtil;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.sql.DataSource;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.util.filter.FileTypeFilter;
import org.jeecg.common.util.oss.OssBootUtil;
import org.jeecgframework.poi.util.PoiPublicUtil;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
public class CommonUtils { public class CommonUtils {
@ -116,6 +116,9 @@ public class CommonUtils {
*/ */
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){ public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try { try {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
FileTypeFilter.fileTypeFilter(mf);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String fileName = null; String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator ); File file = new File(uploadpath + File.separator + bizPath + File.separator );
if (!file.exists()) { if (!file.exists()) {
@ -143,6 +146,8 @@ public class CommonUtils {
return dbpath; return dbpath;
} catch (IOException e) { } catch (IOException e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
}catch (Exception e) {
log.error(e.getMessage(), e);
} }
return ""; return "";
} }
@ -163,6 +168,13 @@ public class CommonUtils {
/** 当前系统数据库类型 */ /** 当前系统数据库类型 */
private static String DB_TYPE = ""; private static String DB_TYPE = "";
private static DbType dbTypeEnum = null;
/**
* 全局获取平台数据库类型(作废了)
* @return
*/
@Deprecated
public static String getDatabaseType() { public static String getDatabaseType() {
if(oConvertUtils.isNotEmpty(DB_TYPE)){ if(oConvertUtils.isNotEmpty(DB_TYPE)){
return DB_TYPE; return DB_TYPE;
@ -177,6 +189,24 @@ public class CommonUtils {
} }
} }
/**
* 全局获取平台数据库类型对应mybaisPlus枚举
* @return
*/
public static DbType getDatabaseTypeEnum() {
if (oConvertUtils.isNotEmpty(dbTypeEnum)) {
return dbTypeEnum;
}
try {
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
return dbTypeEnum;
} catch (SQLException e) {
log.warn(e.getMessage(), e);
return null;
}
}
/** /**
* 获取数据库类型 * 获取数据库类型
* @param dataSource * @param dataSource

View File

@ -291,7 +291,7 @@ public class DateUtils extends PropertyEditorSupport {
Date dt = new Date(); Date dt = new Date();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String nowTime = df.format(dt); String nowTime = df.format(dt);
Timestamp buydate = Timestamp.valueOf(nowTime); java.sql.Timestamp buydate = java.sql.Timestamp.valueOf(nowTime);
return buydate; return buydate;
} }

Some files were not shown because too many files have changed in this diff Show More