JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!!

pull/3142/head
zhangdaiscott 2021-10-27 10:26:33 +08:00
parent 75be8dd5b1
commit 9760185bf6
31 changed files with 449 additions and 568 deletions

View File

@ -7,7 +7,7 @@
JEECG BOOT 低代码开发平台(前后端分离版本)
===============
当前最新版本: 2.4.6发布日期2021-08-16
当前最新版本: 3.0发布日期2021-11-01
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
@ -130,10 +130,24 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
- 依赖管理Maven
- 数据库MySQL5.7+ & Oracle 11g & Sqlserver2017
- 缓存Redis
- 数据库脚本MySQL5.7+ & Oracle 11g & Sqlserver2017默认只提供这三个库脚本其他库需要自己转
| 数据库 | 支持 |
| --- | --- |
| MySQL | √ |
| Oracle11g | √ |
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| DB2、Informix | √ |
| MariaDB | √ |
| SQLite、Hsqldb、Derby、H2 | √ |
| 达梦、人大金仓、神通 | √ |
| 华为高斯、虚谷、瀚高数据库 | √ |
| 阿里云PolarDB、PPAS、HerdDB | √ |
| Hive、HBase、CouchBase | √ |
#### 后端
- 基础框架Spring Boot 2.3.5.RELEASE

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "vue-antd-jeecg",
"version": "2.4.6",
"version": "3.0.0",
"private": true,
"scripts": {
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
@ -11,7 +11,7 @@
},
"dependencies": {
"ant-design-vue": "^1.7.2",
"@jeecg/antd-online-mini": "2.4.6-beta5",
"@jeecg/antd-online-mini": "3.0.0-beta",
"@antv/data-set": "^0.11.4",
"viser-vue": "^2.4.8",
"axios": "^0.18.0",

View File

@ -48,7 +48,6 @@
props:{
disabled: Boolean,
value: [String, Number],
dict: String,
dictOptions: Array,
async: Boolean,
placeholder:{
@ -56,6 +55,11 @@
default:"请选择",
required:false
},
dict:{
type: String,
default: '',
required: false
},
popContainer:{
type:String,
default:'',
@ -185,6 +189,9 @@
})
}
}
}else{
if(!this.dict){
console.error('')
}else{
//异步一开始也加载一点数据
this.loading=true
@ -197,6 +204,7 @@
}
})
}
}
},
filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0

View File

@ -241,7 +241,7 @@
:value="departCompValues[id]"
:placeholder="replaceProps(col, col.placeholder)"
:trigger-change="true"
:multi="true"
:multi="isMultipleSelect(col)"
@change="(v)=>handleChangeDepartCommon(v,id,row,col)"
/>
<span
@ -265,7 +265,7 @@
:value="userCompValues[id]"
:placeholder="replaceProps(col, col.placeholder)"
:trigger-change="true"
:multi="true"
:multi="isMultipleSelect(col)"
@change="(v)=>handleChangeUserCommon(v,id,row,col)"
/>
<span
@ -303,8 +303,33 @@
>{{ jdateValues[id] }}</span>
</a-tooltip>
</template>
<!-- time -->
<template v-else-if="col.type === formTypes.time">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<j-time
v-if="isEditRow(row, col)"
:id="id"
:key="i"
v-bind="buildProps(row,col)"
style="width: 100%;"
:value="jdateValues[id]"
:getCalendarContainer="getParentContainer"
:placeholder="replaceProps(col, col.placeholder)"
allowClear
@change="(v)=>handleChangeJDateCommon(v,id,row,col)"
/>
<span
v-else
class="j-td-span no-edit"
:class="{disabled: buildProps(row,col).disabled}"
@click="handleEditRow(row, col)"
>{{ jdateValues[id] }}</span>
</a-tooltip>
</template>
<!-- input_pop -->
<template v-else-if="col.type === formTypes.input_pop">
<template v-else-if="col.type === formTypes.input_pop||col.type === 'textarea'">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<j-input-pop
v-if="isEditRow(row, col)"
@ -423,7 +448,7 @@
<!-- update-beign-author:taoyan date:0827 for/ -->
<div v-else-if="col.type === formTypes.file" :key="i">
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<div :key="fileKey" style="position: relative;">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading" style="color:red;"/>
@ -462,7 +487,7 @@
</div>
</template>
<div :hidden="uploadValues[id] != null">
<div :hidden="hasUploadValue(id)">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<a-upload
name="file"
@ -482,7 +507,7 @@
</div>
<div v-else-if="col.type === formTypes.image" :key="i">
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<div :key="fileKey" style="position: relative;">
<template v-if="!uploadValues[id] || !(uploadValues[id]['url'] || uploadValues[id]['path'] || uploadValues[id]['message'])">
<a-icon type="loading"/>
@ -520,7 +545,7 @@
</div>
</template>
<div :hidden="uploadValues[id] != null">
<div :hidden="hasUploadValue(id)">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<a-upload
name="file"
@ -1063,7 +1088,11 @@
},
methods: {
// 判断文件/图片是否存在
hasUploadValue(id){
let flag = this.uploadValues[id] != null && this.uploadValues[id].toString().length>0
return flag;
},
getElement(id, noCaseId = false) {
if (!this.el[id]) {
this.el[id] = document.getElementById((noCaseId ? '' : this.caseId) + id)
@ -1274,7 +1303,7 @@
selectValues[inputId] = undefined
}
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
jdateValues[inputId] = sourceValue
} else if (column.type === FormTypes.slot) {
@ -1286,7 +1315,7 @@
departCompValues[inputId] = sourceValue
} else if (column.type === FormTypes.sel_user) {
userCompValues[inputId] = sourceValue
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
jInputPopValues[inputId] = sourceValue
} else if (column.type === FormTypes.radio) {
radioValues[inputId] = sourceValue
@ -1588,7 +1617,7 @@
value[column.key] = selected
}
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
value[column.key] = this.jdateValues[inputId]
} else if (column.type === FormTypes.sel_depart) {
@ -1597,7 +1626,7 @@
} else if (column.type === FormTypes.sel_user) {
value[column.key] = this.userCompValues[inputId]
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
value[column.key] = this.jInputPopValues[inputId]
} else if (column.type === FormTypes.upload) {
@ -1793,13 +1822,13 @@
}
this.$set(this.checkboxValues, key, sourceValue)
edited = true
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
edited = this.setOneValue(this.jdateValues, modelKey, newValue)
} else if (column.type === FormTypes.sel_depart) {
edited = this.setOneValue(this.departCompValues, modelKey, newValue)
} else if (column.type === FormTypes.sel_user) {
edited = this.setOneValue(this.userCompValues, modelKey, newValue)
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
edited = this.setOneValue(this.jInputPopValues, modelKey, newValue)
} else if (column.type === FormTypes.slot) {
edited = this.setOneValue(this.slotValues, modelKey, newValue)
@ -2143,6 +2172,29 @@
clearSelection() {
this.selectedRowIds = []
},
// 获取当前选中的行
getSelection() {
return this.selectedRowIds.map(id => this.getCleanId(id))
},
// 设置当前选中的行
async setSelection(selectedRowIds) {
if (Array.isArray(selectedRowIds) && selectedRowIds.length > 0) {
// 兼容IE
await this.getElementPromise('tbody')
await this.$nextTick()
this.selectedRowIds = selectedRowIds.map(id => {
let temp = id
if (!this.hasCaseId(id)) {
temp = this.caseId + id
}
return temp
})
}
},
// 切换全选状态
toggleSelectionAll() {
this.handleChangeCheckedAll()
},
/** 用于搜索下拉框中的内容 */
handleSelectFilterOption(input, option, column) {
if (column.allowSearch === true || column.allowInput === true) {
@ -2350,11 +2402,7 @@
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
// 触发valueChange 事件
if (showTime) {
this.elemValueChange(FormTypes.datetime, row, column, value)
} else {
this.elemValueChange(FormTypes.date, row, column, value)
}
this.elemValueChange(column.type, row, column, value)
},
//部门组件值改变
handleChangeDepartCommon(value, id, row, column){
@ -2736,6 +2784,11 @@
if (col.type === FormTypes.select && (col.allowInput === true || col.allowSearch === true)) {
props['showSearch'] = true
}
if (col.type === FormTypes.sel_depart || col.type === FormTypes.sel_user) {
let { storeField, textField } = this.getStoreAndTextField(col)
props['store'] = storeField
props['text'] = textField
}
// 判断是否是禁用的列
props['disabled'] = (typeof col['disabled'] === 'boolean' ? col['disabled'] : props['disabled'])
@ -2753,6 +2806,42 @@
return props
},
/**获取部门选择 、用户选择的存储字段、展示字段*/
getStoreAndTextField(col){
let storeField = '', textField = ''
if(col.type === FormTypes.sel_depart){
storeField = 'id'
textField = 'departName'
}else if(col.type === FormTypes.sel_user){
storeField = 'username'
textField = 'realname'
}
if(col.fieldExtendJson){
// online逻辑
let tempJson = JSON.parse(col.fieldExtendJson)
if(tempJson){
if(tempJson.store){
storeField = tempJson.store
}
if(tempJson.text){
textField = tempJson.text
}
}
}else{
// 实际开发逻辑
if(col.store){
storeField = col.store
}
if(col.text){
textField = col.text
}
}
return {
storeField,
textField
}
},
/** 辅助方法:防止过快点击,如果点击过快的话就返回 true */
checkTooFastClick(key = 'default', ms = 300) {
let nowTime = Date.now()
@ -2966,6 +3055,21 @@
}else{
return this.excludeCode.indexOf(code)<0
}
},
// 判断用户、部门组件是否多选
isMultipleSelect(column){
let jsonStr = column.fieldExtendJson
if(jsonStr){
// online
let config = JSON.parse(jsonStr)
if(config && config['multiSelect']==false){
return false
}
}else if(column.multi==false){
// 实际开发
return false
}
return true;
}
},

View File

@ -1,241 +0,0 @@
<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

@ -1,5 +1,5 @@
<template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear>
<a-select-option
v-for="(item,index) in selectOptions"
:key="index"

View File

@ -379,7 +379,7 @@
this.$message.warn("不能查询空条件")
}
},
emitCallback(event = {}) {
emitCallback(event = {}, loadStatus = true) {
let { params = [], matchType = this.matchType } = event
this.superQueryFlag = (params && params.length > 0)
for (let param of params) {
@ -388,7 +388,7 @@
}
}
console.debug('------>', { params, matchType })
this.$emit(this.callback, params, matchType)
this.$emit(this.callback, params, matchType, loadStatus)
},
handleCancel() {
this.close()
@ -428,9 +428,13 @@
handleOpen() {
this.show()
},
handleOutReset(loadStaus=true) {
this.resetLine()
this.emitCallback({}, loadStaus)
},
handleReset() {
this.resetLine()
this.emitCallback()
this.emitCallback({}, true)
},
handleSave() {
let queryParams = this.removeEmptyObject(this.queryParamsModel)

View File

@ -16,6 +16,8 @@
:multi="multi"
:rootOpened="rootOpened"
:depart-id="departIds"
:store="storeField()"
:text="textField()"
@ok="handleOK"
@initComp="initComp"/>
<span style="display: inline-block;height:100%;padding-left:14px" v-if="departIds" >
@ -68,7 +70,7 @@
}
},
multi(){
if(this.cellProps.multi==false){
if(this.cellProps.multi==false || this.originColumn.multi===false){
return false
}else{
return true
@ -101,15 +103,17 @@
handleEmpty(){
this.handleOK('')
},
handleOK(rows, idstr) {
handleOK(rows) {
let value = ''
if (!rows && rows.length <= 0) {
this.departNames = ''
this.departIds = ''
} else {
value = rows.map(row => row[this.customReturnField]).join(',')
this.departNames = rows.map(row => row['departName']).join(',')
this.departIds = idstr
let storeField = this.storeField();
let textField = this.textField();
value = rows.map(row => row[storeField]).join(',')
this.departNames = rows.map(row => row[textField]).join(',')
this.departIds = value
}
this.handleChangeCommon(this.departIds)
},
@ -118,6 +122,34 @@
},
handleChange(value) {
this.handleChangeCommon(value)
},
storeField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.store){
return json.store
}
}else if(this.originColumn.store){
return this.originColumn.store
}
}
return 'id'
},
textField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.text){
return json.text
}
}else if(this.originColumn.text){
return this.originColumn.text
}
}
return 'departName'
}
},
enhanced: {

View File

@ -94,7 +94,6 @@
methods: {
handleChange(value) {
debugger
// 处理下级联动
let linkage = this.renderOptions.linkage
if (linkage) {

View File

@ -0,0 +1,57 @@
<template>
<a-time-picker
ref="timePicker"
:value="innerDateValue"
allowClear
dropdownClassName="j-vxe-date-picker"
style="min-width: 0;"
v-bind="cellProps"
@change="handleChange"
/>
</template>
<script>
import moment from 'moment'
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
export default {
name: 'JVxeTimeCell',
mixins: [JVxeCellMixins],
props: {},
data() {
return {
innerDateValue: null,
dateFormat: 'HH:mm:ss'
}
},
watch: {
innerValue: {
immediate: true,
handler(val) {
if (val == null || val === '') {
this.innerDateValue = null
} else {
this.innerDateValue = moment(val, this.dateFormat)
}
}
}
},
methods: {
handleChange(mom, dateStr) {
this.handleChangeCommon(dateStr)
}
},
// 【组件增强】注释详见JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived(event) {
dispatchEvent.call(this, event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event))
},
},
}
}
</script>
<style scoped>
</style>

View File

@ -16,6 +16,8 @@
:multi="multi"
:user-ids="userIds"
@ok="selectOK"
:store="storeField"
:text="textField"
@initComp="initComp"/>
<span style="display: inline-block;height:100%;padding-left:14px" v-if="userIds" >
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ userNames }}</span>
@ -77,6 +79,30 @@
}else{
return true
}
},
storeField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.store){
return json.store
}
}
}
return 'username'
},
textField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.text){
return json.text
}
}
}
return 'realname'
}
},
watch: {
@ -102,12 +128,8 @@
this.userNames = ''
this.userIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.realname
}
this.userNames = temp.substring(1)
this.userIds = idstr
this.userIds = rows.map(row => row[this.storeField]).join(',')
this.userNames = rows.map(row => row[this.textField]).join(',')
}
this.handleChangeCommon(this.userIds)
},

View File

@ -6,6 +6,7 @@ import JVxeSlotCell from './components/cells/JVxeSlotCell'
import JVxeNormalCell from './components/cells/JVxeNormalCell'
import JVxeInputCell from './components/cells/JVxeInputCell'
import JVxeDateCell from './components/cells/JVxeDateCell'
import JVxeTimeCell from './components/cells/JVxeTimeCell'
import JVxeSelectCell from './components/cells/JVxeSelectCell'
import JVxeCheckboxCell from './components/cells/JVxeCheckboxCell'
import JVxeUploadCell from './components/cells/JVxeUploadCell'
@ -32,6 +33,7 @@ export const AllCells = {
...mapCell(JVXETypes.selectMultiple, JVxeSelectCell), // 下拉多选
...mapCell(JVXETypes.date, JVxeDateCell),
...mapCell(JVXETypes.datetime, JVxeDateCell),
...mapCell(JVXETypes.time, JVxeTimeCell),
...mapCell(JVXETypes.upload, JVxeUploadCell),
...mapCell(JVXETypes.textarea, JVxeTextareaCell),

View File

@ -22,6 +22,7 @@ export const JVXETypes = {
select: 'select',
date: 'date',
datetime: 'datetime',
time: 'time',
checkbox: 'checkbox',
upload: 'upload',
// 下拉搜索

View File

@ -73,6 +73,16 @@ export default {
props['disabled'] = true
}
// update-begin-author:taoyan date:20211011 for: online表单附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
let jsonStr = col['fieldExtendJson']
if(jsonStr){
let fieldExtendJson = JSON.parse(jsonStr)
if(fieldExtendJson && fieldExtendJson['multiSelect']==false){
props['multi'] = false
}
}
// update-end-author:taoyan date:20211011 for: online表单附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
return props
},
},

View File

@ -7,7 +7,7 @@
<a-icon type="close" @click="visible=false"/>
</span>
</div>
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange">
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange" :placeholder="placeholder">
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
</a-input>
<div slot="content">
@ -53,6 +53,10 @@
type: Boolean,
default: false,
},
placeholder:{
type:String,
required:false
}
},
data(){

View File

@ -74,6 +74,7 @@
import {filterObj} from '@/utils/util'
import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
import { httpGroupRequest } from '@/api/GroupRequest.js'
import md5 from 'md5'
const MODAL_WIDTH = 1200;
export default {
@ -390,9 +391,12 @@
}
//update-end---author:liusq Date:20210203 forpop选择器列主键问题 issues/I29P9Q------------
})
if(res.length>50){
// update-begin---author:taoyan Date:20211025 forjpopup 表格key重复BUG /issues/3121
res = md5(res)
/*if(res.length>50){
res = res.substring(0,50)
}
}*/
// update-end---author:taoyan Date:20211025 forjpopup 表格key重复BUG /issues/3121
return res
},

View File

@ -219,10 +219,20 @@
cacheRouterArray.splice(cacheRouterArray.findIndex(item => item === componentName), 1)
Vue.ls.set(CACHE_INCLUDED_ROUTES, cacheRouterArray)
}
this.emitPageClosed(removeRoute[0])
}
//update-end--Author:scott Date:20201015 for路由缓存问题关闭了tab页时再打开就不刷新 #842
},
// 触发 page-closed (页面关闭)全局事件
emitPageClosed(closedRoute) {
this.$root.$emit('page-closed', {
closedRoute,
pageList: this.pageList,
linkList: this.linkList,
activePage: this.activePage
})
},
onContextmenu(e) {
const pagekey = this.getPageKey(e.target)
if (pagekey !== null) {

View File

@ -20,6 +20,7 @@ import 'ant-design-vue/dist/antd.less'; // or 'ant-design-vue/dist/antd.less'
import '@/permission' // permission control
import '@/utils/filter' // base filter
import Print from 'vue-print-nb-jeecg'
/*import '@babel/polyfill'*/
import preview from 'vue-photo-preview'
import 'vue-photo-preview/dist/skin.css'
import SSO from '@/cas/sso.js'

View File

@ -10,7 +10,7 @@ const onlUtil = {
}
},
created(){
this.mixin_pca = new Area(this.$Jpcaa)
this.mixin_pca = new Area()
},
methods:{
simpleDateFormat(millisecond, format){

View File

@ -16,7 +16,8 @@ const getters = {
enhanceJs:(state) => (code) => {
state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
return state.enhance.enhanceJs[code]
}
},
sysSafeMode: state => state.user.sysSafeMode,
}

View File

@ -14,7 +14,9 @@ const user = {
welcome: '',
avatar: '',
permissionList: [],
info: {}
info: {},
// 系统安全模式
sysSafeMode: null,
},
mutations: {
@ -38,6 +40,13 @@ const user = {
SET_TENANT: (state, id) => {
state.tenantid = id
},
SET_SYS_SAFE_MODE: (state, sysSafeMode) => {
if (typeof sysSafeMode === 'boolean') {
state.sysSafeMode = sysSafeMode
} else {
state.sysSafeMode = false
}
},
},
actions: {
@ -124,20 +133,22 @@ const user = {
sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
if (menuData && menuData.length > 0) {
//update--begin--autor:qinfeng-----date:20200109------forJEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
menuData.forEach((item, index) => {
if (item["children"]) {
let hasChildrenMenu = item["children"].filter((i) => {
return !i.hidden || i.hidden == false
})
if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
item["hidden"] = true
}
}
})
//console.log(" menu show json ", menuData)
//update--end--autor:qinfeng-----date:20200109------forJEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
// //update--begin--autor:qinfeng-----date:20200109------forJEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
// menuData.forEach((item, index) => {
// if (item["children"]) {
// let hasChildrenMenu = item["children"].filter((i) => {
// return !i.hidden || i.hidden == false
// })
// if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
// item["hidden"] = true
// }
// }
// })
// //console.log(" menu show json ", menuData)
// //update--end--autor:qinfeng-----date:20200109------forJEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
commit('SET_PERMISSIONLIST', menuData)
// 设置系统安全模式
commit('SET_SYS_SAFE_MODE', response.result.sysSafeMode)
} else {
reject('getPermissionList: permissions must be a non-null array !')
}

View File

@ -8,6 +8,7 @@ const FormTypes = {
select: 'select',
date: 'date',
datetime: 'datetime',
time: 'time',
upload: 'upload',
file: 'file',
image: 'image',

View File

@ -75,7 +75,7 @@ const err = (error) => {
Vue.prototype.$Jnotification.error({ message: '', description: ''})
break
case 401:
Vue.prototype.$Jnotification.error({ message: '', description:'',duration: 4})
Vue.prototype.$Jnotification.error({ message: '', description:'',duration: 4})
if (token) {
store.dispatch('Logout').then(() => {
setTimeout(() => {

View File

@ -591,3 +591,39 @@ export function getReportPrintUrl(url, id, open) {
}
return url
}
/**
* JSAOP
*
* @param obj
* @param funcName
* @param callback callbackfuncName
*/
export function aspectAroundFunction(obj, funcName, callback) {
if (typeof callback !== 'function' || !obj) {
console.warn('aspectAroundFunctionobjcallback')
return
}
// 保存原来的函数
let func = obj[funcName]
if (typeof func !== 'function') {
console.warn('aspectAroundFunction' + funcName + '')
return
}
// 赋值新方法
// 实现当外部调用 funcName 时,首先调用我定义的新方法
// 然后调用传入的callback方法以决定是否执行 funcName以及更改参数、返回值
obj[funcName] = function (...args) {
return callback({
args,
// 只有执行 proceed 才会真正执行给定的 funcName 方法
proceed() {
try {
return func.apply(obj, args)
} catch (e) {
console.error(e)
}
},
})
}
}

View File

@ -14,6 +14,7 @@
<script>
import { JVXETypes } from '@/components/jeecg/JVxeTable'
import { getAction } from '@api/manage'
export default {
name: 'JVxeDemo2',
@ -23,7 +24,7 @@ export default {
linkageConfig: [
{requestData: this.requestData, key: 's1'},
// 可配置多个联动
{requestData: this.loadData, key: 'level1',},
{requestData: this.loadMenu, key: 'menu1',},
],
columns: [
{
@ -61,34 +62,34 @@ export default {
placeholder: '${title}',
},
{
title: '',
key: 'level1',
title: '',
key: 'menu1',
type: JVXETypes.select,
width: '180px',
placeholder: '${title}',
// 联动字段(即下一级的字段)
linkageKey: 'level2',
linkageKey: 'menu2',
},
{
title: '',
key: 'level2',
title: '',
key: 'menu2',
type: JVXETypes.select,
width: '180px',
placeholder: '${title}',
// 联动字段(即下一级的字段)
linkageKey: 'level3',
linkageKey: 'menu3',
},
{
title: '',
key: 'level3',
title: '',
key: 'menu3',
type: JVXETypes.select,
width: '180px',
placeholder: '${title}',
}
],
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'},
{sex: '1', s1: '110000', s2: '110100', s3: '110101'},
{sex: '2', s1: '130000', s2: '130300', s3: '130303'},
],
// 模拟数据
mockData: [
@ -123,24 +124,6 @@ export default {
{text: '', value: '130303', parent: '130300'},
{text: '', value: '130304', parent: '130300'},
],
mockData1: [
{id: '1', name: '', parentId: '0'},
{id: '2', name: '', parentId: '0'},
{id: '3', name: '', parentId: '1'},
{id: '4', name: '', parentId: '1'},
{id: '5', name: '', parentId: '2'},
{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: {
@ -156,23 +139,22 @@ export default {
})
},
// 模拟加载数据,模拟数据格式不同的情况下如何组装数据
async loadData(parent) {
return new Promise((resolve, reject) => {
let parentId = parent === '' ? '0' : parent
let data = this.mockData1.filter(i => i.parentId === parentId)
data = data.map(item => {
return {
// 必须包含以下两个字段
value: item.id,
text: item.name,
async loadMenu(parent) {
let res
// 如果parent为空则查询第一级菜单
if (parent === '') {
res = await getAction('/sys/permission/getSystemMenuList')
} else {
res = await getAction('/sys/permission/getSystemSubmenu', {parentId: parent})
}
})
setTimeout(() => {
resolve(data)
}, 500)
})
if (res.success) {
// 返回的数据里必须包含 value 和 text 字段
return res.result.map(item => ({value: item.id, text: item.name}))
}
this.$message.warning('loadMenu' + res.message)
return []
},
}
}
</script>

View File

@ -268,6 +268,7 @@
placeholder="请选择菜单"
dict="sys_permission,name,id"
pidField="parent_id"
hasChildField="is_leaf"
pidValue=""
/>
</a-form-model-item>
@ -283,6 +284,7 @@
placeholder="请选择菜单"
dict="sys_permission,name,id"
pidField="parent_id"
hasChildField="is_leaf"
pidValue=""
multiple
/>

View File

@ -156,10 +156,6 @@
</a-popconfirm>
</a-menu-item>
<a-menu-item>
<a href="javascript:;" @click="handleAgentSettings(record.username)"></a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
@ -378,10 +374,6 @@
handleChangePassword(username) {
this.$refs.passwordmodal.show(username);
},
handleAgentSettings(username){
this.$refs.sysUserAgentModal.agentSettings(username);
this.$refs.sysUserAgentModal.title = "用户代理人设置";
},
passwordModalOk() {
//TODO 密码修改完成 不需要刷新页面可以把datasource中的数据更新一下
},

View File

@ -1,183 +0,0 @@
<template>
<div class="main">
<a-form style="max-width: 500px; margin: 40px auto 0;" :form="form" @keyup.enter.native="nextStep">
<a-form-item>
<a-input
v-decorator="['username',validatorRules.username]"
size="large"
type="text"
autocomplete="false"
placeholder="请输入用户账号或手机号">
<a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
<a-row :gutter="0">
<a-col :span="14">
<a-form-item>
<a-input
v-decorator="['inputCode',validatorRules.inputCode]"
size="large"
type="text"
@change="inputCodeChange"
placeholder="请输入验证码">
<a-icon slot="prefix" v-if=" inputCodeContent==verifiedCode " type="smile"
:style="{ color: 'rgba(0,0,0,.25)' }"/>
<a-icon slot="prefix" v-else type="frown" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-col>
<a-col :span="10" style="text-align: right">
<img v-if="requestCodeSuccess" style="margin-top: 2px;" :src="randCodeImage" @click="handleChangeCheckCode"/>
<img v-else style="margin-top: 2px;" src="../../../assets/checkcode.png" @click="handleChangeCheckCode"/>
</a-col>
</a-row>
<a-form-item :wrapperCol="{span: 19, offset: 5}">
<router-link style="float: left;line-height: 40px;" :to="{ name: 'login' }">使</router-link>
<a-button type="primary" @click="nextStep"></a-button>
</a-form-item>
</a-form>
</div>
</template>
<script>
import { getAction,postAction } from '@/api/manage'
import { checkOnlyUser } from '@/api/api'
export default {
name: "Step1",
data() {
return {
form: this.$form.createForm(this),
inputCodeContent: "",
inputCodeNull: true,
verifiedCode: "",
validatorRules: {
username: {rules: [{required: false}, {validator: this.validateInputUsername}]},
inputCode: {rules: [{required: true, message: '!'}]},
},
randCodeImage:'',
requestCodeSuccess:true,
currdatetime:''
}
},
created(){
this.handleChangeCheckCode();
},
methods: {
handleChangeCheckCode(){
this.currdatetime = new Date().getTime();
getAction(`/sys/randomImage/${this.currdatetime}`).then(res=>{
if(res.success){
this.randCodeImage = res.result
this.requestCodeSuccess=true
}else{
this.$message.error(res.message)
this.requestCodeSuccess=false
}
}).catch(()=>{
this.requestCodeSuccess=false
})
},
nextStep() {
let that = this
this.form.validateFields((err, values) => {
if (!err) {
let isPhone = false;
var params = {}
var reg = /^[1-9]\d*$|^0$/;
var username = values.username;
if (reg.test(username) == true) {
params.phone = username;
isPhone = true
} else {
params.username = username;
}
that.validateInputCode().then(()=>{
getAction("/sys/user/querySysUser", params).then((res) => {
if (res.success) {
var userList = {
username: res.result.username,
phone: res.result.phone,
isPhone: isPhone
};
setTimeout(function () {
that.$emit('nextStep', userList)
})
}
});
})
}
})
},
validateInputCode() {
return new Promise((resolve,reject)=>{
postAction("/sys/checkCaptcha",{
captcha:this.inputCodeContent,
checkKey:this.currdatetime
}).then(res=>{
if(res.success){
resolve();
}else{
this.$message.error(res.message)
reject();
}
});
})
},
inputCodeChange(e) {
this.inputCodeContent = e.target.value;
console.log(this.inputCodeContent)
if (!e.target.value || 0 == e.target.value) {
this.inputCodeNull = true
} else {
this.inputCodeContent = this.inputCodeContent.toLowerCase()
this.inputCodeNull = false
}
},
generateCode(value) {
this.verifiedCode = value.toLowerCase();
console.log(this.verifiedCode);
},
validateInputUsername(rule, value, callback) {
console.log(value);
var reg = /^[0-9]+.?[0-9]*/;
if (!value) {
callback("请输入用户名和手机号!");
}
//判断用户输入账号还是手机号码
if (reg.test(value)) {
var params = {
phone: value,
};
checkOnlyUser(params).then((res) => {
if (res.success) {
callback("用户名不存在!")
} else {
callback()
}
})
} else {
var params = {
username: value,
};
checkOnlyUser(params).then((res) => {
if (res.success) {
callback("用户名不存在!")
} else {
callback()
}
})
}
},
}
}
</script>
<style scoped>
</style>

View File

@ -14,6 +14,14 @@ module.exports = {
*/
// 如果你不需要生产环境的 source map可以将其设置为 false 以加速生产环境构建。
productionSourceMap: false,
// 多入口配置
// pages: {
// index: {
// entry: 'src/main.js',
// template: 'public/index.html',
// filename: 'index.html',
// }
// },
//打包app时放开该配置
//publicPath:'./',
configureWebpack: config => {

View File

@ -888,10 +888,10 @@
cssnano-preset-default "^4.0.0"
postcss "^7.0.0"
"@jeecg/antd-online-mini@2.4.6-beta5":
version "2.4.6-beta5"
resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-2.4.6-beta5.tgz#d019ddcd87838773407c3052c21ba45163bfa76b"
integrity sha512-js+WOJdvqE31GvVkAt1VLD0L50uHYhY6gE+g8XqJoUS/JD6q4eYyHOKsGj6tG0TlNbCiAFzZBHo6bFWGVI/miQ==
"@jeecg/antd-online-mini@3.0.0-beta":
version "3.0.0-beta"
resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-3.0.0-beta.tgz#bd7cbbd30c7c2cdee9d816ad996cd9a58378dc95"
integrity sha512-V2JwKeBy8WinDrIcGaZyr+FwmF63aItR8dEO570qVhw+Gfz+E9p+fE7SNmSDdbjNu4yiKfsavzdl2JvG4pwgXA==
"@mrmlnc/readdir-enhanced@^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"
ant-design-vue@^1.7.2:
version "1.7.7"
resolved "https://registry.nlark.com/ant-design-vue/download/ant-design-vue-1.7.7.tgz#621cca28c89e2887ec391e5c129c34d463f3803e"
integrity sha1-YhzKKMieKIfsOR5cEpw01GPzgD4=
version "1.7.8"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.8.tgz#1abbf86b68a4f5b1000bea0487b8031dc0001661"
integrity sha512-F1hmiS9vwbyfuFvlamdW5l9bHKqRlj9wHaGDIE41NZMWXyWy8qL0UFa/+I0Wl8gQWZCqODW5pN6Yfoyn85At3A==
dependencies:
"@ant-design/icons" "^2.1.1"
"@ant-design/icons-vue" "^2.0.0"
@ -2892,8 +2892,8 @@ cache-loader@^2.0.1:
call-bind@^1.0.0:
version "1.0.2"
resolved "https://registry.nlark.com/call-bind/download/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha1-sdTonmiBGcPJqQOtMKuy9qkZvjw=
resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
dependencies:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
@ -3086,8 +3086,8 @@ check-types@^8.0.3:
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=
resolved "https://registry.npmjs.org/china-area-data/-/china-area-data-5.0.1.tgz#7943b83a0619f033bb5893da80cb46e52e44be66"
integrity sha512-BQDPpiv5Nn+018ekcJK2oSD9PAD+E1bvXB0wgabc//dFVS/KvRqCgg0QOEUt3vBkx9XzB5a9BmkJCEZDBxVjVw==
chokidar@^2.1.2, chokidar@^2.1.8:
version "2.1.8"
@ -3670,8 +3670,8 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
cron-parser@^2.10.0:
version "2.18.0"
resolved "https://registry.nlark.com/cron-parser/download/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf"
integrity sha1-3huwrVKMgVVINxmT+BpU5aCJ7c8=
resolved "https://registry.npmjs.org/cron-parser/-/cron-parser-2.18.0.tgz#de1bb0ad528c815548371993f81a54e5a089edcf"
integrity sha512-s4odpheTyydAbTBQepsqd2rNWGa2iV3cyo8g7zbI2QQYGLVsfbhmwukayS1XHppe02Oy1fg7mg6xoaraVJeEcg==
dependencies:
is-nan "^1.3.0"
moment-timezone "^0.5.31"
@ -5696,8 +5696,8 @@ get-caller-file@^2.0.1:
get-intrinsic@^1.0.2:
version "1.1.1"
resolved "https://registry.nlark.com/get-intrinsic/download/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha1-FfWfN2+FXERpY5SPDSTNNje0q8Y=
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
@ -6618,8 +6618,8 @@ is-mobile@^2.2.1:
is-nan@^1.3.0:
version "1.3.2"
resolved "https://registry.npm.taobao.org/is-nan/download/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha1-BDpUreoxdItVts1OCara+mm9nh0=
resolved "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d"
integrity sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==
dependencies:
call-bind "^1.0.0"
define-properties "^1.1.3"
@ -7672,8 +7672,8 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1:
moment-timezone@^0.5.31:
version "0.5.33"
resolved "https://registry.nlark.com/moment-timezone/download/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c"
integrity sha1-slL9a7V/NBybWaWrYajlGnO70iw=
resolved "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.33.tgz#b252fd6bb57f341c9b59a5ab61a8e51a73bbd22c"
integrity sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==
dependencies:
moment ">= 2.9.0"