Compare commits

...

35 Commits

Author SHA1 Message Date
8b3d83ae0b HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器 2021-06-21 20:37:23 +08:00
081c2615be HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器 2021-06-21 20:37:09 +08:00
f97c675771 online表单数据源配置,数据库类型识别错误 #2671 2021-06-21 19:29:37 +08:00
1beddbf8e8 online表单数据源配置,不支持数据库密码加密 #2672 2021-06-21 19:29:21 +08:00
8921e84303 群满,加新群 ④774126647 2021-06-17 17:44:43 +08:00
43329545d8 【gitee/I3HTFI】自定义树控件的表单里的外键直接显示id不显示name
新增监控在线用户
阿里监控去掉广告
2021-06-17 10:39:59 +08:00
50333488a5 解决issue#2639 2.4.5升级后出现后端排序报错 2021-06-07 18:21:13 +08:00
624444e3b9 用户添加页面无法修改手机号 2021-06-04 16:52:07 +08:00
1cd0f2627d JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-04 11:14:53 +08:00
18007b0524 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-03 14:04:31 +08:00
d92861ad77 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-03 13:59:37 +08:00
8a6181b108 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 16:25:47 +08:00
6840772959 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:51:21 +08:00
1bc7ee3345 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:38:47 +08:00
5f25f726c2 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:38:28 +08:00
9b14d2d6a5 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:38:14 +08:00
130f2bc4be JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:37:49 +08:00
832bc376be JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:32:35 +08:00
432385fc14 新增一种 Cron表达式 选择组件 2021-06-02 15:28:23 +08:00
4d5ac2b518 JeecgBoot低代码平台 2.4.5 版本发布,钉钉与企业微信集成版本 2021-06-02 15:26:58 +08:00
b83c26b8fd 熔断默认时间改成3秒 2021-06-02 15:25:58 +08:00
b59fc67696 新增图表API接口 2021-06-02 15:22:45 +08:00
af3afc7263 删除不需要的底层工具包 2021-06-02 15:21:16 +08:00
63c3dd26f5 即将发布新版 2021-06-02 09:37:32 +08:00
892196fe9a 外部URL菜单加载为空为空 2021-04-14 22:01:47 +08:00
bbda918cde 升级jimureport,修复已知bug 2021-04-13 16:15:10 +08:00
003ec82f48 阿里oos桶名字修改 2021-04-13 16:14:47 +08:00
4992faf66c minidao db类型说明、阿里oos桶名字修改 2021-04-13 16:14:35 +08:00
34c612d9e4 兼容数据库说明 2021-04-13 16:11:58 +08:00
844f1e228c 外部url菜单,内部打开是,也支持通过 ${token}方式传递当前登录TOKEN 2021-04-07 14:07:41 +08:00
cf5fde80d4 nacos建表语句优化 2021-04-06 14:06:55 +08:00
49ea36c50f 升级脚本到积木报表的最新版本 2021-04-06 14:06:35 +08:00
f6e2b67c61 升级SQL:积木报表的示例 2021-04-05 19:01:12 +08:00
69a4a7df6d 重构登录注册页面为a-form-model模式 2021-04-05 18:58:36 +08:00
649f99664e 积木报表升级到最新版本1.3.1-beta2 2021-04-05 18:53:14 +08:00
247 changed files with 15499 additions and 11373 deletions

View File

@ -7,12 +7,12 @@
JEECG BOOT 低代码开发平台(前后端分离版本)
===============
当前最新版本: 2.4.3发布日期2021-03-22
当前最新版本: 2.4.5发布日期2021-06-07
[![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/version-2.4.3-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-2.4.5-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 forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -23,7 +23,7 @@ JEECG BOOT 低代码开发平台(前后端分离版本)
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
JeecgBoot 是一款基于代码生成器的`低代码`开发平台!前后端分离架构 SpringBoot2.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务。强大的代码生成器让前后端代码一键生成实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
JeecgBoot 是一款基于代码生成器的`低代码平台`!前后端分离架构 SpringBoot2.xSpringCloudAnt Design&VueMybatis-plusShiroJWT支持微服务。强大的代码生成器让前后端代码一键生成实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`Online表单开发、Online报表、报表配置能力、在线图表设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔等等
@ -61,7 +61,7 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
交流互动
-----------------------------------
- QQ交流群 ③816531124、②769925425、①284271917
- QQ交流群 ④774126647、③816531124(满)、②769925425、①284271917
- 反馈问题: [反馈问题请按格式发Issues](https://github.com/zhangdaiscott/jeecg-boot/issues/new)
@ -90,7 +90,7 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
* 16.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等);
* 17.支持SAAS服务模式提供SaaS多租户架构方案。
* 18.分布式文件服务集成minio、阿里OSS等优秀的第三方提供便捷的文件上传与管理同时也支持本地存储。
* 19.主流数据库兼容一套代码完全兼容Mysql、Postgresql、Oracle三大主流数据库。
* 19.主流数据库兼容一套代码完全兼容Mysql、Postgresql、Oracle、Sqlserver、MariaDB、达梦等主流数据库。
* 20.集成工作流activiti并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 21.低代码能力在线流程设计采用开源Activiti流程引擎实现在线画流程,自定义表单,表单挂靠,业务流转
* 22.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;

View File

@ -1,13 +1,13 @@
Ant Design Jeecg Vue
====
当前最新版本: 2.4.3发布日期20210322
当前最新版本: 2.4.5发布日期20210607
Overview
----
基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Ant Design Pro Vue 版
Jeecg-boot 的前UI框架采用前后端分离方案提供强大代码生成器的低代码平台。
Jeecg-boot 的前UI框架采用前后端分离方案提供强大代码生成器的低代码平台。
前端页面代码和后端功能代码一键生成不需要写任何代码保持jeecg一贯的强大

View File

@ -1,26 +1,26 @@
{
"name": "vue-antd-jeecg",
"version": "2.4.3",
"version": "2.2.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@ant-design/colors": {
"version": "3.2.2",
"resolved": "https://registry.npm.taobao.org/@ant-design/colors/download/@ant-design/colors-3.2.2.tgz",
"integrity": "sha1-WtQ9YZ6RHzSI66wwPWBuZqhCOQM=",
"resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-3.2.2.tgz",
"integrity": "sha512-YKgNbG2dlzqMhA9NtI3/pbY16m3Yl/EeWBRa+lB1X1YaYxHrxNexiQYCLTWO/uDvAjLFMEDU+zR901waBtMtjQ==",
"requires": {
"tinycolor2": "^1.4.1"
}
},
"@ant-design/icons": {
"version": "2.1.1",
"resolved": "https://registry.npm.taobao.org/@ant-design/icons/download/@ant-design/icons-2.1.1.tgz?cache=0&sync_timestamp=1612952243741&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Ficons%2Fdownload%2F%40ant-design%2Ficons-2.1.1.tgz",
"integrity": "sha1-e5wI3/1PXUHbZn2dvl4BB9C9mko="
"resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
"integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
},
"@ant-design/icons-vue": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/@ant-design/icons-vue/download/@ant-design/icons-vue-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40ant-design%2Ficons-vue%2Fdownload%2F%40ant-design%2Ficons-vue-2.0.0.tgz",
"integrity": "sha1-A1f1AQpATp80qHpLQbKgjfaR284=",
"resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-2.0.0.tgz",
"integrity": "sha512-2c0QQE5hL4N48k5NkPG5sdpMl9YnvyNhf0U7YkdZYDlLnspoRU7vIA0UK9eHBs6OpFLcJB6o8eJrIl2ajBskPg==",
"requires": {
"@ant-design/colors": "^3.1.0",
"babel-runtime": "^6.26.0"
@ -1417,18 +1417,18 @@
"dev": true
},
"@simonwep/pickr": {
"version": "1.7.4",
"resolved": "https://registry.npm.taobao.org/@simonwep/pickr/download/@simonwep/pickr-1.7.4.tgz",
"integrity": "sha1-sU/NlFiQOIuHDNbbTWx41THyUUE=",
"version": "1.7.1",
"resolved": "https://registry.npmjs.org/@simonwep/pickr/-/pickr-1.7.1.tgz",
"integrity": "sha512-ftbskrPKAkRLYVj8IhV4Bn86g16It9Uq/p4G0FdjRz36pKKjW0JdxdDWDIVuAev0Urg8604Ho98js6JmjXdiZQ==",
"requires": {
"core-js": "^3.6.5",
"nanopop": "^2.1.0"
"nanopop": "^1.3.0"
},
"dependencies": {
"core-js": {
"version": "3.9.1",
"resolved": "https://registry.npm.taobao.org/core-js/download/core-js-3.9.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-3.9.1.tgz",
"integrity": "sha1-zsjeWT246yqF/7Db3rMSy25UYK4="
"version": "3.6.5",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz",
"integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA=="
}
}
},
@ -1443,6 +1443,11 @@
"string-width": "^2.0.0"
}
},
"@sphinxxxx/color-conversion": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz",
"integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw=="
},
"@tinymce/tinymce-vue": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-vue/-/tinymce-vue-2.1.0.tgz",
@ -3730,6 +3735,11 @@
"negotiator": "0.6.2"
}
},
"ace-builds": {
"version": "1.4.12",
"resolved": "https://registry.npmjs.org/ace-builds/-/ace-builds-1.4.12.tgz",
"integrity": "sha512-G+chJctFPiiLGvs3+/Mly3apXTcfgE45dT5yp12BcWZ1kUs+gm0qd3/fv4gsz6fVag4mM0moHVpjHDIgph6Psg=="
},
"acorn": {
"version": "5.7.4",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
@ -3777,8 +3787,8 @@
},
"add-dom-event-listener": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/add-dom-event-listener/download/add-dom-event-listener-1.1.0.tgz",
"integrity": "sha1-apLbOg3Qq8JU4JXA8dwUrLuq4xA=",
"resolved": "https://registry.npmjs.org/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz",
"integrity": "sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==",
"requires": {
"object-assign": "4.x"
}
@ -3914,9 +3924,9 @@
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
},
"ant-design-vue": {
"version": "1.7.4",
"resolved": "https://registry.npm.taobao.org/ant-design-vue/download/ant-design-vue-1.7.4.tgz",
"integrity": "sha1-hU6QmF3A+pzMbWgcEEVgwA6q+FU=",
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.6.3.tgz",
"integrity": "sha512-Zt0z0SXzHCgow0chv4OA8lONxVOzXf7iLmZxdVHlsS3IaPn5n8QNPCzBUh3z0IXBdaDPiX9tjELQCXf9vdXdlw==",
"requires": {
"@ant-design/icons": "^2.1.1",
"@ant-design/icons-vue": "^2.0.0",
@ -4028,8 +4038,8 @@
},
"array-tree-filter": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/array-tree-filter/download/array-tree-filter-2.1.0.tgz",
"integrity": "sha1-hzrAD+yDdJ8lWsjdCDgUtPYykZA="
"resolved": "https://registry.npmjs.org/array-tree-filter/-/array-tree-filter-2.1.0.tgz",
"integrity": "sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw=="
},
"array-union": {
"version": "1.0.2",
@ -4138,9 +4148,9 @@
"dev": true
},
"async-validator": {
"version": "3.5.1",
"resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-3.5.1.tgz?cache=0&sync_timestamp=1605751734916&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-validator%2Fdownload%2Fasync-validator-3.5.1.tgz",
"integrity": "sha1-zWK5aIskZfSEIOJ620d2CrG1VZ8="
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-3.3.0.tgz",
"integrity": "sha512-cAHGD9EL8aCqWXjnb44q94MWiDFzUo1tMhvLb2WzcpWqGiKugsjWG9cvl+jPgkPca7asNbsBU3fa0cwkI/P+Xg=="
},
"asynckit": {
"version": "0.4.0",
@ -4501,8 +4511,8 @@
},
"babel-helper-vue-jsx-merge-props": {
"version": "2.0.3",
"resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
"integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY="
"resolved": "https://registry.npmjs.org/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
"integrity": "sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg=="
},
"babel-helpers": {
"version": "6.24.1",
@ -5617,8 +5627,8 @@
},
"classnames": {
"version": "2.2.6",
"resolved": "https://registry.npm.taobao.org/classnames/download/classnames-2.2.6.tgz",
"integrity": "sha1-Q5Nb/90pHzJtrQogUwmzjQD2UM4="
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
"integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
},
"clean-css": {
"version": "4.2.3",
@ -6079,7 +6089,7 @@
},
"component-classes": {
"version": "1.2.6",
"resolved": "https://registry.npm.taobao.org/component-classes/download/component-classes-1.2.6.tgz",
"resolved": "https://registry.npmjs.org/component-classes/-/component-classes-1.2.6.tgz",
"integrity": "sha1-xkI5TDYYpNiwuJGe/Mu9kw5c1pE=",
"requires": {
"component-indexof": "0.0.3"
@ -6092,7 +6102,7 @@
},
"component-indexof": {
"version": "0.0.3",
"resolved": "https://registry.npm.taobao.org/component-indexof/download/component-indexof-0.0.3.tgz",
"resolved": "https://registry.npmjs.org/component-indexof/-/component-indexof-0.0.3.tgz",
"integrity": "sha1-EdCRMSI5648yyPJa6csAL/6NPCQ="
},
"compressible": {
@ -8018,7 +8028,7 @@
},
"dom-closest": {
"version": "0.2.0",
"resolved": "https://registry.npm.taobao.org/dom-closest/download/dom-closest-0.2.0.tgz",
"resolved": "https://registry.npmjs.org/dom-closest/-/dom-closest-0.2.0.tgz",
"integrity": "sha1-69n5HRvyLo1vR3h2u80+yQIWwM8=",
"requires": {
"dom-matches": ">=1.0.1"
@ -8035,13 +8045,13 @@
},
"dom-matches": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/dom-matches/download/dom-matches-2.0.0.tgz",
"resolved": "https://registry.npmjs.org/dom-matches/-/dom-matches-2.0.0.tgz",
"integrity": "sha1-0nKLQWqHUzmA6wibhI0lPPI6dYw="
},
"dom-scroll-into-view": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/dom-scroll-into-view/download/dom-scroll-into-view-2.0.1.tgz",
"integrity": "sha1-DezIUigB/Y0/HGujVadNOCxfmJs="
"resolved": "https://registry.npmjs.org/dom-scroll-into-view/-/dom-scroll-into-view-2.0.1.tgz",
"integrity": "sha512-bvVTQe1lfaUr1oFzZX80ce9KLDlZ3iU+XGNE/bz9HnGdklTieqsbmsLHe+rT2XWqopvL0PckkYqN7ksmm5pe3w=="
},
"dom-serializer": {
"version": "0.2.2",
@ -10588,7 +10598,7 @@
},
"intersperse": {
"version": "1.0.0",
"resolved": "https://registry.npm.taobao.org/intersperse/download/intersperse-1.0.0.tgz",
"resolved": "https://registry.npmjs.org/intersperse/-/intersperse-1.0.0.tgz",
"integrity": "sha1-8lYfsc/vn1J3zDNHoiiGtDUaUYE="
},
"invariant": {
@ -10759,13 +10769,13 @@
},
"is-mobile": {
"version": "2.2.2",
"resolved": "https://registry.npm.taobao.org/is-mobile/download/is-mobile-2.2.2.tgz",
"integrity": "sha1-9snF1Q7gElTOBec5vdg18e1OmVQ="
"resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.2.tgz",
"integrity": "sha512-wW/SXnYJkTjs++tVK5b6kVITZpAZPtUrt9SF80vvxGiF/Oywal+COk1jlRkiVq15RFNEQKQY31TkV24/1T5cVg=="
},
"is-negative-zero": {
"version": "2.0.1",
"resolved": "https://registry.npm.taobao.org/is-negative-zero/download/is-negative-zero-2.0.1.tgz",
"integrity": "sha1-PedGwY3aIxkkGlNnWQjY92bxHCQ="
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
"integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE="
},
"is-number": {
"version": "7.0.0",
@ -10886,8 +10896,8 @@
},
"ismobilejs": {
"version": "1.1.1",
"resolved": "https://registry.npm.taobao.org/ismobilejs/download/ismobilejs-1.1.1.tgz",
"integrity": "sha1-xWygro5Sskyg8iul7zIVot27qg4="
"resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz",
"integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw=="
},
"isobject": {
"version": "3.0.1",
@ -10900,12 +10910,22 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
"dev": true
},
"javascript-natural-sort": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz",
"integrity": "sha1-+eIwPUUH9tdDVac2ZNFED7Wg71k="
},
"javascript-stringify": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz",
"integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=",
"dev": true
},
"jmespath": {
"version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"js-base64": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.3.tgz",
@ -10984,6 +11004,11 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"json-source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/json-source-map/-/json-source-map-0.6.1.tgz",
"integrity": "sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg=="
},
"json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@ -11006,7 +11031,7 @@
},
"json2mq": {
"version": "0.2.0",
"resolved": "https://registry.npm.taobao.org/json2mq/download/json2mq-0.2.0.tgz",
"resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz",
"integrity": "sha1-tje9O6nqvhIsg+lyBIOusQ0skEo=",
"requires": {
"string-convert": "^0.2.0"
@ -11025,6 +11050,21 @@
"minimist": "^1.2.0"
}
},
"jsoneditor": {
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/jsoneditor/-/jsoneditor-9.0.3.tgz",
"integrity": "sha512-4uwT7nmNBAlC2KUUpsHaX2GZ+KoOohjtE/HzGToufYASGjnMzkf6EcLS+pAwwVHLv7VUnXmAuMY4NaQ9cS25lA==",
"requires": {
"ace-builds": "^1.4.11",
"ajv": "^6.12.2",
"javascript-natural-sort": "^0.7.1",
"jmespath": "^0.15.0",
"json-source-map": "^0.6.1",
"mobius1-selectr": "^2.4.13",
"picomodal": "^3.0.0",
"vanilla-picker": "^2.10.1"
}
},
"jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
@ -11432,7 +11472,7 @@
},
"lodash.toarray": {
"version": "4.4.0",
"resolved": "https://registry.npm.taobao.org/lodash.toarray/download/lodash.toarray-4.4.0.tgz",
"resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz",
"integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE="
},
"lodash.transform": {
@ -12024,10 +12064,15 @@
"minimist": "^1.2.5"
}
},
"mobius1-selectr": {
"version": "2.4.13",
"resolved": "https://registry.npmjs.org/mobius1-selectr/-/mobius1-selectr-2.4.13.tgz",
"integrity": "sha512-Mk9qDrvU44UUL0EBhbAA1phfQZ7aMZPjwtL7wkpiBzGh8dETGqfsh50mWoX9EkjDlkONlErWXArHCKfoxVg0Bw=="
},
"moment": {
"version": "2.29.1",
"resolved": "https://registry.npm.taobao.org/moment/download/moment-2.29.1.tgz",
"integrity": "sha1-sr52n6MZQL6e7qZGnAdeNQBvo9M="
"version": "2.27.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz",
"integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ=="
},
"move-concurrently": {
"version": "1.0.1",
@ -12064,8 +12109,8 @@
},
"mutationobserver-shim": {
"version": "0.3.7",
"resolved": "https://registry.npm.taobao.org/mutationobserver-shim/download/mutationobserver-shim-0.3.7.tgz",
"integrity": "sha1-i/YzsMCwKRoRByVe0ywTCIqMW/M="
"resolved": "https://registry.npmjs.org/mutationobserver-shim/-/mutationobserver-shim-0.3.7.tgz",
"integrity": "sha512-oRIDTyZQU96nAiz2AQyngwx1e89iApl2hN5AOYwyxLUB47UYsU3Wv9lJWqH5y/QdiYkc5HQLi23ZNB3fELdHcQ=="
},
"mute-stream": {
"version": "0.0.7",
@ -12110,9 +12155,9 @@
}
},
"nanopop": {
"version": "2.1.0",
"resolved": "https://registry.npm.taobao.org/nanopop/download/nanopop-2.1.0.tgz",
"integrity": "sha1-I0dlE87iQFiIr9LopLVAZrcLnmA="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/nanopop/-/nanopop-1.3.0.tgz",
"integrity": "sha512-DQDhHyPhKLKrXOjVkChsAoWh/WpKuVINDKl4qvFbguqokRJWQBSNSlPzMS+Xy3yBQKeQ39rICMB2asDvdUiVxw=="
},
"natural-compare": {
"version": "1.4.0",
@ -12151,8 +12196,8 @@
},
"node-emoji": {
"version": "1.10.0",
"resolved": "https://registry.npm.taobao.org/node-emoji/download/node-emoji-1.10.0.tgz",
"integrity": "sha1-iIar0l2ce7YYAqZYUj0fjSqJsto=",
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz",
"integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==",
"requires": {
"lodash.toarray": "^4.4.0"
}
@ -12410,8 +12455,8 @@
},
"omit.js": {
"version": "1.0.2",
"resolved": "https://registry.npm.taobao.org/omit.js/download/omit.js-1.0.2.tgz",
"integrity": "sha1-kaFPDrqEBm36AVvzDkdMR/MLyFg=",
"resolved": "https://registry.npmjs.org/omit.js/-/omit.js-1.0.2.tgz",
"integrity": "sha512-/QPc6G2NS+8d4L/cQhbk6Yit1WTB6Us2g84A7A/1+w9d/eRGHyEqC5kkQtHVoHZ5NFWGG7tUGgrhVZwgZanKrQ==",
"requires": {
"babel-runtime": "^6.23.0"
}
@ -12823,6 +12868,11 @@
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"optional": true
},
"picomodal": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/picomodal/-/picomodal-3.0.0.tgz",
"integrity": "sha1-+s0w9PvzSoCcHgTqUl8ATzmcC4I="
},
"pify": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
@ -14630,8 +14680,8 @@
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npm.taobao.org/raf/download/raf-3.4.1.tgz",
"integrity": "sha1-B0LpmkplUvRF1z4+4DKK8P8e3jk=",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"requires": {
"performance-now": "^2.1.0"
}
@ -15028,8 +15078,8 @@
},
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ="
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": {
"version": "1.17.0",
@ -15459,13 +15509,13 @@
},
"shallow-equal": {
"version": "1.2.1",
"resolved": "https://registry.npm.taobao.org/shallow-equal/download/shallow-equal-1.2.1.tgz",
"integrity": "sha1-TBar+lYEOqINBQMk76aJQLDaedo="
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz",
"integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA=="
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npm.taobao.org/shallowequal/download/shallowequal-1.1.0.tgz",
"integrity": "sha1-GI1SHelbkIdAT9TctosT3wrk5/g="
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"shebang-command": {
"version": "1.2.0",
@ -15951,7 +16001,7 @@
},
"string-convert": {
"version": "0.2.1",
"resolved": "https://registry.npm.taobao.org/string-convert/download/string-convert-0.2.1.tgz",
"resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz",
"integrity": "sha1-aYLMMEn7tM2F+LJFaLnZvznu/5c="
},
"string-width": {
@ -16481,9 +16531,9 @@
"integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
},
"tinycolor2": {
"version": "1.4.2",
"resolved": "https://registry.npm.taobao.org/tinycolor2/download/tinycolor2-1.4.2.tgz?cache=0&sync_timestamp=1601056395015&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftinycolor2%2Fdownload%2Ftinycolor2-1.4.2.tgz",
"integrity": "sha1-P2pNEHGtB2dtf6Ry4frECnGdiAM="
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.4.1.tgz",
"integrity": "sha1-9PrTM0R7wLB9TcjpIJ2POaisd+g="
},
"tinymce": {
"version": "5.4.1",
@ -16956,6 +17006,14 @@
"spdx-expression-parse": "^3.0.0"
}
},
"vanilla-picker": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.10.1.tgz",
"integrity": "sha512-Bo4HOKkSorcQoRB08HwDMb8X2jt3SsZw7gzFlbzXbhnaxdUVJBm3LOUudr7M1SCVwPCo8d3nq8ajiAg8lAoqPg==",
"requires": {
"@sphinxxxx/color-conversion": "^2.2.2"
}
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@ -17220,8 +17278,8 @@
},
"vue-ref": {
"version": "2.0.0",
"resolved": "https://registry.npm.taobao.org/vue-ref/download/vue-ref-2.0.0.tgz",
"integrity": "sha1-SDCE1zKr7RHaeWd4qCZqOvDqGpw="
"resolved": "https://registry.npmjs.org/vue-ref/-/vue-ref-2.0.0.tgz",
"integrity": "sha512-uKNKpFOVeWNqS2mrBZqnpLyXJo5Q+vnkex6JvpENvhXHFNBW/SJTP8vJywLuVT3DpxwXcF9N0dyIiZ4/NpTexQ=="
},
"vue-router": {
"version": "3.3.4",
@ -17269,20 +17327,10 @@
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.5.1.tgz",
"integrity": "sha512-w7oJzmHQs0FM9LXodfskhw9wgKBiaB+totOdb8sNzbTB2KDCEEwEs29NzBZFh/lmEK1t5tDmM1vtsO7ubG1DFw=="
},
"vxe-table": {
"version": "2.9.13",
"resolved": "https://registry.npm.taobao.org/vxe-table/download/vxe-table-2.9.13.tgz",
"integrity": "sha1-YZzVhRYN3ptSa5DOVSo1jss6YGA="
},
"vxe-table-plugin-antd": {
"version": "1.8.10",
"resolved": "https://registry.npm.taobao.org/vxe-table-plugin-antd/download/vxe-table-plugin-antd-1.8.10.tgz",
"integrity": "sha1-psAr/HzyJUYxETG4uFTAqtqwIOo="
},
"warning": {
"version": "4.0.3",
"resolved": "https://registry.npm.taobao.org/warning/download/warning-4.0.3.tgz",
"integrity": "sha1-Fungd+uKhtavfWSqHgX9hbRnjKM=",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"requires": {
"loose-envify": "^1.0.0"
}
@ -18128,11 +18176,6 @@
"async-limiter": "~1.0.0"
}
},
"xe-utils": {
"version": "2.4.8",
"resolved": "https://registry.npm.taobao.org/xe-utils/download/xe-utils-2.4.8.tgz",
"integrity": "sha1-Dv2jyoH2tV9oqKMeJ2+xfaWcmLM="
},
"xregexp": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "vue-antd-jeecg",
"version": "2.4.3",
"version": "2.4.5",
"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.3-RC",
"@jeecg/antd-online-mini": "2.4.5-RC",
"@antv/data-set": "^0.11.4",
"viser-vue": "^2.4.8",
"axios": "^0.18.0",
@ -43,7 +43,8 @@
"dom-align": "1.12.0",
"xe-utils": "2.4.8",
"vxe-table": "2.9.13",
"vxe-table-plugin-antd": "1.8.10"
"vxe-table-plugin-antd": "1.8.10",
"cron-parser": "^2.10.0"
},
"devDependencies": {
"@babel/polyfill": "^7.2.5",
@ -96,10 +97,7 @@
"vue/html-closing-bracket-newline": 0,
"vue/no-parsing-error": 0,
"no-tabs": 0,
"indent": [
"off",
2
],
"indent": ["off", 2],
"no-console": 0,
"space-before-function-paren": 0
}

View File

@ -71,4 +71,17 @@ export function thirdLogin(token,thirdType) {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}
/**
* 强退其他账号
* @param token
* @returns {*}
*/
export function forceLogout(parameter) {
return axios({
url: '/sys/online/forceLogout',
method: 'post',
data: parameter
})
}

View File

@ -1,5 +1,6 @@
import Vue from 'vue'
import { axios } from '@/utils/request'
import signMd5Utils from '@/utils/encryption/signMd5Utils'
const api = {
user: '/mock/api/user',
@ -13,19 +14,29 @@ export default api
//post
export function postAction(url,parameter) {
let sign = signMd5Utils.getSign(url, parameter);
//将签名和时间戳,添加在请求接口 Header
let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
return axios({
url: url,
method:'post' ,
data: parameter
data: parameter,
headers: signHeader
})
}
//post method= {post | put}
export function httpAction(url,parameter,method) {
let sign = signMd5Utils.getSign(url, parameter);
//将签名和时间戳,添加在请求接口 Header
let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
return axios({
url: url,
method:method ,
data: parameter
data: parameter,
headers: signHeader
})
}
@ -40,10 +51,15 @@ export function putAction(url,parameter) {
//get
export function getAction(url,parameter) {
let sign = signMd5Utils.getSign(url, parameter);
//将签名和时间戳,添加在请求接口 Header
let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getDateTimeToString()};
return axios({
url: url,
method: 'get',
params: parameter
params: parameter,
headers: signHeader
})
}

View File

@ -6,7 +6,7 @@
</template>
<script>
import JVxeCellMixins, { vModel, dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
import JVxeCellMixins, { dispatchEvent, vModel } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
export default {
name: 'JVxePopupCell',
@ -22,6 +22,8 @@
orgFields: col.orgFields,
destFields: col.destFields,
groupId: caseId,
param: col.param,
sorter: col.sorter,
}
},
},
@ -48,7 +50,9 @@
// 【组件增强】注释详见JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-input'),
editActived(event) {
dispatchEvent.call(this, event, 'ant-input')
},
},
},
}

View File

@ -254,7 +254,9 @@ export const DictSearchInputCell = {
// 【组件增强】注释详见JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-select'),
editActived(event) {
dispatchEvent.call(this, event, 'ant-select')
},
},
}
}

View File

@ -39,5 +39,5 @@ UserMenu.vue:首页右上侧的内容
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png "在这里输入图片标题")
####16.trend包 趋势显示组件(如下图)
![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png "在这里输入图片标题")
![corn表达式](https://oscimg.oschina.net/oscnet/661f9ac09016395f9f49286143af3241623.jpg)
![corn控件添加清除按钮](https://oscimg.oschina.net/oscnet/15096e49f2e29bd829e304d56770025d03c.jpg)
![cron表达式](https://oscimg.oschina.net/oscnet/661f9ac09016395f9f49286143af3241623.jpg)
![cron控件添加清除按钮](https://oscimg.oschina.net/oscnet/15096e49f2e29bd829e304d56770025d03c.jpg)

View File

@ -25,7 +25,6 @@
props: {
dictCode: String,
placeholder: String,
triggerChange: Boolean,
disabled: Boolean,
value: [String, Number],
type: String,
@ -82,19 +81,15 @@
}
})
},
handleInput(e) {
handleInput(e='') {
let val;
if(this.tagType=="radio"){
if(Object.keys(e).includes('target')){
val = e.target.value
}else{
val = e
}
console.log(val);
if(this.triggerChange){
this.$emit('change', val);
}else{
this.$emit('input', val);
}
this.$emit('change', val);
},
setCurrentDictOptions(dictOptions){
this.dictOptions = dictOptions
@ -102,6 +97,10 @@
getCurrentDictOptions(){
return this.dictOptions
}
},
model:{
prop: 'value',
event: 'change'
}
}
</script>

View File

@ -65,7 +65,11 @@
type: Number,
default: 10,
required: false
}
},
getPopupContainer: {
type:Function,
default: null
},
},
data(){
this.loadData = debounce(this.loadData, 800);//消抖
@ -226,7 +230,9 @@
return this.options
},
getParentContainer(node){
if(!this.popContainer){
if(typeof this.getPopupContainer === 'function'){
return this.getPopupContainer(node)
} else if(!this.popContainer){
return node.parentNode
}else{
return document.querySelector(this.popContainer)

View File

@ -1,7 +1,7 @@
<template>
<div class="components-input-demo-presuffix">
<a-input @click="openModal" placeholder="corn表达式" v-model="cron" @change="handleOK">
<a-icon slot="prefix" type="schedule" title="corn控件"/>
<a-input @click="openModal" placeholder="cron表达式" v-model="cron" @change="(e)=>handleOK(e.target.value)">
<a-icon slot="prefix" type="schedule" title="cron控件"/>
<a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<JCronModal ref="innerVueCron" :data="cron" @ok="handleOK"></JCronModal>

View File

@ -0,0 +1,246 @@
<template>
<div class="j-easy-cron">
<div class="content">
<div>
<a-tabs size="small" v-model="curtab">
<a-tab-pane tab="" key="second" v-if="!hideSecond">
<second-ui v-model="second" :disabled="disabled"></second-ui>
</a-tab-pane>
<a-tab-pane tab="" key="minute">
<minute-ui v-model="minute" :disabled="disabled"></minute-ui>
</a-tab-pane>
<a-tab-pane tab="" key="hour">
<hour-ui v-model="hour" :disabled="disabled"></hour-ui>
</a-tab-pane>
<a-tab-pane tab="" key="day">
<day-ui v-model="day" :week="week" :disabled="disabled"></day-ui>
</a-tab-pane>
<a-tab-pane tab="" key="month">
<month-ui v-model="month" :disabled="disabled"></month-ui>
</a-tab-pane>
<a-tab-pane tab="" key="week">
<week-ui v-model="week" :day="day" :disabled="disabled"></week-ui>
</a-tab-pane>
<a-tab-pane tab="" key="year" v-if="!hideYear && !hideSecond">
<year-ui v-model="year" :disabled="disabled"></year-ui>
</a-tab-pane>
</a-tabs>
</div>
<a-divider/>
<!-- 执行时间预览 -->
<a-row :gutter="8">
<a-col :span="18" style="margin-top: 22px;">
<a-row :gutter="8">
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.second" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.minute" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.hour" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.day" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.month" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.week" @blur="onInputBlur"/>
</a-col>
<a-col :span="8" style="margin-bottom: 8px;">
<a-input addon-before="" v-model="inputValues.year" @blur="onInputBlur"/>
</a-col>
<a-col :span="16" style="margin-bottom: 8px;">
<a-input addon-before="Cron" v-model="inputValues.cron" @blur="onInputCronBlur"/>
</a-col>
</a-row>
</a-col>
<a-col :span="6">
<div>近十次执行时间(不含年)</div>
<a-textarea type="textarea" :value="preTimeList" :rows="5"/>
</a-col>
</a-row>
</div>
</div>
</template>
<script>
import SecondUi from './tabs/second'
import MinuteUi from './tabs/minute'
import HourUi from './tabs/hour'
import DayUi from './tabs/day'
import WeekUi from './tabs/week'
import MonthUi from './tabs/month'
import YearUi from './tabs/year'
import CronParser from 'cron-parser'
import dateFormat from './format-date'
import { simpleDebounce } from '@/utils/util'
import ACol from 'ant-design-vue/es/grid/Col'
export default {
name: 'easy-cron',
components: {
ACol,
SecondUi,
MinuteUi,
HourUi,
DayUi,
WeekUi,
MonthUi,
YearUi
},
props: {
cronValue: {
type: String,
default: ''
},
disabled: {
type: Boolean,
default: false
},
hideSecond: {
type: Boolean,
default: false
},
hideYear: {
type: Boolean,
default: false
},
remote: {
type: Function,
default: null
}
},
data() {
return {
curtab: this.hideSecond ? 'minute' : 'second',
second: '*',
minute: '*',
hour: '*',
day: '*',
month: '*',
week: '?',
year: '*',
inputValues: {second: '', minute: '', hour: '', day: '', month: '', week: '', year: '', cron: ''},
preTimeList: '执行预览,会忽略年份参数',
}
},
computed: {
cronValue_c() {
let result = []
if (!this.hideSecond) result.push(this.second ? this.second : '*')
result.push(this.minute ? this.minute : '*')
result.push(this.hour ? this.hour : '*')
result.push(this.day ? this.day : '*')
result.push(this.month ? this.month : '*')
result.push(this.week ? this.week : '?')
if (!this.hideYear && !this.hideSecond) result.push(this.year ? this.year : '*')
return result.join(' ')
},
cronValue_c2() {
const v = this.cronValue_c
if (this.hideYear || this.hideSecond) return v
const vs = v.split(' ')
return vs.slice(0, vs.length - 1).join(' ')
}
},
watch: {
cronValue(newVal, oldVal) {
if (newVal === this.cronValue_c) {
// console.info('same cron value: ' + newVal)
return
}
this.formatValue()
},
cronValue_c(newVal, oldVal) {
this.calTriggerList()
this.$emit('change', newVal)
Object.assign(this.inputValues, {
second: this.second,
minute: this.minute,
hour: this.hour,
day: this.day,
month: this.month,
week: this.week,
year: this.year,
cron: this.cronValue_c,
})
}
},
created() {
this.formatValue()
this.$nextTick(() => {
this.calTriggerListInner()
})
},
methods: {
formatValue() {
if (!this.cronValue) return
const values = this.cronValue.split(' ').filter(item => !!item)
if (!values || values.length <= 0) return
let i = 0
if (!this.hideSecond) this.second = values[i++]
if (values.length > i) this.minute = values[i++]
if (values.length > i) this.hour = values[i++]
if (values.length > i) this.day = values[i++]
if (values.length > i) this.month = values[i++]
if (values.length > i) this.week = values[i++]
if (values.length > i) this.year = values[i]
},
calTriggerList: simpleDebounce(function () {
this.calTriggerListInner()
}, 500),
calTriggerListInner() {
// 设置了回调函数
if (this.remote) {
this.remote(this.cronValue_c, +new Date(), v => {
this.preTimeList = v
})
return
}
const format = 'yyyy-MM-dd hh:mm:ss'
const options = {
currentDate: dateFormat(new Date(), format)
}
const iter = CronParser.parseExpression(this.cronValue_c2, options)
const result = []
for (let i = 1; i <= 10; i++) {
result.push(dateFormat(new Date(iter.next()), format))
}
this.preTimeList = result.length > 0 ? result.join('\n') : '无执行时间'
},
onInputBlur(){
this.second = this.inputValues.second
this.minute = this.inputValues.minute
this.hour = this.inputValues.hour
this.day = this.inputValues.day
this.month = this.inputValues.month
this.week = this.inputValues.week
this.year = this.inputValues.year
},
onInputCronBlur(event){
this.$emit('change', event.target.value)
},
},
model: {
prop: 'cronValue',
event: 'change'
},
}
</script>
<style scoped lang="less">
.j-easy-cron {
/deep/ .content {
.ant-checkbox-wrapper + .ant-checkbox-wrapper {
margin-left: 0;
}
}
}
</style>

View File

@ -0,0 +1,99 @@
<template>
<div class="input-cron">
<a-input :placeholder="placeholder" v-model="editCronValue" :disabled="disabled">
<a slot="addonAfter" @click="showConfigDlg" class="config-btn" :disabled="disabled">
<a-icon type="setting"></a-icon>
选择
</a>
</a-input>
<j-modal :visible.sync="show" title="Cron表达式" width="800px">
<easy-cron
v-model="editCronValue"
:exeStartTime="exeStartTime"
:hideYear="hideYear"
:remote="remote"
:hideSecond="hideSecond"
style="width: 100%"
></easy-cron>
</j-modal>
</div>
</template>
<script>
import EasyCron from './EasyCron.vue'
export default {
name: 'input-cron',
components: {EasyCron},
model: {
prop: 'cronValue',
event: 'change'
},
props: {
cronValue: {
type: String,
default: ''
},
width: {
type: String,
default: '800px'
},
placeholder: {
type: String,
default: '请输入cron表达式'
},
disabled: {
type: Boolean,
default: false
},
exeStartTime: {
type: [Number, String, Object],
default: 0
},
hideSecond: {
type: Boolean,
default: false
},
hideYear: {
type: Boolean,
default: false
},
remote: {
type: Function,
default: null
}
},
data() {
return {
editCronValue: this.cronValue,
show: false,
}
},
watch: {
cronValue(newVal, oldVal) {
if (newVal === this.editCronValue) {
return
}
this.editCronValue = newVal
},
editCronValue(newVal, oldVal) {
this.$emit('change', newVal)
}
},
methods: {
showConfigDlg() {
if (!this.disabled) {
this.show = true
}
}
}
}
</script>
<style scoped>
.config-btn {
cursor: pointer;
}
</style>

View File

@ -0,0 +1,37 @@
const dateFormat = (date, block) => {
if (!date) {
return ''
}
let format = block || 'yyyy-MM-dd'
date = new Date(date)
const map = {
M: date.getMonth() + 1, // 月份
d: date.getDate(), // 日
h: date.getHours(), // 小时
m: date.getMinutes(), // 分
s: date.getSeconds(), // 秒
q: Math.floor((date.getMonth() + 3) / 3), // 季度
S: date.getMilliseconds() // 毫秒
}
format = format.replace(/([yMdhmsqS])+/g, (all, t) => {
let v = map[t]
if (v !== undefined) {
if (all.length > 1) {
v = `0${v}`
v = v.substr(v.length - 2)
}
return v
} else if (t === 'y') {
return (date.getFullYear().toString()).substr(4 - all.length)
}
return all
})
return format
}
export default dateFormat

View File

@ -0,0 +1,6 @@
// 原开源项目地址https://gitee.com/toktok/easy-cron
import InputCron from './InputCron.vue'
InputCron.name = 'JEasyCron'
export default InputCron

View File

@ -0,0 +1,21 @@
export const WEEK_MAP_EN = {
'SUN': '0',
'MON': '1',
'TUE': '2',
'WED': '3',
'THU': '4',
'FRI': '5',
'SAT': '6'
}
export const replaceWeekName = (c) => {
// console.info('after: ' + c)
if (c) {
Object.keys(WEEK_MAP_EN).forEach(k => {
c = c.replace(new RegExp(k, 'g'), WEEK_MAP_EN[k])
})
c = c.replace(new RegExp('7', 'g'), '0')
}
// console.info('after: ' + c)
return c
}

View File

@ -0,0 +1,101 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_NOT_SET" class="choice" :disabled="disableChoice">不设置</a-radio>
<span class="tip-info">日和周只能设置其中之一</span>
</div>
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disableChoice">每日</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disableChoice">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disableChoice">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.start"/>
日开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_WORK" class="choice" :disabled="disableChoice">工作日</a-radio>
本月
<a-input-number :disabled="type!==TYPE_WORK || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueWork"/>
日,最近的工作日
</div>
<div class="item">
<a-radio value="TYPE_LAST" class="choice" :disabled="disableChoice">最后一日</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'day',
mixins: [mixin],
props: {
week: {
type: String,
default: '?'
}
},
data() {
return {}
},
computed: {
disableChoice() {
return (this.week && this.week !== '?') || this.disabled
}
},
watch: {
value_c(newVal, oldVal) {
// 数值变化
this.updateValue()
},
week(newVal, oldVal) {
// console.info('new week: ' + newVal)
this.updateValue()
}
},
methods: {
updateValue() {
this.$emit('change', this.disableChoice ? '?' : this.value_c)
}
},
created() {
this.DEFAULT_VALUE = '*'
this.minValue = 1
this.maxValue = 31
this.valueRange.start = 1
this.valueRange.end = 31
this.valueLoop.start = 1
this.valueLoop.interval = 1
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,67 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disabled">每时</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disabled">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disabled">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.start"/>
时开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'minute',
mixins: [mixin],
data() {
return {}
},
watch: {
value_c(newVal, oldVal) {
this.$emit('change', newVal)
}
},
created() {
this.DEFAULT_VALUE = '*'
this.minValue = 0
this.maxValue = 23
this.valueRange.start = 0
this.valueRange.end = 23
this.valueLoop.start = 0
this.valueLoop.interval = 1
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,67 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disabled">每分</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disabled">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disabled">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.start"/>
分开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'minute',
mixins: [mixin],
data() {
return {}
},
watch: {
value_c(newVal, oldVal) {
this.$emit('change', newVal)
}
},
created() {
this.DEFAULT_VALUE = '*'
this.minValue = 0
this.maxValue = 59
this.valueRange.start = 0
this.valueRange.end = 59
this.valueLoop.start = 0
this.valueLoop.interval = 1
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,151 @@
// 主要用于日和星期的互斥使用
const TYPE_NOT_SET = 'TYPE_NOT_SET'
const TYPE_EVERY = 'TYPE_EVERY'
const TYPE_RANGE = 'TYPE_RANGE'
const TYPE_LOOP = 'TYPE_LOOP'
const TYPE_WORK = 'TYPE_WORK'
const TYPE_LAST = 'TYPE_LAST'
const TYPE_SPECIFY = 'TYPE_SPECIFY'
const DEFAULT_VALUE = '?'
export default {
model: {
prop: 'prop',
event: 'change'
},
props: {
prop: {
type: String,
default: DEFAULT_VALUE
},
disabled: {
type: Boolean,
default: false
}
},
data () {
const type = TYPE_EVERY
return {
DEFAULT_VALUE,
// 类型
type,
// 启用日或者星期互斥用
TYPE_NOT_SET,
TYPE_EVERY,
TYPE_RANGE,
TYPE_LOOP,
TYPE_WORK,
TYPE_LAST,
TYPE_SPECIFY,
// 对于不同的类型,所定义的值也有所不同
valueRange: {
start: 0,
end: 0
},
valueLoop: {
start: 0,
interval: 1
},
valueWeek: {
start: 0,
end: 0
},
valueList: [],
valueWork: 1,
maxValue: 0,
minValue: 0
}
},
watch: {
prop (newVal, oldVal) {
if (newVal === this.value_c) {
// console.info('skip ' + newVal)
return
}
this.parseProp(newVal)
}
},
computed: {
value_c () {
let result = []
switch (this.type) {
case TYPE_NOT_SET:
result.push('?')
break
case TYPE_EVERY:
result.push('*')
break
case TYPE_RANGE:
result.push(`${this.valueRange.start}-${this.valueRange.end}`)
break
case TYPE_LOOP:
result.push(`${this.valueLoop.start}/${this.valueLoop.interval}`)
break
case TYPE_WORK:
result.push(`${this.valueWork}W`)
break
case TYPE_LAST:
result.push('L')
break
case TYPE_SPECIFY:
result.push(this.valueList.join(','))
break
default:
result.push(this.DEFAULT_VALUE)
break
}
return result.length > 0 ? result.join('') : this.DEFAULT_VALUE
}
},
methods: {
parseProp (value) {
if (value === this.value_c) {
// console.info('same ' + value)
return
}
if (typeof (this.preProcessProp) === 'function') {
value = this.preProcessProp(value)
}
try {
if (!value || value === this.DEFAULT_VALUE) {
this.type = TYPE_EVERY
} else if (value.indexOf('?') >= 0) {
this.type = TYPE_NOT_SET
} else if (value.indexOf('-') >= 0) {
this.type = TYPE_RANGE
const values = value.split('-')
if (values.length >= 2) {
this.valueRange.start = parseInt(values[0])
this.valueRange.end = parseInt(values[1])
}
} else if (value.indexOf('/') >= 0) {
this.type = TYPE_LOOP
const values = value.split('/')
if (values.length >= 2) {
this.valueLoop.start = value[0] === '*' ? 0 : parseInt(values[0])
this.valueLoop.interval = parseInt(values[1])
}
} else if (value.indexOf('W') >= 0) {
this.type = TYPE_WORK
const values = value.split('W')
if (!values[0] && !isNaN(values[0])) {
this.valueWork = parseInt(values[0])
}
} else if (value.indexOf('L') >= 0) {
this.type = TYPE_LAST
const values = value.split('L')
this.valueLast = parseInt(values[0])
} else if (value.indexOf(',') >= 0 || !isNaN(value)) {
this.type = TYPE_SPECIFY
this.valueList = value.split(',').map(item => parseInt(item))
} else {
this.type = TYPE_EVERY
}
} catch (e) {
// console.info(e)
this.type = TYPE_EVERY
}
}
}
}

View File

@ -0,0 +1,35 @@
.config-list {
text-align: left;
margin: 0 10px 10px 10px;
}
.item {
margin-top: 5px;
}
.choice {
padding: 5px 8px;
}
.w60 {
width: 60px;
}
.w80 {
width: 80px;
}
.list {
margin: 0 20px;
}
.list-check-item {
padding: 1px 3px;
width: 4em;
}
.tip-info {
color: #999
}

View File

@ -0,0 +1,67 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disabled">每月</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disabled">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disabled">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.start"/>
月开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'month',
mixins: [mixin],
data() {
return {}
},
watch: {
value_c(newVal, oldVal) {
this.$emit('change', newVal)
}
},
created() {
this.DEFAULT_VALUE = '*'
this.minValue = 1
this.maxValue = 12
this.valueRange.start = 1
this.valueRange.end = 12
this.valueLoop.start = 1
this.valueLoop.interval = 1
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,68 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disabled">每秒</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disabled">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disabled">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.start"/>
秒开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'second',
mixins: [mixin],
data() {
return {}
},
watch: {
value_c(newVal, oldVal) {
this.$emit('change', newVal)
}
},
created() {
this.DEFAULT_VALUE = '*'
this.minValue = 0
this.maxValue = 59
this.valueRange.start = 0
this.valueRange.end = 59
this.valueLoop.start = 0
this.valueLoop.interval = 1
// console.info('created')
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,117 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_NOT_SET" class="choice" :disabled="disableChoice">不设置</a-radio>
<span class="tip-info">日和周只能设置其中之一</span>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disableChoice">区间</a-radio>
<a-select v-model="valueRange.start" class="w80" :disabled="type!==TYPE_RANGE || disableChoice">
<template v-for="(v, k) of WEEK_MAP">
<a-select-option :value="v">{{k}}</a-select-option>
</template>
</a-select>
<a-select v-model="valueRange.end" class="w80" :disabled="type!==TYPE_RANGE || disableChoice">
<template v-for="(v, k) of WEEK_MAP">
<a-select-option :value="v">{{k}}</a-select-option>
</template>
</a-select>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disableChoice">循环</a-radio>
<a-select v-model="valueLoop.start" class="w80" :disabled="type!==TYPE_LOOP || disableChoice">
<template v-for="(v, k) of WEEK_MAP">
<a-select-option :value="v">{{k}}</a-select-option>
</template>
</a-select>
开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disableChoice" :max="maxValue" :min="minValue" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
<div class="item">
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
import { replaceWeekName, WEEK_MAP_EN } from './const.js'
const WEEK_MAP = {
'周日': 0,
'周一': 1,
'周二': 2,
'周三': 3,
'周四': 4,
'周五': 5,
'周六': 6
}
export default {
name: 'week',
mixins: [mixin],
props: {
day: {
type: String,
default: '*'
}
},
data() {
return {
WEEK_MAP,
WEEK_MAP_EN
}
},
computed: {
disableChoice() {
return (this.day && this.day !== '?') || this.disabled
}
},
watch: {
value_c(newVal, oldVal) {
// 如果设置日,那么星期就直接不设置
this.updateValue()
},
day(newVal) {
// console.info('new day: ' + newVal)
this.updateValue()
}
},
methods: {
updateValue() {
this.$emit('change', this.disableChoice ? '?' : this.value_c)
},
preProcessProp(c) {
return replaceWeekName(c)
}
},
created() {
this.DEFAULT_VALUE = '*'
// 0,7表示周日 1表示周一
this.minValue = 0
this.maxValue = 6
this.valueRange.start = 0
this.valueRange.end = 6
this.valueLoop.start = 2
this.valueLoop.interval = 1
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,60 @@
<template>
<div class="config-list">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_EVERY" class="choice" :disabled="disabled">每年</a-radio>
</div>
<div class="item">
<a-radio value="TYPE_RANGE" class="choice" :disabled="disabled">区间</a-radio>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :min="0" :precision="0" class="w60" v-model="valueRange.start"/>
<a-input-number :disabled="type!==TYPE_RANGE || disabled" :min="1" :precision="0" class="w60" v-model="valueRange.end"/>
</div>
<div class="item">
<a-radio value="TYPE_LOOP" class="choice" :disabled="disabled">循环</a-radio>
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :min="0" :precision="0" class="w60" v-model="valueLoop.start"/>
年开始,间隔
<a-input-number :disabled="type!==TYPE_LOOP || disabled" :min="1" :precision="0" class="w60" v-model="valueLoop.interval"/>
</div>
</a-radio-group>
</div>
</template>
<script>
import mixin from './mixin'
export default {
name: 'year',
mixins: [mixin],
data() {
return {}
},
watch: {
value_c(newVal, oldVal) {
// console.info('change:' + newVal)
this.$emit('change', newVal)
}
},
created() {
const nowYear = (new Date()).getFullYear()
this.DEFAULT_VALUE = '*'
this.minValue = 0
this.maxValue = 0
this.valueRange.start = nowYear
this.valueRange.end = nowYear + 100
this.valueLoop.start = nowYear
this.valueLoop.interval = 1
// console.info('created')
this.parseProp(this.prop)
}
}
</script>
<style lang="less" scoped>
@import "mixin.less";
</style>

View File

@ -0,0 +1,51 @@
import CronParser from 'cron-parser'
import { replaceWeekName } from './tabs/const'
export default (rule, value, callback) => {
// 没填写就不校验
if (!value) {
callback()
return true
}
const values = value.split(' ').filter(item => !!item)
if (values.length > 7) {
callback(new Error('Cron表达式最多7项'))
return false
}
// 检查第7项
let e = value
if (values.length === 7) {
const year = replaceWeekName(values[6])
if (year !== '*' && year !== '?') {
let yearValues = []
if (year.indexOf('-') >= 0) {
yearValues = year.split('-')
} else if (year.indexOf('/')) {
yearValues = year.split('/')
} else {
yearValues = [year]
}
// console.info(yearValues)
// 判断是否都是数字
const checkYear = yearValues.some(item => isNaN(item))
if (checkYear) {
callback(new Error('Cron表达式参数[]错误' + year))
return false
}
}
// 取其中的前六项
e = values.slice(0, 6).join(' ')
}
// 6位 没有年
// 5位没有秒、年
let result = true
try {
const iter = CronParser.parseExpression(e)
iter.next()
callback()
} catch (e) {
callback(new Error('Cron表达式错误' + e))
result = false
}
return result
}

View File

@ -350,7 +350,7 @@
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<a-upload
name="file"
:data="{'isup':1}"
:data="{'isup':1, ...(col.data||{})}"
:multiple="false"
:action="col.action"
:headers="uploadGetHeaders(row,col)"
@ -381,6 +381,8 @@
:dest-fields="col.destFields"
:code="col.popupCode"
:groupId="caseId"
:param="col.param"
:sorter="col.sorter"
@input="(value,others)=>popupCallback(value,others,id,row,col,rowIndex)"
/>
<span
@ -595,6 +597,33 @@
</template>
<!-- select搜索 -end -->
<!-- select异步搜索 -begin -->
<template v-else-if="col.type === formTypes.sel_search_async">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<j-search-select-tag
v-if="isEditRow(row, col)"
:id="id"
:key="i"
:value="searchSelectAsyncValues[id]"
:placeholder="replaceProps(col, col.placeholder)"
:dict="col.dict"
:async="true"
:getPopupContainer="getParentContainer"
v-bind="buildProps(row,col)"
style="width: 100%;"
@change="(v)=>handleSearchSelectAsyncChange(v,id,row,col)"
>
</j-search-select-tag>
<span
v-else
class="j-td-span no-edit"
:class="{disabled: buildProps(row,col).disabled}"
@click="handleEditRow(row, col)"
>{{ searchSelectAsyncValues[id] }}</span>
</a-tooltip>
</template>
<!-- select异步搜索 -end -->
<div v-else-if="col.type === formTypes.slot" :key="i">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<slot
@ -615,7 +644,7 @@
</div>
<!-- else (normal) -->
<span v-else :key="i" v-bind="buildProps(row,col)">{{ inputValues[rowIndex][col.key] }}</span>
<span class="comp-normal" v-else :key="i" :title="inputValues[rowIndex][col.key]" v-bind="buildProps(row,col)">{{ inputValues[rowIndex][col.key] }}</span>
</template>
</div>
</div>
@ -672,13 +701,13 @@
import Draggable from 'vuedraggable'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { FormTypes, VALIDATE_NO_PASSED } from '@/utils/JEditableTableUtil'
import { cloneObject, randomString, randomNumber, getEventPath } from '@/utils/util'
import { cloneObject, getEventPath, randomNumber, randomString } from '@/utils/util'
import JDate from '@/components/jeecg/JDate'
import { filterDictText, initDictOptions } from '@/components/dict/JDictSelectUtil'
import { getFileAccessHttpUrl } from '@/api/manage';
import { getFileAccessHttpUrl } from '@/api/manage'
import JInputPop from '@/components/jeecg/minipop/JInputPop'
import JFilePop from '@/components/jeecg/minipop/JFilePop'
import { getNoAuthCols } from "@/utils/authFilter"
import { getNoAuthCols } from '@/utils/authFilter'
// 行高,需要在实例加载完成前用到
let rowHeight = 61
@ -814,6 +843,7 @@
metaCheckboxValues: {},
multiSelectValues: {},
searchSelectValues: {},
searchSelectAsyncValues: {},
// 绑定左侧选择框已选择的id
selectedRowIds: [],
// 存储被删除行的id
@ -1048,6 +1078,10 @@
this.inputValues = []
this.rows = []
this.deleteIds = []
this.selectedRowIds = []
this.tooltips = {}
this.notPassedIds = []
// 重置values
this.selectValues = {}
this.checkboxValues = {}
this.jdateValues = {}
@ -1055,14 +1089,16 @@
this.departCompValues = {}
this.userCompValues = {}
this.slotValues = {}
this.selectedRowIds = []
this.tooltips = {}
this.notPassedIds = []
this.uploadValues = []
this.popupValues = []
this.radioValues = []
this.multiSelectValues = []
this.searchSelectValues = []
//update-begin-author:shunjlei date:20210415 for:类型赋值错误
this.uploadValues = {}
this.popupValues = {}
this.radioValues = {}
this.multiSelectValues = {}
this.searchSelectValues = {}
this.searchSelectAsyncValues = {}
//update-end-author:shunjlei date:20210415 for:类型赋值错误
// 重置滚动条
this.scrollTop = 0
this.$nextTick(() => {
this.getElement('tbody').scrollTop = 0
@ -1136,6 +1172,7 @@
let radioValues = { ...this.radioValues }
let multiSelectValues = { ...this.multiSelectValues }
let searchSelectValues = { ...this.searchSelectValues }
let searchSelectAsyncValues = { ...this.searchSelectAsyncValues }
// 禁用行的id
let disabledRowIds = (this.disabledRowIds || [])
dataSource.forEach((data, newValueIndex) => {
@ -1225,6 +1262,8 @@
radioValues[inputId] = sourceValue
} else if (column.type === FormTypes.sel_search) {
searchSelectValues[inputId] = sourceValue
} else if (column.type === FormTypes.sel_search_async) {
searchSelectAsyncValues[inputId] = sourceValue
} else if (column.type === FormTypes.list_multi) {
if (typeof sourceValue === 'string' && sourceValue.length > 0) {
multiSelectValues[inputId] = sourceValue.split(',')
@ -1245,6 +1284,8 @@
status: 'done',
path: sourceValue
}
} else {
uploadValues[inputId] = null
}
} else {
value[column.key] = sourceValue
@ -1309,6 +1350,7 @@
this.radioValues = radioValues
this.multiSelectValues = multiSelectValues
this.searchSelectValues = searchSelectValues
this.searchSelectAsyncValues = searchSelectAsyncValues
// 重新计算所有统计列
this.recalcAllStatisticsColumns()
// 更新到 dom
@ -1543,6 +1585,8 @@
value[column.key] = this.radioValues[inputId]
} else if (column.type === FormTypes.sel_search) {
value[column.key] = this.searchSelectValues[inputId]
} else if (column.type === FormTypes.sel_search_async) {
value[column.key] = this.searchSelectAsyncValues[inputId]
} else if (column.type === FormTypes.list_multi) {
if (!this.multiSelectValues[inputId] || this.multiSelectValues[inputId].length === 0) {
value[column.key] = ''
@ -1671,6 +1715,7 @@
radioValues: this.radioValues,
multiSelectValues: this.multiSelectValues,
searchSelectValues: this.searchSelectValues,
searchSelectAsyncValues: this.searchSelectAsyncValues,
})
},
/** 设置某行某列的值 */
@ -1736,6 +1781,8 @@
edited = this.setOneValue(this.multiSelectValues, modelKey, newValue, true)
} else if (column.type === FormTypes.sel_search) {
edited = this.setOneValue(this.searchSelectValues, modelKey, newValue)
} else if (column.type === FormTypes.sel_search_async) {
edited = this.setOneValue(this.searchSelectAsyncValues, modelKey, newValue)
} else {
edited = false
}
@ -2731,6 +2778,11 @@
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
this.elemValueChange(FormTypes.sel_search, row, column, value)
},
handleSearchSelectAsyncChange(value, id, row, column) {
this.searchSelectAsyncValues = this.bindValuesChange(value, id, 'searchSelectAsyncValues')
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
this.elemValueChange(FormTypes.sel_search_async, row, column, value)
},
filterOption(input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
@ -2992,6 +3044,8 @@
border-bottom: @border;
transition: background-color 300ms;
width: 100%;
height: 61px;
overflow: hidden;
position: absolute;
left: 0;
z-index: 10;
@ -3101,6 +3155,12 @@
}
}
.comp-normal {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.j-td-span {
position: relative;
padding: 4px 11px;

View File

@ -6,6 +6,13 @@
:confirmLoading="uploading"
@cancel="handleClose">
<div style="margin: 0px 0px 5px 1px" v-if="online">
<span style="display: inline-block;height: 32px;line-height: 32px;vertical-align: middle;">是否开启校验:</span>
<span style="display: inline-block;height: 32px;margin-left: 6px">
<a-switch :checked="validateStatus==1" @change="handleChangeValidateStatus" checked-children="" un-checked-children="" size="small"/>
</span>
</div>
<a-upload
name="file"
:multiple="true"
@ -47,6 +54,12 @@
type: String,
default: '',
required: false
},
//是否online导入
online:{
type: Boolean,
default: false,
required: false
}
},
data(){
@ -55,7 +68,8 @@
uploading:false,
fileList:[],
uploadAction:'',
foreignKeys:''
foreignKeys:'',
validateStatus: 0
}
},
watch: {
@ -78,6 +92,7 @@
this.uploading = false
this.visible = true
this.foreignKeys = arg;
this.validateStatus = 0
},
handleRemove(file) {
const index = this.fileList.indexOf(file);
@ -98,6 +113,9 @@
if(this.foreignKeys && this.foreignKeys.length>0){
formData.append('foreignKeys',this.foreignKeys);
}
if(this.online==true){
formData.append('validateStatus',this.validateStatus);
}
fileList.forEach((file) => {
formData.append('files[]', file);
});
@ -105,14 +123,41 @@
postAction(this.uploadAction, formData).then((res) => {
this.uploading = false
if(res.success){
this.$message.success(res.message)
if(res.code == 201){
this.errorTip(res.message, res.result)
}else{
this.$message.success(res.message)
}
this.visible=false
this.$emit('ok')
}else{
this.$message.warning(res.message)
}
})
}
},
// 是否开启校验 开关改变事件
handleChangeValidateStatus(checked){
this.validateStatus = checked==true?1:0
},
// 错误信息提示
errorTip(tipMessage, fileUrl) {
const h = this.$createElement;
let href = window._CONFIG['domianURL'] + fileUrl
this.$warning({
title: '导入成功,但是有错误数据!',
content: h('div', {}, [
h('div', tipMessage),
h('span', '具体详情请 '),
h('a', {
attrs: {
href: href,
target: '_blank'
},
},'点击下载'),
]),
onOk() {},
});
},
}
}

View File

@ -10,6 +10,7 @@
ref="jPopupOnlReport"
:code="code"
:multi="multi"
:sorter="sorter"
:groupId="uniqGroupId"
:param="param"
@ok="callBack"
@ -47,6 +48,11 @@
default: '',
required: false
},
/** 排序列,指定要排序的列,使用方式:列名=desc|asc */
sorter: {
type: String,
default: ''
},
width: {
type: Number,
default: 1200,

View File

@ -12,7 +12,7 @@
<a-upload
name="file"
:multiple="true"
:multiple="multiple"
:action="uploadAction"
:headers="headers"
:data="{'biz':bizPath}"
@ -135,6 +135,10 @@
required:false,
default: true
},
multiple: {
type: Boolean,
default: true
},
},
watch:{
value:{

View File

@ -13,7 +13,7 @@ import JVxeDetailsModal from './JVxeDetailsModal'
import JVxePagination from './JVxePagination'
import { cloneObject, getVmParentByName, pushIfNotExist, randomString, simpleDebounce } from '@/utils/util'
import { UtilTools } from 'vxe-table/packages/tools/src/utils'
import { getNoAuthCols } from "@/utils/authFilter"
import { getNoAuthCols } from '@/utils/authFilter'
export default {
name: 'JVxeTable',
@ -95,6 +95,9 @@ export default {
// 是否异步删除行,如果你要实现异步删除,那么需要把这个选项开启,
// 在remove事件里调用confirmRemove方法才会真正删除除非删除的全是新增的行
asyncRemove: PropTypes.bool.def(false),
// 是否一直显示组件如果为false则只有点击的时候才出现组件
// 注:该参数不能动态修改;如果行、列字段多的情况下,会根据机器性能造成不同程度的卡顿。
alwaysEdit: PropTypes.bool.def(false),
},
data() {
return {
@ -354,7 +357,7 @@ export default {
col.visible = false
} else if (enhanced.switches.editRender) {
renderName = 'editRender'
renderOptions.type = enhanced.switches.visible ? 'visible' : 'default'
renderOptions.type = (enhanced.switches.visible || this.alwaysEdit) ? 'visible' : 'default'
}
} else {
renderOptions.name = JVXETypes._prefix + JVXETypes.normal
@ -1076,7 +1079,7 @@ export default {
// 添加默认值
xTable.tableFullColumn.forEach(column => {
let col = column.own
if (record[col.key] == null || record[col.key] === '') {
if (col.key && (record[col.key] == null || record[col.key] === '')) {
// 设置默认值
let createValue = getEnhancedMixins(col.$type || col.type, 'createValue')
record[col.key] = createValue({row: record, column, $table: xTable})

View File

@ -55,7 +55,9 @@
// 【组件增强】注释详见JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event)),
editActived(event) {
dispatchEvent.call(this, event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event))
},
},
}
}

View File

@ -34,7 +34,7 @@
return this.rowIndex === 0
},
disabledMoveDown() {
return this.rowIndex === (this.rows.length - 1)
return this.rowIndex === (this.fullDataLength - 1)
},
},
methods: {

View File

@ -116,7 +116,9 @@
// 【组件增强】注释详见JVxeCellMixins.js
enhanced: {
aopEvents: {
editActived: event => dispatchEvent(event, 'ant-select'),
editActived(event) {
dispatchEvent.call(this, event, 'ant-select')
},
},
translate: {enabled: true},
getValue(value) {

View File

@ -23,7 +23,9 @@
autofocus: '.ant-input',
},
aopEvents: {
editActived: event => dispatchEvent(event, 'anticon-fullscreen'),
editActived(event) {
dispatchEvent.call(this, event, 'anticon-fullscreen')
},
},
},
}

View File

@ -36,6 +36,9 @@ export default {
rows() {
return this.params.data
},
fullDataLength() {
return this.params.$table.tableFullData.length
},
rowIndex() {
return this.params.rowIndex
},
@ -291,6 +294,10 @@ export function vModel(value, row, property) {
/** 模拟触发事件 */
export function dispatchEvent({cell, $event}, className, handler) {
// alwaysEdit 下不模拟触发事件,否者会导致触发两次
if (this && this.alwaysEdit) {
return
}
window.setTimeout(() => {
let element = cell.getElementsByClassName(className)
if (element && element.length > 0) {
@ -298,9 +305,7 @@ export function dispatchEvent({cell, $event}, className, handler) {
handler(element[0])
} else {
// 模拟触发点击事件
console.log($event)
if($event){
console.log("$event===>",$event)
element[0].dispatchEvent($event)
}
}

View File

@ -26,6 +26,7 @@ import JSlider from './JSlider.vue'
import JSwitch from './JSwitch.vue'
import JTime from './JTime.vue'
import JTreeTable from './JTreeTable.vue'
import JEasyCron from "@/components/jeecg/JEasyCron";
//jeecgbiz
import JSelectDepart from '../jeecgbiz/JSelectDepart.vue'
@ -71,5 +72,6 @@ export default {
Vue.component('JSelectPosition', JSelectPosition)
Vue.component('JSelectRole', JSelectRole)
Vue.component('JSelectUserByDep', JSelectUserByDep)
Vue.component(JEasyCron.name, JEasyCron)
}
}

View File

@ -1,6 +1,6 @@
<template>
<a-modal
title="corn表达式"
title="cron表达式"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"

View File

@ -78,7 +78,7 @@
const MODAL_WIDTH = 1200;
export default {
name: 'JPopupOnlReport',
props: ['multi', 'code', 'groupId', 'param'],
props: ['multi', 'code', 'sorter', 'groupId', 'param'],
components:{
},
data(){
@ -122,8 +122,9 @@
cgRpConfigId:"",
modalWidth:MODAL_WIDTH,
tableScroll:{x:true},
dynamicParam:{}
dynamicParam:{},
// 排序字段,默认无排序
iSorter: null,
}
},
mounted() {
@ -139,7 +140,28 @@
this.dynamicParamHandler()
this.loadData();
},
}
},
sorter: {
immediate: true,
handler() {
if (this.sorter) {
let arr = this.sorter.split('=')
if (arr.length === 2 && ['asc', 'desc'].includes(arr[1].toLowerCase())) {
this.iSorter = {column: arr[0], order: arr[1].toLowerCase()}
// 排序字段受控
this.table.columns.forEach(col => {
if (col.dataIndex === this.iSorter.column) {
this.$set(col, 'sortOrder', this.iSorter.order === 'asc' ? 'ascend' : 'descend')
} else {
this.$set(col, 'sortOrder', false)
}
})
} else {
console.warn('【JPopup】sorter参数不合法')
}
}
},
},
},
computed:{
showSearchFlag(){
@ -167,6 +189,10 @@
return filterMultiDictText(this.dictOptions[dictCode], text+"");
}
}
// 排序字段受控
if (this.iSorter && currColumns[a].dataIndex === this.iSorter.column) {
currColumns[a].sortOrder = this.iSorter.order === 'asc' ? 'ascend' : 'descend'
}
}
this.table.columns = [...currColumns]
this.initQueryInfo()
@ -253,7 +279,7 @@
paramTarget['self_'+key] = this.dynamicParam[key]
})
}
let param = Object.assign(paramTarget, this.queryParam, this.sorter);
let param = Object.assign(paramTarget, this.queryParam, this.iSorter);
param.pageNo = this.table.pagination.current;
param.pageSize = this.table.pagination.pageSize;
return filterObj(param);
@ -288,8 +314,18 @@
handleChangeInTable(pagination, filters, sorter) {
//分页、排序、筛选变化时触发
if (Object.keys(sorter).length > 0) {
this.sorter.column = sorter.field
this.sorter.order = 'ascend' == sorter.order ? 'asc' : 'desc'
this.iSorter = {
column: sorter.field,
order: 'ascend' === sorter.order ? 'asc' : 'desc'
}
// 排序字段受控
this.table.columns.forEach(col => {
if (col.dataIndex === sorter.field) {
this.$set(col, 'sortOrder',sorter.order)
} else {
this.$set(col, 'sortOrder', false)
}
})
}
this.table.pagination = pagination
this.loadData()

View File

@ -56,8 +56,9 @@
</template>
<script>
import {filterObj} from '@/utils/util'
import { pushIfNotExist, filterObj } from '@/utils/util'
import {queryDepartTreeList, getUserList, queryUserByDepId} from '@/api/api'
import { getAction } from '@/api/manage'
export default {
name: 'JSelectUserByDepModal',
@ -106,6 +107,7 @@
],
scrollTrigger: {},
dataSource: [],
selectionRows: [],
selectedRowKeys: [],
selectUserRows: [],
selectUserIds: [],
@ -163,11 +165,13 @@
pageSize: values.length
}).then((res) => {
if (res.success) {
this.selectionRows = []
let selectedRowKeys = []
let realNames = []
res.result.records.forEach(user => {
realNames.push(user['realname'])
selectedRowKeys.push(user['id'])
this.selectionRows.push(user)
})
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', realNames.join(','))
@ -182,20 +186,16 @@
if (arg === 1) {
this.ipagination.current = 1;
}
if (this.selectedDepIds && this.selectedDepIds.length > 0) {
await this.initQueryUserByDepId(this.selectedDepIds)
} else {
this.loading = true
let params = this.getQueryParams()//查询条件
await getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records
this.ipagination.total = res.result.total
}
}).finally(() => {
this.loading = false
})
}
let params = this.getQueryParams()//查询条件
this.loading = true
getAction('/sys/user/queryUserComponentData', params).then(res=>{
if (res.success) {
this.dataSource = res.result.records
this.ipagination.total = res.result.total
}
}).finally(() => {
this.loading = false
})
},
// 触发屏幕自适应
resetScreenSize() {
@ -218,6 +218,7 @@
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
param.departId = this.selectedDepIds.join(',')
return filterObj(param);
},
getQueryField() {
@ -229,13 +230,13 @@
},
searchReset(num) {
let that = this;
that.selectedRowKeys = [];
that.selectUserIds = [];
that.selectedDepIds = [];
if (num !== 0) {
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.selectUserIds = [];
that.selectedDepIds = [];
},
close() {
this.searchReset(0);
@ -258,30 +259,27 @@
that.close();
},
//获取选择用户信息
getSelectUserRows(rowId) {
let dataSource = this.dataSource;
let userIds = "";
this.selectUserRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectUserRows.push(dataSource[i]);
userIds = userIds + "," + dataSource[i].username
getSelectUserRows() {
this.selectUserRows = []
for (let row of this.selectionRows) {
if (this.selectedRowKeys.includes(row.id)) {
this.selectUserRows.push(row)
}
}
this.selectUserIds = userIds.substring(1);
this.selectUserIds = this.selectUserRows.map(row => row.username).join(',')
},
// 点击树节点,筛选出对应的用户
onDepSelect(selectedDepIds) {
if (selectedDepIds[0] != null) {
this.initQueryUserByDepId(selectedDepIds); // 调用方法根据选选择的id查询用户信息
if (this.selectedDepIds[0] !== selectedDepIds[0]) {
this.selectedDepIds = [selectedDepIds[0]];
}
this.loadData(1);
}
},
onSelectChange(selectedRowKeys, selectionRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectionRows;
selectionRows.forEach(row => pushIfNotExist(this.selectionRows, row, 'id'))
},
onSearch() {
this.loadData(1);

View File

@ -0,0 +1,214 @@
<template>
<span v-if="syncToApp || syncToLocal">
<j-third-app-dropdown v-if="enabledTypes.wechatEnterprise" type="wechatEnterprise" name="企微" v-bind="bindAttrs" v-on="bindEvents"/>
<j-third-app-dropdown v-if="enabledTypes.dingtalk" type="dingtalk" name="钉钉" v-bind="bindAttrs" v-on="bindEvents"/>
</span>
<span v-else>未设置任何同步方向</span>
</template>
<script>
import { getAction } from '@/api/manage'
import { cloneObject } from '@/utils/util'
import JThirdAppDropdown from './JThirdAppDropdown'
const backEndUrl = {
// 获取启用的第三方App
getEnabledType: '/sys/thirdApp/getEnabledType',
// 企业微信
wechatEnterprise: {
user: '/sys/thirdApp/sync/wechatEnterprise/user',
depart: '/sys/thirdApp/sync/wechatEnterprise/depart',
},
// 钉钉
dingtalk: {
user: '/sys/thirdApp/sync/dingtalk/user',
depart: '/sys/thirdApp/sync/dingtalk/depart',
}
}
export default {
name: 'JThirdAppButton',
components: {JThirdAppDropdown},
props: {
// 同步类型,可以是 user、depart
bizType: {
type: String,
required: true,
},
// 是否允许同步到第三方APP
syncToApp: Boolean,
// 是否允许第三方APP同步到本地
syncToLocal: Boolean,
// 选择的行
selectedRowKeys: Array,
},
data() {
return {
enabledTypes: {},
attrs: {
dingtalk: {},
},
}
},
computed: {
bindAttrs() {
return {
syncToApp: this.syncToApp,
syncToLocal: this.syncToLocal
}
},
bindEvents() {
return {
'to-app': this.onToApp,
'to-local': this.onToLocal,
}
},
},
created() {
this.loadEnabledTypes()
},
methods: {
handleMenuClick() {
console.log(arguments)
},
onToApp(e) {
this.doSync(e.type, '/toApp')
},
onToLocal(e) {
this.doSync(e.type, '/toLocal')
},
// 获取启用的第三方App
async loadEnabledTypes() {
this.enabledTypes = await loadEnabledTypes()
},
// 开始同步第三方App
doSync(type, direction) {
let urls = backEndUrl[type]
if (!(urls && urls[this.bizType])) {
console.warn('配置出错')
return
}
let url = urls[this.bizType] + direction
let selectedRowKeys = this.selectedRowKeys
let content = '确定要开始同步全部数据吗可能花费较长时间'
if (Array.isArray(selectedRowKeys) && selectedRowKeys.length > 0) {
content = `确定要开始同步这 ${selectedRowKeys.length} 项吗`
} else {
selectedRowKeys = []
}
return new Promise((resolve, reject) => {
let model = this.$confirm({
title: '同步',
content,
onOk: () => {
model.update({
keyboard: false,
okText: '同步中',
cancelButtonProps: {props: {disabled: true}}
})
return getAction(url, {
ids: selectedRowKeys.join(',')
}).then(res => {
let options = null
if (res.result) {
options = {
width: 600,
title: res.message,
content: (h) => {
let nodes
let successInfo = [
`成功信息如下`,
this.renderTextarea(h, res.result.successInfo.map((v, i) => `${i + 1}. ${v}`).join('\n')),
]
if (res.success) {
nodes = [
...successInfo,
h('br'),
`无失败信息`,
]
} else {
nodes = [
`失败信息如下`,
this.renderTextarea(h, res.result.failInfo.map((v, i) => `${i + 1}. ${v}`).join('\n')),
h('br'),
...successInfo,
]
}
return nodes
}
}
}
if (res.success) {
if (options != null) {
this.$success(options)
} else {
this.$message.success(res.message)
}
this.$emit('sync-ok')
} else {
if (options != null) {
this.$warning(options)
} else {
this.$message.warning(res.message)
}
this.$emit('sync-error')
}
}).catch(() => model.destroy()).finally(() => {
resolve()
this.$emit('sync-finally', {
type,
direction,
isToApp: direction === '/toApp',
isToLocal: direction === '/toLocal',
})
})
},
onCancel() {
resolve()
},
})
})
},
renderTextarea(h, value) {
return h('a-textarea', {
props: {
value: value,
readOnly: true,
autosize: {minRows: 5, maxRows: 10},
},
style: {
// 关闭textarea的自动换行使其可以左右滚动
whiteSpace: 'pre',
overflow: 'auto',
}
})
}
},
}
// 启用了哪些第三方App在此缓存
let enabledTypes = null
// 获取启用的第三方App
export async function loadEnabledTypes() {
// 获取缓存
if (enabledTypes != null) {
return cloneObject(enabledTypes)
} else {
let {success, result} = await getAction(backEndUrl.getEnabledType)
if (success) {
// 在此缓存
enabledTypes = cloneObject(result)
return result
} else {
console.warn('getEnabledType查询失败', res)
}
}
return {}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,34 @@
<template>
<a-dropdown v-if="syncToApp && syncToLocal">
<a-button type="primary" icon="sync">同步{{name}}</a-button>
<a-menu slot="overlay" @click="handleMenuClick">
<a-menu-item v-if="syncToApp" key="to-app">同步到{{name}}</a-menu-item>
<a-menu-item v-if="syncToLocal" key="to-local">同步到本地</a-menu-item>
</a-menu>
</a-dropdown>
<a-button v-else-if="syncToApp" type="primary" icon="sync" @click="handleMenuClick({key:'to-app'})">同步{{name}}</a-button>
<a-button v-else type="primary" icon="sync" @click="handleMenuClick({key:'to-local'})">同步{{name}}到本地</a-button>
</template>
<script>
/* JThirdAppButton 的子组件,不可单独使用 */
export default {
name: 'JThirdAppDropdown',
props: {
type: String,
name: String,
syncToApp: Boolean,
syncToLocal: Boolean,
},
methods: {
handleMenuClick(event) {
this.$emit(event.key, {type: this.type})
},
},
}
</script>
<style scoped>
</style>

View File

@ -38,16 +38,20 @@
//url = "http://www.baidu.com"
console.log("------url------"+url)
if (url !== null && url !== undefined) {
this.url = url;
//-----------------------------------------------------------------------------------------
//url支持通过 ${token}方式传递当前登录TOKEN
let tokenStr = "${token}";
if(url.indexOf(tokenStr)!=-1) {
let token = Vue.ls.get(ACCESS_TOKEN);
this.url = url.replace(tokenStr, token);
} else {
this.url = url
}
//-----------------------------------------------------------------------------------------
/*update_begin author:wuxianquan date:20190908 for:判断打开方式新窗口打开时this.$route.meta.internalOrExternal==true */
if(this.$route.meta.internalOrExternal != undefined && this.$route.meta.internalOrExternal==true){
this.closeCurrent();
//外部url加入token
let tokenStr = "${token}";
if(url.indexOf(tokenStr)!=-1){
let token = Vue.ls.get(ACCESS_TOKEN);
this.url = url.replace(tokenStr,token);
}
window.open(this.url);
}
/*update_end author:wuxianquan date:20190908 for:判断打开方式新窗口打开时this.$route.meta.internalOrExternal==true */

View File

@ -36,9 +36,10 @@
import Contextmenu from '@/components/menu/Contextmenu'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { triggerWindowResizeEvent } from '@/utils/util'
const indexKey = '/dashboard/analysis'
import Vue from 'vue'
import { CACHE_INCLUDED_ROUTES } from "@/store/mutation-types"
import { CACHE_INCLUDED_ROUTES } from '@/store/mutation-types'
const indexKey = '/dashboard/analysis'
export default {
name: 'TabLayout',
@ -86,13 +87,6 @@
// 复制一个route对象出来不能影响原route
let currentRoute = Object.assign({}, this.$route)
currentRoute.meta = Object.assign({}, currentRoute.meta)
// update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
let storeKey = 'route:title:' + currentRoute.fullPath
let routeTitle = this.$ls.get(storeKey)
if (routeTitle) {
currentRoute.meta.title = routeTitle
}
// update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
this.pageList.push(currentRoute)
this.linkList.push(currentRoute.fullPath)
this.activePage = currentRoute.fullPath

View File

@ -57,7 +57,8 @@ import {
Cascader,
Slider,
Transfer,
Rate
Rate,
Collapse,
} from 'ant-design-vue'
import Viser from 'viser-vue'
@ -112,6 +113,7 @@ Vue.use(Cascader)
Vue.use(Slider)
Vue.use(Transfer)
Vue.use(Rate)
Vue.use(Collapse)
Vue.prototype.$confirm = Modal.confirm
Vue.prototype.$message = message

View File

@ -11,6 +11,7 @@
:menu="menus"
:theme="theme"
@select="onSelect"
@updateMenuTitle="onUpdateMenuTitle"
:mode="mode"
:style="smenuStyle">
</s-menu>
@ -19,7 +20,7 @@
</template>
<script>
import ALayoutSider from "ant-design-vue/es/layout/Sider"
import ALayoutSider from 'ant-design-vue/es/layout/Sider'
import Logo from '../tools/Logo'
import SMenu from './index'
import { mixin, mixinDevice } from '@/utils/mixin.js'
@ -68,6 +69,9 @@
methods: {
onSelect (obj) {
this.$emit('menuSelect', obj)
},
onUpdateMenuTitle (obj) {
this.$emit('updateMenuTitle', obj)
}
}
}

View File

@ -82,18 +82,49 @@ export default {
} else {
this.selectedKeys = [routes.pop().path]
}
const openKeys = []
let openKeys = []
if (this.mode === 'inline') {
routes.forEach(item => {
openKeys.push(item.path)
})
}
// update-begin-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
// 包含冒号的是动态菜单
if (this.selectedKeys[0].includes(':')) {
let selectedKey = this.$route.fullPath
this.selectedKeys = [selectedKey]
let newOpenKeys = []
this.fullOpenKeys(this.menu, selectedKey, newOpenKeys)
if (newOpenKeys.length > 0) {
openKeys = newOpenKeys.reverse()
}
}
// update-end-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
//update-begin-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
if(!this.selectedKeys || this.selectedKeys[0].indexOf(":")<0){
this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys)
}
//update-end-author:taoyan date:20190510 for:online表单菜单点击展开的一级目录不对
},
// update-begin-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
// 递归查找当前选中的菜单和父级菜单填充openKeys
fullOpenKeys(menus, selectedKey, openKeys) {
for (let item of menus) {
if (item.path === selectedKey) {
openKeys.push(item.path)
this.$emit('updateMenuTitle', item)
return true
} else if (Array.isArray(item.children)) {
if (this.fullOpenKeys(item.children, selectedKey, openKeys)) {
openKeys.push(item.path)
return true
}
}
}
},
// update-end-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
// render
renderItem (menu) {

View File

@ -31,7 +31,9 @@
<s-menu
mode="horizontal"
:menu="menus"
:theme="theme"></s-menu>
:theme="theme"
@updateMenuTitle="handleUpdateMenuTitle"
></s-menu>
</div>
<a-icon
v-else
@ -155,8 +157,15 @@
this.topMenuStyle.headerIndexLeft = { 'width': `calc(100% - ${rightWidth})` }
}
}
}
},
//update-begin--author:sunjianlei---date:20190508------for: 顶部导航栏过长时显示更多按钮-----
// update-begin-author:sunjianlei date:20210508 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
handleUpdateMenuTitle(value) {
this.$emit('updateMenuTitle', value)
},
// update-end-author:sunjianlei date:20210508 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
}
}
</script>

View File

@ -16,6 +16,7 @@
v-if="device === 'mobile'"
:menus="menus"
@menuSelect="menuSelect"
@updateMenuTitle="handleUpdateMenuTitle"
:theme="navTheme"
:collapsed="false"
:collapsible="true"></side-menu>
@ -26,6 +27,7 @@
mode="inline"
:menus="menus"
@menuSelect="myMenuSelect"
@updateMenuTitle="handleUpdateMenuTitle"
:theme="navTheme"
:collapsed="collapsed"
:collapsible="true"></side-menu>
@ -45,6 +47,7 @@
mode="inline"
:menus="menus"
@menuSelect="menuSelect"
@updateMenuTitle="handleUpdateMenuTitle"
:theme="navTheme"
:collapsed="false"
:collapsible="true"></side-menu>
@ -62,6 +65,7 @@
:collapsed="collapsed"
:device="device"
@toggle="toggle"
@updateMenuTitle="handleUpdateMenuTitle"
/>
<!-- layout content -->
@ -85,15 +89,14 @@
import SideMenu from '@/components/menu/SideMenu'
import GlobalHeader from '@/components/page/GlobalHeader'
import GlobalFooter from '@/components/page/GlobalFooter'
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapActions, mapState } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
// update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
// import SettingDrawer from '@/components/setting/SettingDrawer'
// 注释这个因为在个人设置模块已经加载了SettingDrawer页面
// update-end ---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
import { triggerWindowResizeEvent } from '@/utils/util'
import { mapState, mapActions } from 'vuex'
import { mixin, mixinDevice } from '@/utils/mixin.js'
export default {
name: 'GlobalLayout',
components: {
@ -160,10 +163,6 @@
//此处触发动态路由被点击事件
this.findMenuBykey(this.menus,value.key)
this.$emit("dynamicRouterShow",value.key,this.activeMenu.meta.title)
// update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
let storeKey = 'route:title:' + this.activeMenu.path
this.$ls.set(storeKey, this.activeMenu.meta.title)
// update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
},
findMenuBykey(menus,key){
for(let i of menus){
@ -173,8 +172,17 @@
this.findMenuBykey(i.children,key)
}
}
}
},
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
// update-begin-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
handleUpdateMenuTitle(value) {
this.findMenuBykey(this.menus, value.path)
this.activeMenu.meta.title = value.meta.title
this.$emit('dynamicRouterShow', value.path, this.activeMenu.meta.title)
},
// update-end-author:sunjianlei date:20210409 for: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
}
}

View File

@ -185,8 +185,8 @@
return that.Logout({}).then(() => {
// update-begin author:wangshuai date:20200601 for: 退出登录跳转登录页面
that.$router.push({ path: '/user/login' });
window.location.reload()
// update-end author:wangshuai date:20200601 for: 退出登录跳转登录页面
//window.location.reload()
}).catch(err => {
that.$message.error({
title: '错误',
@ -225,11 +225,17 @@
// update_begin author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
searchMethods(value) {
let route = this.searchMenuOptions.filter(item => item.id === value)[0]
if (route.meta.internalOrExternal === true || route.component.includes('layouts/IframePageView')) {
//update-begin-author:taoyan date:20210528 for: 【菜单问题】配置一个iframe地址的菜单内部打开在搜索菜单上打开却新开了一个窗口
if (route.meta.internalOrExternal === true) {
window.open(route.meta.url, '_blank')
} else {
this.$router.push({ path: route.path })
if(route.component.includes('layouts/IframePageView')){
this.$router.push(route)
}else{
this.$router.push({ path: route.path })
}
}
//update-end-author:taoyan date:20210528 for: 【菜单问题】配置一个iframe地址的菜单内部打开在搜索菜单上打开却新开了一个窗口
this.searchMenuVisible = false
},
// update_end author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题

View File

@ -14,6 +14,7 @@ const FormTypes = {
popup:'popup',
list_multi:"list_multi",
sel_search:"sel_search",
sel_search_async:"sel_search_async",
radio:'radio',
checkbox_meta:"checkbox_meta",
input_pop:'input_pop',

View File

@ -0,0 +1,127 @@
import md5 from 'md5'
//签名密钥串(前后端要一致,正式发布请自行修改)
const signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
export default class signMd5Utils {
/**
* json参数升序
* @param jsonObj 发送参数
*/
static sortAsc(jsonObj) {
let arr = new Array();
let num = 0;
for (let i in jsonObj) {
arr[num] = i;
num++;
}
let sortArr = arr.sort();
let sortObj = {};
for (let i in sortArr) {
sortObj[sortArr[i]] = jsonObj[sortArr[i]];
}
return sortObj;
}
/**
* @param url 请求的url,应该包含请求参数(url的?后面的参数)
* @param requestParams 请求参数(POST的JSON参数)
* @returns {string} 获取签名
*/
static getSign(url, requestParams) {
let urlParams = this.parseQueryString(url);
let jsonObj = this.mergeObject(urlParams, requestParams);
//console.log("sign jsonObj: ",jsonObj)
let requestBody = this.sortAsc(jsonObj);
console.log("sign requestBody: ",requestBody)
return md5(JSON.stringify(requestBody) + signatureSecret).toUpperCase();
}
/**
* @param url 请求的url
* @returns {{}} 将url中请求参数组装成json对象(url的?后面的参数)
*/
static parseQueryString(url) {
let urlReg = /^[^\?]+\?([\w\W]+)$/,
paramReg = /([^&=]+)=([\w\W]*?)(&|$|#)/g,
urlArray = urlReg.exec(url),
result = {};
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
let lastpathVariable = url.substring(url.lastIndexOf('/') + 1);
if(lastpathVariable.includes(",")){
if(lastpathVariable.includes("?")){
lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?"));
}
result["x-path-variable"] = lastpathVariable;
}
if (urlArray && urlArray[1]) {
let paramString = urlArray[1], paramResult;
while ((paramResult = paramReg.exec(paramString)) != null) {
//数字值转为string类型前后端加密规则保持一致
if(this.myIsNaN(paramResult[2])){
paramResult[2] = paramResult[2].toString()
}
result[paramResult[1]] = paramResult[2];
}
}
return result;
}
/**
* @returns {*} 将两个对象合并成一个
*/
static mergeObject(objectOne, objectTwo) {
if (objectTwo && Object.keys(objectTwo).length > 0) {
for (let key in objectTwo) {
if (objectTwo.hasOwnProperty(key) === true) {
//数字值转为string类型前后端加密规则保持一致
if(this.myIsNaN(objectTwo[key])){
objectTwo[key] = objectTwo[key].toString()
}
objectOne[key] = objectTwo[key];
}
}
}
return objectOne;
}
static urlEncode(param, key, encode) {
if (param == null) return '';
let paramStr = '';
let t = typeof (param);
if (t == 'string' || t == 'number' || t == 'boolean') {
paramStr += '&' + key + '=' + ((encode == null || encode) ? encodeURIComponent(param) : param);
} else {
for (let i in param) {
let k = key == null ? i : key + (param instanceof Array ? '[' + i + ']' : '.' + i);
paramStr += this.urlEncode(param[i], k, encode);
}
}
return paramStr;
};
static getDateTimeToString() {
const date_ = new Date()
const year = date_.getFullYear()
let month = date_.getMonth() + 1
let day = date_.getDate()
if (month < 10) month = '0' + month
if (day < 10) day = '0' + day
let hours = date_.getHours()
let mins = date_.getMinutes()
let secs = date_.getSeconds()
const msecs = date_.getMilliseconds()
if (hours < 10) hours = '0' + hours
if (mins < 10) mins = '0' + mins
if (secs < 10) secs = '0' + secs
if (msecs < 10) secs = '0' + msecs
return year + '' + month + '' + day + '' + hours + '' + mins + '' + secs
}
// true:数值型的false非数值型
static myIsNaN(value) {
return typeof value === 'number' && !isNaN(value);
}
}

View File

@ -2,26 +2,35 @@
<a-card :bordered="false" style="height:100%;padding-bottom:200px; ">
<div class="table-page-search-wrapper">
<a-form layout="inline" :form="form">
<a-form-model ref="form" :model="formData" layout="inline">
<!-- 字典下拉 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="性别">
<a-form-model-item label="性别" prop="sex">
<j-dict-select-tag v-model="formData.sex" title="性别" dictCode="sex" placeholder="请选择性别"/>
<!-- <j-dict-select-tag title="性别" dictCode="sex" disabled/>-->
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.sex}}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-model-item label="性别2" prop="sex2">
<j-dict-select-tag v-model="formData.sex2" type="radioButton" title="性别2" dictCode="sex" placeholder="请选择性别2"/>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.sex2}}</a-col>
</a-row>
<!-- 字典表下拉 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="字典表下拉">
<a-form-model-item label="字典表下拉" prop="user">
<j-dict-select-tag v-model="formData.user" placeholder="请选择用户" dictCode="sys_user,realname,id"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.user}}</a-col>
</a-row>
@ -29,9 +38,9 @@
<!-- 带条件字典表下拉 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="字典表下拉(带条件)">
<a-form-model-item label="字典表下拉(带条件)" prop="user2">
<j-dict-select-tag v-model="formData.user2" placeholder="请选择用户" dictCode="sys_user,realname,id,username!='admin' order by create_time"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.user2}}</a-col>
</a-row>
@ -40,10 +49,10 @@
<!-- 字典搜索 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="字典搜索(同步)">
<a-form-model-item label="字典搜索(同步)" prop="searchValue">
<j-search-select-tag placeholder="请做出你的选择" v-model="formData.searchValue" :dictOptions="searchOptions">
</j-search-select-tag>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.searchValue}}</a-col>
</a-row>
@ -51,7 +60,7 @@
<!-- 字典搜索 异步加载 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="字典搜索(异步)">
<a-form-model-item label="字典搜索(异步)" prop="asyncSelectValue">
<j-search-select-tag
placeholder="请做出你的选择"
v-model="formData.asyncSelectValue"
@ -59,7 +68,7 @@
:pageSize="6"
:async="true">
</j-search-select-tag>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.asyncSelectValue}}</a-col>
</a-row>
@ -67,13 +76,13 @@
<!-- JMultiSelectTag -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="字典下拉(多选)">
<a-form-model-item label="字典下拉(多选)" prop="selMuti">
<j-multi-select-tag
v-model="formData.selMuti"
dictCode="sex"
placeholder="请选择">
</j-multi-select-tag>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">多选组合(v-model){{ formData.selMuti }}</a-col>
</a-row>
@ -81,48 +90,48 @@
<!-- 部门选择控件 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择部门 自定义返回值">
<j-select-depart v-model="orgCodes" :trigger-change="true" customReturnField="orgCode" :multi="true"></j-select-depart>
</a-form-item>
<a-form-model-item label="选择部门 自定义返回值" prop="orgCodes">
<j-select-depart v-model="formData.orgCodes" :trigger-change="true" customReturnField="orgCode" :multi="true"></j-select-depart>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的部门Code(v-decorator):{{ orgCodes }}</a-col>
<a-col :span="12">选中的部门Code(v-model):{{ formData.orgCodes }}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择部门">
<j-select-depart v-model="departId" :multi="true"></j-select-depart>
</a-form-item>
<a-form-model-item label="选择部门" prop="departId">
<j-select-depart v-model="formData.departId" :multi="true"></j-select-depart>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的部门ID(v-model):{{ departId }}</a-col>
<a-col :span="12">选中的部门ID(v-model):{{ formData.departId }}</a-col>
</a-row>
<!-- 通过部门选择用户控件 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择用户">
<j-select-user-by-dep v-model="userIds" :multi="true"></j-select-user-by-dep>
</a-form-item>
<a-form-model-item label="选择用户" prop="userIds">
<j-select-user-by-dep v-model="formData.userIds" :multi="true"></j-select-user-by-dep>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的用户(v-model):{{ userIds }}</a-col>
<a-col :span="12">选中的用户(v-model):{{ formData.userIds }}</a-col>
</a-row>
<!-- 用户选择控件 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择用户">
<j-select-multi-user v-model="multiUser" :query-config="selectUserQueryConfig"/>
</a-form-item>
<a-form-model-item label="选择用户" prop="multiUser">
<j-select-multi-user v-model="formData.multiUser" :query-config="selectUserQueryConfig"/>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的用户(v-model):{{ multiUser }}</a-col>
<a-col :span="12">选中的用户(v-model):{{ formData.multiUser }}</a-col>
</a-row>
<!-- 角色选择 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择角色">
<a-form-model-item label="选择角色" prop="selectRole">
<j-select-role v-model="formData.selectRole" @change="changeMe"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.selectRole}}</a-col>
</a-row>
@ -130,9 +139,9 @@
<!-- 职务选择 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="选择职务">
<a-form-model-item label="选择职务" prop="selectPosition">
<j-select-position :buttons="false" v-model="formData.selectPosition" />
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中值:{{ formData.selectPosition}}</a-col>
</a-row>
@ -140,90 +149,90 @@
<!-- JCheckbox -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="多选组合">
<a-form-model-item label="多选组合" prop="jCheckbox">
<j-checkbox
v-model="jcheckbox.values"
:options="jcheckbox.options"
v-model="formData.jCheckbox"
:options="jCheckboxOptions"
/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">多选组合(v-model){{ jcheckbox.values }}</a-col>
<a-col :span="12">多选组合(v-model){{ formData.jCheckbox }}</a-col>
</a-row>
<!-- JCodeEditor -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="代码输入框" style="min-height: 120px">
<a-form-model-item label="代码输入框" style="min-height: 120px" prop="jCodeEditor">
<j-code-editor
language="javascript"
v-model="jcodedditor.value"
v-model="formData.jCodeEditor"
:fullScreen="true"
style="min-height: 100px"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">代码输入框(v-model){{ jcodedditor.value }}</a-col>
<a-col :span="12">代码输入框(v-model){{ formData.jCodeEditor }}</a-col>
</a-row>
<!-- JDate -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="日期选择框">
<j-date v-model="jdate.value" :showTime="true" dateFormat="YYYY-MM-DD HH:mm:ss"/>
</a-form-item>
<a-form-model-item label="日期选择框" prop="jDate">
<j-date v-model="formData.jDate" :showTime="true" dateFormat="YYYY-MM-DD HH:mm:ss"/>
</a-form-model-item>
</a-col>
<a-col :span="12">日期选择框(v-model){{ jdate.value }}</a-col>
<a-col :span="12">日期选择框(v-model){{ formData.jDate }}</a-col>
</a-row>
<!-- JEditor -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="富文本编辑器" style="min-height: 300px">
<j-editor v-model="jeditor.value"/>
</a-form-item>
<a-form-model-item label="富文本编辑器" style="min-height: 300px" prop="jEditor">
<j-editor v-model="formData.jEditor"/>
</a-form-model-item>
</a-col>
<a-col :span="12">富文本编辑器(v-model){{ jeditor.value }}</a-col>
<a-col :span="12">富文本编辑器(v-model){{ formData.jEditor }}</a-col>
</a-row>
<!-- JEllipsis -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="过长剪切">
<j-ellipsis :value="jellipsis.value" :length="30"/>
</a-form-item>
<a-form-model-item label="过长剪切" prop="jEllipsis">
<j-ellipsis :value="formData.jEllipsis" :length="30"/>
</a-form-model-item>
</a-col>
<a-col :span="12">过长剪切:{{ jellipsis.value }}</a-col>
<a-col :span="12">过长剪切:{{ formData.jEllipsis }}</a-col>
</a-row>
<!-- JSlider -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="滑块验证码">
<a-form-model-item label="滑块验证码" prop="jSlider">
<j-slider @onSuccess="handleJSliderSuccess"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">滑块验证码验证通过:{{ jslider.value }}</a-col>
<a-col :span="12">滑块验证码验证通过:{{ formData.jSlider }}</a-col>
</a-row>
<!-- JSelectMultiple -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="多选下拉框">
<j-select-multiple v-model="jselectMultiple.value" :options="jselectMultiple.options"/>
</a-form-item>
<a-form-model-item label="多选下拉框" prop="jSelectMultiple">
<j-select-multiple v-model="formData.jSelectMultiple" :options="jSelectMultipleOptions"/>
</a-form-model-item>
</a-col>
<a-col :span="12">多选下拉框(v-model){{ jselectMultiple.value }}</a-col>
<a-col :span="12">多选下拉框(v-model){{ formData.jSelectMultiple }}</a-col>
</a-row>
<!-- JSelectMultiple -->
<a-row :gutter="24">
<a-col>
<a-form-item label="JModal弹窗">
<a-form-model-item label="JModal弹窗">
<a-button style="margin-right: 8px;" @click="()=>modal.visible=true">点击弹出JModal</a-button>
<span style="margin-right: 8px;">全屏化:<a-switch v-model="modal.fullscreen"/></span>
<span style="margin-right: 8px;">允许切换全屏:<a-switch v-model="modal.switchFullscreen"/></span>
</a-form-item>
</a-form-model-item>
<j-modal
:visible.sync="modal.visible"
@ -244,16 +253,16 @@
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="树字典">
<j-tree-dict v-model="formData.treeDict" placeholder="请选择树字典" parentCode="B01" />
</a-form-item>
<a-form-model-item label="树字典" prop="treeDict">
<j-tree-dict v-model="formData.treeDict" placeholder="请选择树字典" parentCode="A01" />
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ formData.treeDict }}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="下拉树选择">
<a-form-model-item label="下拉树选择" prop="treeSelect">
<j-tree-select
v-model="formData.treeSelect"
placeholder="请选择菜单"
@ -261,14 +270,14 @@
pidField="parent_id"
pidValue=""
/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ formData.treeSelect }}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="下拉树多选">
<a-form-model-item label="下拉树多选" prop="treeSelectMultiple">
<j-tree-select
v-model="formData.treeSelectMultiple"
placeholder="请选择菜单"
@ -277,7 +286,7 @@
pidValue=""
multiple
/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ formData.treeSelectMultiple }}</a-col>
</a-row>
@ -285,9 +294,9 @@
<!-- 分类字典树 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="分类字典树">
<a-form-model-item label="分类字典树" prop="selectCategory">
<j-category-select v-model="formData.selectCategory" pcode="B01" :multiple="true"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ formData.selectCategory }}</a-col>
</a-row>
@ -295,23 +304,23 @@
<!-- VueCron -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="cron表达式">
<j-cron ref="innerVueCron" v-decorator="['cronExpression', { initialValue: '* * * * * ? *' }]" @change="setCorn"></j-cron>
</a-form-item>
<a-form-model-item label="cron表达式" prop="cronExpression">
<j-cron v-model="formData.cronExpression"></j-cron>
</a-form-model-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="高级查询">
<a-form-model-item label="高级查询">
<j-super-query :fieldList="superQuery.fieldList" />
</a-form-item>
</a-form-model-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="高级查询自定义按钮">
<a-form-model-item label="高级查询自定义按钮">
<j-super-query :fieldList="superQuery.fieldList">
<!--
v-slot:button 可以更高自由的定制按钮
@ -334,34 +343,34 @@
</a-button-group>
</template>
</j-super-query>
</a-form-item>
</a-form-model-item>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="图片上传">
<j-image-upload v-model="imgList"></j-image-upload>
</a-form-item>
<a-form-model-item label="图片上传" prop="imgList">
<j-image-upload bizPath="scott/pic" v-model="formData.imgList"></j-image-upload>
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ imgList }}</a-col>
<a-col :span="12">选中的值(v-model){{ formData.imgList }}</a-col>
</a-row>
<a-row :gutter="24" style="margin-top: 65px;margin-bottom:50px;">
<a-col :span="12">
<a-form-item label="文件上传">
<j-upload v-model="fileList"></j-upload>
</a-form-item>
<a-form-model-item label="文件上传" prop="fileList">
<j-upload v-model="formData.fileList"></j-upload>
</a-form-model-item>
</a-col>
<a-col :span="12">
选中的值(v-model)
<j-ellipsis :value="fileList" :length="30" v-if="fileList.length>0"/>
<j-ellipsis :value="formData.fileList" :length="30" v-if="formData.fileList.length>0"/>
</a-col>
</a-row>
<!-- 特殊查询组件 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="特殊查询组件">
<a-form-model-item label="特殊查询组件" prop="jInput">
<a-row>
<a-col :span="15">
<j-input v-model="formData.jInput" :type="jInput.type"/>
@ -371,27 +380,27 @@
<a-select v-model="jInput.type" :options="jInput.options"></a-select>
</a-col>
</a-row>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">输入的值(v-model){{ formData.jInput }}</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="15">
<a-form-item label="MarkdownEditor" style="min-height: 300px">
<j-markdown-editor v-model="content"></j-markdown-editor>
</a-form-item>
<a-form-model-item label="MarkdownEditor" prop="content" style="min-height: 300px">
<j-markdown-editor v-model="formData.content"></j-markdown-editor>
</a-form-model-item>
</a-col>
<a-col :span="9">
输入的值(v-model){{ content }}
输入的值(v-model){{ formData.content }}
</a-col>
</a-row>
<!-- 省市县级联 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="省市县级联">
<a-form-model-item label="省市县级联" prop="areaLinkage1">
<j-area-linkage v-model="formData.areaLinkage1" type="cascader"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">输入的值(v-model){{ formData.areaLinkage1 }}</a-col>
</a-row>
@ -400,9 +409,9 @@
<!-- 省市县级联 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="省市县级联">
<a-form-model-item label="省市县级联" prop="areaLinkage2">
<j-area-linkage v-model="formData.areaLinkage2" type="select"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">输入的值(v-model){{ formData.areaLinkage2 }}</a-col>
</a-row>
@ -410,23 +419,23 @@
<!-- 功能示例:关闭当前页面 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="功能示例关闭当前页面">
<a-form-model-item label="功能示例关闭当前页面">
<a-button type="primary" @click="handleCloseCurrentPage">点击关闭当前页面</a-button>
</a-form-item>
</a-form-model-item>
</a-col>
</a-row>
<!-- JPopup示例 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-item label="JPopup示例">
<a-form-model-item label="JPopup示例" prop="jPopup">
<j-popup v-model="formData.jPopup" code="demo" field="name" orgFields="name" destFields="name" :multi="true"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :span="12">选择的值(v-model){{ formData.jPopup }}</a-col>
</a-row>
</a-form>
</a-form-model>
</div>
</a-card>
@ -490,49 +499,35 @@
formData: {
areaLinkage1: '110105',
areaLinkage2: '140221',
sex: 1
},
form: this.$form.createForm(this),
departId: '57197590443c44f083d42ae24ef26a2c,a7d7e77e06c84325a40932163adcdaa6',
orgCodes: 'A02A01,A02A02',
userIds: 'admin',
multiUser: 'admin,jeecg',
jcheckbox: {
values: 'spring,jeecgboot',
options: [
{ label: 'Jeecg', value: 'jeecg' },
{ label: 'Jeecg-Boot', value: 'jeecgboot' },
{ label: 'Spring', value: 'spring', disabled: true },
{ label: 'MyBaits', value: 'mybatis' }
]
},
jcodedditor: {
value: `function sayHi(word) {
alert(word)
}
sayHi('hello, world!')`
},
jdate: {
value: '2019-5-10 15:33:06'
},
jeditor: {
value: '<h2 style="text-align: center;">富文本编辑器</h2> <p>这里是富文本编辑器。</p>'
},
jellipsis: {
value: '这是一串很长很长的文字段落。这是一串很长很长的文字段落。这是一串很长很长的文字段落。这是一串很长很长的文字段落。'
},
jslider: {
value: false
},
jselectMultiple: {
options: [
{ text: '字符串', value: 'String' },
{ text: '整数型', value: 'Integer' },
{ text: '浮点型', value: 'Double' },
{ text: '布尔型', value: 'Boolean' }
],
value: 'Integer,Boolean'
sex: 1,
orgCodes: 'A02A01,A02A02',
departId: '57197590443c44f083d42ae24ef26a2c,a7d7e77e06c84325a40932163adcdaa6',
userIds: 'admin',
multiUser: 'admin,jeecg',
jCheckbox: 'spring,jeecgboot',
jCodeEditor: `function sayHi(word) {\n alert(word)\n}\nsayHi('hello, world!')`,
jDate: '2019-5-10 15:33:06',
jEditor: '<h2 style="text-align: center;">富文本编辑器</h2> <p>这里是富文本编辑器。</p>',
jEllipsis: '这是一串很长很长的文字段落。这是一串很长很长的文字段落。这是一串很长很长的文字段落。这是一串很长很长的文字段落。',
jSlider: false,
jSelectMultiple: 'Integer,Boolean',
imgList:[],
fileList:[],
content: '',
cronExpression: '* * * * * ? *',
},
jCheckboxOptions: [
{label: 'Jeecg', value: 'jeecg'},
{label: 'Jeecg-Boot', value: 'jeecgboot'},
{label: 'Spring', value: 'spring', disabled: true},
{label: 'MyBaits', value: 'mybatis'}
],
jSelectMultipleOptions: [
{text: '字符串', value: 'String'},
{text: '整数型', value: 'Integer'},
{text: '浮点型', value: 'Double'},
{text: '布尔型', value: 'Boolean'}
],
modal: {
title: '这里是标题',
visible: false,
@ -556,8 +551,6 @@ sayHi('hello, world!')`
},
]
},
fileList:[],
imgList:[],
jInput: {
type: 'like',
options: [
@ -567,7 +560,6 @@ sayHi('hello, world!')`
{ value: 'le', label: '小于等于le)' },
],
},
content: '',
searchOptions:[{
text:"选项一",
value:"1"
@ -599,10 +591,10 @@ sayHi('hello, world!')`
handleChange() {
},
getDepartIdValue() {
return this.form.getFieldValue('departId')
return this.formData.departId
},
getOrgCodesValue() {
return this.form.getFieldValue('orgCodes')
return this.formData.orgCodes
},
changeMe() {
console.log('you so ... , change Me')
@ -626,12 +618,7 @@ sayHi('hello, world!')`
this.selectedDepUsers = selectedDepUsers
},
handleJSliderSuccess(value) {
this.jslider.value = value
},
setCorn(data){
this.$nextTick(() => {
this.form.cronExpression = data;
})
this.formData.jSlider = value
},
handleCloseCurrentPage() {

View File

@ -0,0 +1,208 @@
<template>
<a-card :bordered="false">
<a-spin :spinning="loading">
<a-form-model ref="form" :model="model" :rules="rules">
<a-tabs>
<a-tab-pane tab="消息选项" key="1">
<a-form-model-item label="测试APP" prop="app" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select v-model="model.app" placeholder="请选择测试APP" :options="appOptions"/>
</a-form-model-item>
<a-form-model-item label="发送给所有人" prop="sendAll" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-switch checkedChildren="" unCheckedChildren="" v-model="model.sendAll" @change="onSendAllChange"/>
</a-form-model-item>
<a-form-model-item label="接收人" prop="receiver" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-select-multi-user v-model="model.receiver" :disabled="model.sendAll" placeholder="请选择接收人"/>
</a-form-model-item>
<a-form-model-item label="消息内容" prop="content" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-textarea :rows="5" v-model="model.content" placeholder="请输入消息内容"/>
</a-form-model-item>
<div style="text-align: center;">
<a-button type="primary" size="large" @click="onSend" style="width: 120px;">发送</a-button>
</div>
</a-tab-pane>
</a-tabs>
<a-tabs>
<a-tab-pane tab="测试结果(刷新自动清空)" key="1">
<a-table
rowKey="id"
bordered
size="middle"
:columns="columns"
:dataSource="dataSource"
>
<div slot="action" slot-scope="text, record">
<template v-if="record.app==='DINGTALK'">
<a-popconfirm v-if="!record.recalled" title="确定吗?" @confirm="handleRecall(record)">
<a @click="">撤回</a>
</a-popconfirm>
<span v-else>已撤回</span>
</template>
<template v-else>-</template>
</div>
</a-table>
</a-tab-pane>
</a-tabs>
</a-form-model>
</a-spin>
</a-card>
</template>
<script>
import { loadEnabledTypes } from '@/components/jeecgbiz/thirdApp/JThirdAppButton'
import { postAction } from '@/api/manage'
import { randomUUID } from '@/utils/util'
export default {
name: 'ThirdAppMessageTest',
data() {
return {
loading: false,
labelCol: {span: 6},
wrapperCol: {span: 12},
model: {
sendAll: false,
},
enabledTypes: {},
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: (t, r, index) => {
return this.dataSource.length - index
}
},
{
title: '测试APP',
align: 'center',
dataIndex: 'app',
customRender: (t, r, index) => {
if (t === 'WECHAT_ENTERPRISE') {
return '企业微信'
}
if (t === 'DINGTALK') {
return '钉钉'
} else {
return t
}
}
},
{
title: '接收人',
align: 'center',
dataIndex: 'receiver',
customRender: (t, r, index) => {
return r.sendAll ? '全体' : t
}
},
{
title: '消息内容',
align: 'center',
dataIndex: 'content'
},
{
title: 'response',
align: 'center',
dataIndex: 'response'
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
fixed: 'right',
width: 80,
scopedSlots: {customRender: 'action'}
}
],
dataSource: [],
}
},
computed: {
rules() {
return {
app: [{required: true, message: '请选择测试APP'}],
url: [{required: this.show, message: '请输入菜单路径!'}],
receiver: [{required: !this.model.sendAll, message: '请选择接收人'}],
content: [{required: true, message: '消息内容不能为空'}]
}
},
appOptions() {
return [
{
label: `企业微信${this.enabledTypes.wechatEnterprise ? '' : '已禁用'}`,
value: 'WECHAT_ENTERPRISE',
disabled: !this.enabledTypes.wechatEnterprise
},
{
label: `钉钉${this.enabledTypes.dingtalk ? '' : '已禁用'}`,
value: 'DINGTALK',
disabled: !this.enabledTypes.dingtalk
},
]
},
},
created() {
this.loadEnabledTypes()
},
methods: {
// 获取启用的第三方App
async loadEnabledTypes() {
this.enabledTypes = await loadEnabledTypes()
},
onSendAllChange() {
this.$refs.form.clearValidate('receiver')
},
onSend() {
this.$refs.form.validate((ok, err) => {
if (ok) {
this.loading = true
postAction('/sys/thirdApp/sendMessageTest', this.model).then(({success, result, message}) => {
if (success) {
let response = ''
try {
response = JSON.stringify(result)
} catch (e) {
response = result
}
this.dataSource.unshift(Object.assign({id: randomUUID()}, this.model, {response}))
} else {
this.$message.warning(message)
}
}).finally(() => this.loading = false)
}
})
},
handleRecall(record) {
try {
let response = JSON.parse(record.response)
postAction('/sys/thirdApp/recallMessageTest', {
app: record.app,
msg_task_id: response.result
}).then(res => {
if (res.success) {
this.$set(record, 'recalled', true)
this.$message.success(res.message)
} else {
this.$message.warning(res.message)
}
}).catch(e => this.$message.warning(e.message || e))
} catch (e) {
this.$message.warning(e.message || e)
}
},
},
}
</script>
<style scoped>
</style>

View File

@ -1,38 +1,38 @@
<template>
<a-card :bordered="false">
<a-form @submit="handleSubmit" :form="form">
<a-form-model ref="form" :model="model" :rules="rules" @submit="handleSubmit">
<a-row>
<a-col :md="24" :sm="24">
<a-form-item label="Note" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-input v-decorator="['note',{rules: [{ required: true, message: 'Please input your note!' }]}]"/>
</a-form-item>
<a-form-model-item label="Note" prop="note" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-input v-model="model.note"/>
</a-form-model-item>
</a-col>
</a-row>
<a-row>
<a-col :md="24" :sm="24">
<a-form-item label="Gender" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-select v-decorator="['gender',{rules: [{ required: true, message: 'Please select your gender!' }]}]" placeholder="Select a option and change input text above" @change="this.handleSelectChange">
<a-form-model-item label="Gender" prop="gender" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-select v-model="model.gender" placeholder="Select a option and change input text above" @change="handleSelectChange">
<a-select-option value="male">male</a-select-option>
<a-select-option value="female">female</a-select-option>
</a-select>
</a-form-item>
</a-form-model-item>
</a-col>
</a-row>
<a-row>
<a-col :md="24" :sm="24">
<a-form-item label="Gender" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-form-model-item label="Gender" prop="cascader" :labelCol="{ span: 7 }" :wrapperCol="{ span: 15 }">
<a-cascader :options="areaOptions" @change="onChange" :showSearch="{filter}" placeholder="Please select" />
</a-form-item>
</a-form-model-item>
</a-col>
</a-row>
<a-form-item :wrapperCol="{ span: 12, offset: 5 }">
<a-form-model-item :wrapperCol="{ span: 12, offset: 5 }">
<a-col :md="24" :sm="24">
<a-form-item :wrapperCol="{ span: 12, offset: 5 }">
<a-form-model-item :wrapperCol="{ span: 12, offset: 5 }">
<a-button type="primary" htmlType="submit">Submit</a-button>
</a-form-item>
</a-form-model-item>
</a-col>
</a-form-item>
</a-form>
</a-form-model-item>
</a-form-model>
</a-card>
</template>
@ -43,24 +43,27 @@
data () {
return {
formLayout: 'horizontal',
form: this.$form.createForm(this),
model: {},
rules: {
note: [{required: true, message: 'Please input your note!'}],
gender:[{ required: true, message: 'Please select your gender!' }]
},
areaOptions:[]
}
},
methods: {
handleSubmit (e) {
e.preventDefault()
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values)
this.$refs.form.validate((ok, err) => {
if (ok) {
console.log('Received values of form: ', this.model)
this.$message.success('succeed!')
}
})
},
handleSelectChange (value) {
console.log(value)
this.form.setFieldsValue({
note: `Hi, ${value === 'male' ? 'man' : 'lady'}!`,
})
this.model.note = `Hi, ${value === 'male' ? 'man' : 'lady'}!`
},
onChange(value, selectedOptions) {
console.log(value, selectedOptions);

View File

@ -99,7 +99,11 @@
placeholder: '点击上传',
token: true,
responseName: 'message',
action: window._CONFIG['domianURL'] + '/sys/common/upload'
action: window._CONFIG['domianURL'] + '/sys/common/upload',
data: {
biz: 'temp',
// 更多扩展参数
},
},
{
title: '字段类型',

View File

@ -1,6 +1,6 @@
<template>
<a-modal
title="corn表达式"
title="cron表达式"
:width="modalWidth"
:visible="visible"
:confirmLoading="confirmLoading"

View File

@ -4,13 +4,14 @@
<a-card :bordered="false">
<!-- 按钮操作区域 -->
<a-row style="margin-left: 14px">
<a-row style="margin: 0 0 0 14px" class="table-operator">
<a-button @click="handleAdd(1)" type="primary">添加部门</a-button>
<a-button @click="handleAdd(2)" type="primary">添加下级</a-button>
<a-button type="primary" icon="download" @click="handleExportXls('部门信息')">导出</a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="import">导入</a-button>
</a-upload>
<j-third-app-button biz-type="depart" :selected-row-keys="selectedRowKeys" syncToApp @sync-finally="onSyncFinally"/>
<a-button title="删除多条数据" @click="batchDel" type="default">批量删除</a-button>
</a-row>
<div style="background: #fff;padding-left:16px;height: 100%; margin-top: 5px">
@ -27,6 +28,7 @@
<a-dropdown :trigger="[this.dropTrigger]" @visibleChange="dropStatus">
<span style="user-select: none">
<a-tree
v-if="loading"
checkable
multiple
@select="onSelect"
@ -36,11 +38,10 @@
:checkedKeys="checkedKeys"
:treeData="departTree"
:checkStrictly="checkStrictly"
:expandedKeys="iExpandedKeys"
:expandedKeys.sync="iExpandedKeys"
:load-data="loadSubTree"
@expand="onExpand"/>
</span>
<!--新增右键点击事件,和增加添加和删除功能-->
<a-menu slot="overlay">
<a-menu-item @click="handleAdd(3)" key="1">添加</a-menu-item>
<a-menu-item @click="handleDelete" key="2">删除</a-menu-item>
@ -73,42 +74,27 @@
<a-tabs defaultActiveKey="1">
<a-tab-pane tab="基本信息" key="1" >
<a-card :bordered="false" v-if="selectedKeys.length>0">
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构名称">
<a-input placeholder="请输入机构/部门名称" v-decorator="['departName', validatorRules.departName ]"/>
</a-form-item>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门">
<a-tree-select
style="width:100%"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="treeData"
:disabled="disable"
v-model="model.parentId"
placeholder="">
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="机构名称" prop="departName" >
<a-input placeholder="请输入机构/部门名称" v-model="model.departName"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门" prop="parentId">
<a-tree-select style="width:100%" :dropdownStyle="{maxHeight:'200px',overflow:'auto'}" :treeData="treeData" :disabled="disable" v-model="model.parentId" placeholder="">
</a-tree-select>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构编码">
<a-input disabled placeholder="请输入机构编码" v-decorator="['orgCode', validatorRules.orgCode ]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构类型">
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="机构编码" prop="orgCode">
<a-input disabled placeholder="请输入机构编码" v-model="model.orgCode"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="机构类型" prop="orgCategory">
<template v-if="orgCategoryDisabled">
<a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
<a-radio-group v-model="model.orgCategory" placeholder="请选择机构类型">
<a-radio value="1">
公司
</a-radio>
</a-radio-group>
</template>
<template v-else>
<a-radio-group v-decorator="['orgCategory',validatorRules.orgCategory]" placeholder="请选择机构类型">
<a-radio-group v-model="model.orgCategory" placeholder="请选择机构类型">
<a-radio value="2">
部门
</a-radio>
@ -117,32 +103,20 @@
</a-radio>
</a-radio-group>
</template>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="排序">
<a-input-number v-decorator="[ 'departOrder',{'initialValue':0}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="手机号">
<a-input placeholder="请输入手机号" v-decorator="['mobile', {'initialValue':''}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="地址">
<a-input placeholder="请输入地址" v-decorator="['address', {'initialValue':''}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="备注">
<a-textarea placeholder="请输入备注" v-decorator="['memo', {'initialValue':''}]"/>
</a-form-item>
</a-form>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="排序" prop="departOrder">
<a-input-number v-model="model.departOrder"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="手机号" prop="mobile">
<a-input placeholder="请输入手机号" v-model="model.mobile"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="地址" prop="address">
<a-input placeholder="请输入地址" v-model="model.address"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="备注" prop="memo">
<a-textarea placeholder="请输入备注" v-model="model.memo"/>
</a-form-model-item>
</a-form-model>
<div class="anty-form-btn">
<a-button @click="emptyCurrForm" type="default" htmlType="button" icon="sync">重置</a-button>
<a-button @click="submitCurrForm" type="primary" htmlType="button" icon="form">保存</a-button>
@ -165,11 +139,12 @@
</template>
<script>
import DepartModal from './modules/DepartModal'
import pick from 'lodash.pick'
import {queryDepartTreeSync, searchByKeywords, deleteByDepartId} from '@/api/api'
import {httpAction, deleteAction} from '@/api/manage'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
import { deleteByDepartId, queryDepartTreeSync, searchByKeywords } from '@/api/api'
import { deleteAction, httpAction } from '@/api/manage'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import DepartAuthModal from './modules/DepartAuthModal'
import { cloneObject } from '@/utils/util'
import JThirdAppButton from '@comp/jeecgbiz/thirdApp/JThirdAppButton'
// 表头
const columns = [
{
@ -213,13 +188,14 @@
name: 'DepartList',
mixins: [JeecgListMixin],
components: {
JThirdAppButton,
DepartAuthModal,
DepartModal
},
data() {
return {
iExpandedKeys: [],
loading: false,
loading: true,
autoExpandParent: false,
currFlowId: '',
currFlowName: '',
@ -244,9 +220,8 @@
currSelected: {},
allTreeKeys:[],
loadTreeKeys:[],
checkStrictly: true,
form: this.$form.createForm(this),
labelCol: {
xs: {span: 24},
sm: {span: 5}
@ -260,10 +235,10 @@
edges: []
},
validatorRules: {
departName: {rules: [{required: true, message: '请输入机构/部门名称!'}]},
orgCode: {rules: [{required: true, message: '请输入机构编码!'}]},
orgCategory: {rules: [{required: true, message: '请输入机构类型!'}]},
mobile: {rules: [{validator: this.validateMobile}]}
departName: [{required: true, message: '请输入机构/部门名称!'}],
orgCode: [{required: true, message: '请输入机构编码!'}],
orgCategory: [{required: true, message: '请输入机构类型!'}],
mobile: [{validator: this.validateMobile}]
},
url: {
delete: '/sys/sysDepart/delete',
@ -294,6 +269,10 @@
that.departTree = []
//所有的树节点key信息
that.allIds = []
that.iExpandedKeys = []
that.loading = false
queryDepartTreeSync().then((res) => {
if (res.success) {
this.allTreeKeys = [];
@ -304,30 +283,38 @@
that.departTree.push(temp)
that.allIds.push(temp.key)
that.allTreeKeys.push(temp.key)
if(that.loadTreeKeys.indexOf(temp.key)>=0){
that.iExpandedKeys.push(temp.key)
}
}
this.loading = false
that.$nextTick(()=>{
that.loading = true
})
}
})
},
loadSubTree(treeNode) {
var that = this;
return new Promise(resolve => {
queryDepartTreeSync({pid:treeNode.dataRef.id}).then((res) => {
if (res.success) {
//判断chidlren是否为空并修改isLeaf属性值
if(res.result.length == 0){
treeNode.dataRef['isLeaf']=true
return;
}else{
treeNode.dataRef['children']= res.result;
}
for (let i = 0; i < res.result.length; i++) {
let temp = res.result[i]
that.allIds.push(temp.key)
queryDepartTreeSync({pid:treeNode.dataRef.id}).then((res) => {
if (res.success) {
//判断chidlren是否为空并修改isLeaf属性值
if(res.result.length == 0){
treeNode.dataRef['isLeaf']=true
return;
}else{
treeNode.dataRef['children']= res.result;
}
for (let i = 0; i < res.result.length; i++) {
let temp = res.result[i]
that.allIds.push(temp.key)
if(that.loadTreeKeys.indexOf(temp.key)>0){
that.iExpandedKeys.push(temp.key)
}
}
})
resolve();
}
})
resolve();
});
//保存全部部门信息,方便后面搜索使用
that.departTreeAll=that.departTree
@ -340,8 +327,7 @@
rightHandle(node) {
this.dropTrigger = 'contextmenu'
console.log(node.node.eventKey)
//注释异步书加载key拼接__,以便于每次展开节点会刷新数据
this.rightClickSelectedKey = node.node.eventKey.split('__')[0]
this.rightClickSelectedKey = node.node.eventKey
this.rightClickSelectedOrgCode = node.node.dataRef.orgCode
},
onExpand(expandedKeys) {
@ -349,6 +335,7 @@
this.iExpandedKeys = expandedKeys
this.autoExpandParent = false
this.allTreeKeys=expandedKeys
this.loadTreeKeys=expandedKeys
},
backFlowList() {
this.$router.back(-1)
@ -373,7 +360,7 @@
} else {
var ids = ''
for (var a = 0; a < this.checkedKeys.length; a++) {
ids += this.checkedKeys[a].split('__')[0] + ','
ids += this.checkedKeys[a]+ ','
}
var that = this
this.$confirm({
@ -453,8 +440,7 @@
this.orgCategoryDisabled = false;
}
this.$nextTick(() => {
this.form.getFieldDecorator('fax', {initialValue: ''})
this.form.setFieldsValue(pick(record, 'departName','orgCategory', 'orgCode', 'departOrder', 'mobile', 'fax', 'address', 'memo'))
this.model = cloneObject(record)
})
},
getCurrSelectedTitle() {
@ -464,7 +450,7 @@
this.hiding = true
this.checkedKeys = []
this.currSelected = {}
this.form.resetFields()
this.model = cloneObject(this.currSelected)
this.selectedKeys = []
this.$refs.departAuth.departId = ''
},
@ -478,14 +464,14 @@
this.currSelected.receiptTriggerType = value
},
submitCurrForm() {
this.form.validateFields((err, values) => {
if (!err) {
this.$refs.form.validate((ok, err) => {
if (ok) {
if (!this.currSelected.id) {
this.$message.warning('请点击选择要修改部门!')
return
}
let formData = Object.assign(this.currSelected, values)
let formData = Object.assign(this.currSelected, this.model)
console.log('Received values of form: ', formData)
httpAction(this.url.edit, formData, 'put').then((res) => {
if (res.success) {
@ -499,12 +485,12 @@
})
},
emptyCurrForm() {
this.form.resetFields()
this.model = this.currSelected
},
nodeSettingFormSubmit() {
this.form.validateFields((err, values) => {
if (!err) {
console.log('Received values of form: ', values)
this.$refs.form.validate((ok, err) => {
if (ok) {
console.log('Received values of form: ', this.model)
}
})
},
@ -521,7 +507,7 @@
this.$message.warning('请先点击选中上级部门!')
return false
}
this.$refs.departModal.add(this.selectedKeys[0].split('__')[0])
this.$refs.departModal.add(this.selectedKeys[0])
this.$refs.departModal.title = '新增'
} else {
this.$refs.departModal.add(this.rightClickSelectedKey)
@ -541,7 +527,7 @@
that.$message.success('删除成功!')
that.loadTree()
//删除后同步清空右侧基本信息内容
let orgCode=that.form.getFieldValue("orgCode");
let orgCode = that.model.orgCode
if(orgCode && orgCode === that.rightClickSelectedOrgCode){
that.onClearSelected()
}
@ -576,6 +562,7 @@
// <!---- author:os_chengtgen -- date:20190827 -- for:切换父子勾选模式 =======------>
expandAll () {
this.iExpandedKeys = this.allTreeKeys
//this.loadTree()
},
closeAll () {
this.iExpandedKeys = []
@ -604,9 +591,24 @@
this.getAllKeys(node.children[a])
}
}
}
},
// <!---- author:os_chengtgen -- date:20190827 -- for:切换父子勾选模式 =======------>
// 验证手机号
validateMobile(rule,value,callback){
if (!value || new RegExp(/^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$/).test(value)){
callback();
}else{
callback("您的手机号码格式不正确!");
}
},
onSyncFinally({isToLocal}) {
// 同步到本地时刷新下数据
if (isToLocal) {
this.loadData()
}
},
},
created() {
this.currFlowId = this.$route.params.id
@ -616,6 +618,9 @@
}
</script>
<style scoped>
@import '~@assets/less/common.less';
</style>
<style scoped>
.ant-card-body .table-operator {
margin: 15px;
@ -661,10 +666,6 @@
}
/** Button按钮间距 */
.ant-btn {
margin-left: 3px
}
.drawer-bootom-button {
/*position: absolute;*/
bottom: 0;

View File

@ -25,7 +25,7 @@
<div style="margin-top: 24px;" v-else-if="userIdentity === '2' && departTree.length==0">
<h3><span>您的部门下暂无有效部门信息</span></h3>
</div>
<div style="margin-top: 24px;" v-else><h3>普通员工暂此权限</h3></div>
<div style="margin-top: 24px;" v-else><h3>普通员工暂此权限</h3></div>
</div>
</a-card>
</a-col>
@ -49,8 +49,8 @@
<script>
import DeptBaseInfo from './modules/DeptBaseInfo'
import DeptUserInfo from './modules/DeptUserInfo'
import {queryMyDepartTreeList, searchByKeywords} from '@/api/api'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
import { queryMyDepartTreeList, searchByKeywords } from '@/api/api'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import DeptRoleInfo from './modules/DeptRoleInfo'
export default {

View File

@ -3,22 +3,22 @@
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-form-model layout="inline" :model="queryParam" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="10">
<a-form-item label="任务类名">
<a-form-model-item label="任务类名" prop="jobClassName">
<a-input placeholder="请输入任务类名" v-model="queryParam.jobClassName"></a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="10">
<a-form-item label="任务状态">
<a-form-model-item label="任务状态" prop="status">
<a-select style="width: 220px" v-model="queryParam.status" placeholder="请选择状态">
<a-select-option value="">全部</a-select-option>
<a-select-option value="0">正常</a-select-option>
<a-select-option value="-1">停止</a-select-option>
</a-select>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="10" >
@ -29,7 +29,7 @@
</a-col>
</a-row>
</a-form>
</a-form-model>
</div>
<!-- 操作按钮区域 -->
@ -225,7 +225,7 @@
title:"确认暂停",
content:"是否暂停选中任务?",
onOk: function(){
getAction(that.url.pause,{jobClassName:record.jobClassName}).then((res)=>{
getAction(that.url.pause,{id:record.id}).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.loadData();
@ -245,7 +245,7 @@
title:"确认启动",
content:"是否启动选中任务?",
onOk: function(){
getAction(that.url.resume,{jobClassName:record.jobClassName}).then((res)=>{
getAction(that.url.resume,{id:record.id}).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.loadData();

View File

@ -102,6 +102,11 @@
<!-- 表单区域 -->
<sysAnnouncement-modal ref="modalForm" @ok="modalFormOk"></sysAnnouncement-modal>
<!-- 查看详情 -->
<j-modal class="detail-modal" title="查看详情" :visible.sync="detailModal.visible" :top="50" :width="600" switchFullscreen :footer="null">
<iframe v-if="detailModal.url" class="detail-iframe" :src="detailModal.url"/>
</j-modal>
</a-card>
</template>
@ -110,6 +115,7 @@
import {doReleaseData, doReovkeData} from '@/api/api'
import {getAction} from '@/api/manage'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
import { ACCESS_TOKEN } from '@/store/mutation-types'
export default {
name: "SysAnnouncementList",
@ -237,6 +243,7 @@
scopedSlots: {customRender: 'action'},
}
],
detailModal: {visible: false, url: '',},
url: {
list: "/sys/annountCement/list",
delete: "/sys/annountCement/delete",
@ -284,14 +291,42 @@
getAction("sys/annountCement/syncNotic",{anntId:anntId})
},
handleDetail:function(record){
this.$refs.modalForm.edit(record);
this.$refs.modalForm.title="详情";
this.$refs.modalForm.disableSubmit = true;
this.$refs.modalForm.disabled = true;
const domain = window._CONFIG['domianURL']
const token = this.$ls.get(ACCESS_TOKEN)
this.detailModal.url = `${domain}/sys/annountCement/show/${record.id}?token=${token}`
this.detailModal.visible = true
},
}
}
</script>
<style scoped>
@import '~@assets/less/common.less'
<style scoped lang="less">
@import '~@assets/less/common.less';
/** 查看详情弹窗的样式 */
.detail-modal {
.detail-iframe {
border: 0;
width: 100%;
height: 88vh;
min-height: 600px;
}
&.fullscreen .detail-iframe {
height: 100%;
}
}
.detail-modal /deep/ .ant-modal {
top: 30px;
.ant-modal-body {
font-size: 0;
padding: 0;
}
}
.detail-modal.fullscreen /deep/ .ant-modal {
top: 0;
}
</style>

View File

@ -3,18 +3,18 @@
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-form-model layout="inline" :model="queryParam" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="规则名称">
<a-form-model-item label="规则名称" prop="ruleName">
<a-input placeholder="请输入规则名称" v-model="queryParam.ruleName"/>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="规则Code">
<a-form-model-item label="规则Code" prop="ruleCode">
<a-input placeholder="请输入规则Code" v-model="queryParam.ruleCode"/>
</a-form-item>
</a-form-model-item>
</a-col>
<template v-if="toggleSearchStatus">
</template>
@ -30,7 +30,7 @@
</a-col>
</a-row>
</a-form>
</a-form-model>
</div>
<!-- 操作按钮区域 -->

View File

@ -3,17 +3,17 @@
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-form-model layout="inline" :model="queryParam" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="规则名称">
<a-form-model-item label="规则名称" prop="ruleName">
<a-input placeholder="请输入规则名称" v-model="queryParam.ruleName"></a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="规则Code">
<a-form-model-item label="规则Code" prop="ruleCode">
<a-input placeholder="请输入规则Code" v-model="queryParam.ruleCode"></a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
@ -22,7 +22,7 @@
</span>
</a-col>
</a-row>
</a-form>
</a-form-model>
</div>
<!-- 操作按钮区域 -->

View File

@ -0,0 +1,158 @@
<template>
<a-card :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="12">
<a-form-item label="账号">
<a-input placeholder="请输入账号查询" v-model="queryParam.username"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 查询区域-END -->
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{ selectedRowKeys.length }}</a>项
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table
ref="table"
size="middle"
:scroll="{x:true}"
bordered
rowKey="token"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
class="j-table-force-nowrap"
@change="handleTableChange">
<template slot="avatarslot" slot-scope="text, record, index">
<div class="anty-img-wrap">
<a-avatar shape="square" :src="getAvatarView(record.avatar)" icon="user"/>
</div>
</template>
<span slot="action" slot-scope="text, record">
<a-popconfirm title="强制退出用户" @confirm="() => handleForce(record)">
<a-button type="danger">强退</a-button>
</a-popconfirm>
</span>
</a-table>
</div>
</a-card>
</template>
<script>
import '@/assets/less/TableExpand.less'
import { mixinDevice } from '@/utils/mixin'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { forceLogout } from '@/api/login'
import {filterDictTextByCache} from '@/components/dict/JDictSelectUtil'
import {getFileAccessHttpUrl} from '@/api/manage';
export default {
name: "SysOnlineList",
mixins:[JeecgListMixin, mixinDevice],
components: {},
data () {
return {
description: '在线用户管理页面',
queryParam: {
username: ''
},
// 表头
columns: [
{
title:'用户账号',
align:"center",
dataIndex: 'username'
},{
title:'用户姓名',
align:"center",
dataIndex: 'realname'
},{
title: '头像',
align: "center",
width: 120,
dataIndex: 'avatar',
scopedSlots: {customRender: "avatarslot"}
},{
title:'生日',
align:"center",
dataIndex: 'birthday'
},{
title: '性别',
align: "center",
dataIndex: 'sex',
customRender: (text) => {
//字典值翻译通用方法
return filterDictTextByCache('sex', text);
}
},{
title:'手机号',
align:"center",
dataIndex: 'phone'
},{
title: '操作',
dataIndex: 'action',
scopedSlots: {customRender: 'action'},
align: "center",
width: 170
}
],
url: {
list: "/sys/online/list"
},
dictOptions:{},
}
},
created() {
},
computed: {
importExcelUrl: function(){
return `${window._CONFIG['domianURL']}/${this.url.importExcelUrl}`;
},
},
methods: {
getAvatarView: function (avatar) {
return getFileAccessHttpUrl(avatar)
},
handleForce(record) {
let that = this;
let forceParam = {
token: record.token
}
return forceLogout(forceParam).then((res) => {
if (res.success) {
that.loadData();
this.$message.success('强制退出用户”'+record.realname+'“成功!');
} else {
that.$message.warning(res.message);
}
})
}
}
}
</script>
<style scoped>
@import '~@assets/less/common.less';
</style>

View File

@ -65,11 +65,12 @@
<!-- 操作按钮区域 -->
<div class="table-operator" style="border-top: 5px">
<a-button @click="handleAdd" type="primary" icon="plus">添加用户</a-button>
<a-button @click="handleAdd" type="primary" icon="plus" >添加用户</a-button>
<a-button type="primary" icon="download" @click="handleExportXls('用户信息')">导出</a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button type="primary" icon="import">导入</a-button>
</a-upload>
<j-third-app-button biz-type="user" :selected-row-keys="selectedRowKeys" syncToApp syncToLocal @sync-finally="onSyncFinally"/>
<a-button type="primary" icon="hdd" @click="recycleBinVisible=true">回收站</a-button>
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay" @click="handleMenuClick">
@ -120,9 +121,9 @@
</template>
<span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">编辑</a>
<a @click="handleEdit(record)" >编辑</a>
<a-divider type="vertical"/>
<a-divider type="vertical" />
<a-dropdown>
<a class="ant-dropdown-link">
@ -154,6 +155,11 @@
<a>解冻</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item>
<a href="javascript:;" @click="handleAgentSettings(record.username)">代理人</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
@ -167,6 +173,8 @@
<password-modal ref="passwordmodal" @ok="passwordModalOk"></password-modal>
<sys-user-agent-modal ref="sysUserAgentModal"></sys-user-agent-modal>
<!-- 用户回收站 -->
<user-recycle-bin-modal :visible.sync="recycleBinVisible" @ok="modalFormOk"/>
@ -179,14 +187,18 @@
import {putAction,getFileAccessHttpUrl} from '@/api/manage';
import {frozenBatch} from '@/api/api'
import {JeecgListMixin} from '@/mixins/JeecgListMixin'
import SysUserAgentModal from "./modules/SysUserAgentModal";
import JInput from '@/components/jeecg/JInput'
import UserRecycleBinModal from './modules/UserRecycleBinModal'
import JSuperQuery from '@/components/jeecg/JSuperQuery'
import JThirdAppButton from '@/components/jeecgbiz/thirdApp/JThirdAppButton'
export default {
name: "UserList",
mixins: [JeecgListMixin],
components: {
JThirdAppButton,
SysUserAgentModal,
UserModal,
PasswordModal,
JInput,
@ -366,9 +378,19 @@
handleChangePassword(username) {
this.$refs.passwordmodal.show(username);
},
handleAgentSettings(username){
this.$refs.sysUserAgentModal.agentSettings(username);
this.$refs.sysUserAgentModal.title = "用户代理人设置";
},
passwordModalOk() {
//TODO 密码修改完成 不需要刷新页面可以把datasource中的数据更新一下
}
},
onSyncFinally({isToLocal}) {
// 同步到本地时刷新下数据
if (isToLocal) {
this.loadData()
}
},
}
}

View File

@ -1,20 +1,20 @@
<template>
<a-card class="j-address-list-right-card-box" :loading="cardLoading" :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-form-model layout="inline" :model="queryParam">
<a-row :gutter="10">
<a-col :md="6" :sm="12">
<a-form-item label="姓名" style="margin-left:8px">
<a-form-model-item label="姓名" prop="realname" style="margin-left:8px">
<a-input placeholder="请输入姓名查询" v-model="queryParam.realname"></a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col :md="6" :sm="12">
<a-form-item label="工号" style="margin-left:8px">
<a-form-model-item label="工号" prop="workNo" style="margin-left:8px">
<a-input placeholder="请输入工号查询" v-model="queryParam.workNo"></a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
@ -24,7 +24,7 @@
</a-col>
</span>
</a-row>
</a-form>
</a-form-model>
</div>
<a-table

View File

@ -109,7 +109,8 @@
visible: false,
condition:true,
disableSubmit:false,
model: {
model: {},
defaultModel:{
departOrder:0,
orgCategory:'1'
},
@ -165,6 +166,7 @@
},
edit (record) {
this.visible = true;
this.model = Object.assign({}, this.defaultModel, record)
this.loadTreeData();
this.model.parentId = record!=null?record.toString():null;
if(this.seen){

View File

@ -1,33 +1,33 @@
<template>
<a-card :visible="visible">
<a-form :form="form">
<a-form-item
<a-form-model ref="form" :model="model">
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构名称">
<a-input style="border:0px;" placeholder="" v-decorator="['departName', {}]"/>
</a-form-item>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门">
<a-input style="border:0;" placeholder="" v-model="model.departName"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="上级部门">
<a-tree-select
disabled
style="width:100%;border: 0px;border: none;outline:none;"
style="width:100%;border: 0;border: none;outline:none;"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="treeData"
v-model="model.parentId"
placeholder="">
</a-tree-select>
</a-form-item>
<a-form-item
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构编码">
<a-input style="border:0px;" placeholder="" v-decorator="['orgCode', {}]"/>
</a-form-item>
<a-form-item
<a-input style="border:0;" placeholder="" v-model="model.orgCode"/>
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="机构类型">
<a-radio-group :disabled="true" v-decorator="['orgCategory',{}]" placeholder="请选择机构类型">
<a-radio-group :disabled="true" v-model="model.orgCategory" read-only>
<a-radio value="1">
公司
</a-radio>
@ -38,37 +38,36 @@
岗位
</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="排序">
<a-input-number style="border:0px;" v-decorator="[ 'departOrder',{}]"/>
</a-form-item>
<a-form-item
<a-input-number style="border:0;" v-model="model.departOrder"/>
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="手机号">
<a-input style="border:0px;" placeholder="" v-decorator="['mobile', {}]"/>
</a-form-item>
<a-form-item
<a-input style="border:0;" placeholder="" v-model="model.mobile"/>
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="地址">
<a-input style="border:0px;" placeholder="" v-decorator="['address', {}]"/>
</a-form-item>
<a-form-item
<a-input style="border:0;" placeholder="" v-model="model.address"/>
</a-form-model-item>
<a-form-model-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="备注">
<a-textarea style="border:0px;" placeholder="" v-decorator="['memo', {}]"/>
</a-form-item>
</a-form>
<a-textarea style="border:0;" placeholder="" v-model="model.memo"/>
</a-form-model-item>
</a-form-model>
</a-card>
</template>
<script>
import pick from 'lodash.pick'
import {queryIdTree} from '@/api/api'
import { queryIdTree } from '@/api/api'
export default {
name: 'DeptBaseInfo',
@ -81,7 +80,6 @@
visible: false,
disable: true,
treeData: [],
form: this.$form.createForm(this),
labelCol: {
xs: {span: 24},
sm: {span: 3}
@ -108,17 +106,14 @@
})
},
open(record) {
this.form.resetFields();
this.model = Object.assign({}, record);
this.visible = true;
console.log("record:");
console.log(record);
this.$nextTick(() => {
this.form.setFieldsValue(pick(record, 'orgCategory','departName', 'parentId', 'orgCode', 'departOrder', 'mobile', 'fax', 'address', 'memo'));
});
this.$refs.form.resetFields()
this.model = Object.assign({}, record)
})
},
clearForm() {
this.form.resetFields();
this.$refs.form.resetFields();
this.treeData = [];
},
}

View File

@ -10,60 +10,38 @@
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="任务类名"
hasFeedback >
<a-input placeholder="请输入任务类名" v-decorator="['jobClassName', {rules: [{ required: true, message: '请输入任务类名!' }]}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="cron表达式">
<!-- <a-input placeholder="请输入cron表达式" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?',rules: [{ required: true, message: '请输入任务类名!' }]}]" />-->
<!-- <a target="_blank" href="http://cron.qqe2.com/">-->
<!-- <a-icon type="share-alt" />-->
<!-- 在线cron表达式生成-->
<!-- </a>-->
<!-- <j-cron ref="innerVueCron" v-decorator="['cronExpression', {'initialValue':'0/1 * * * * ?',rules: [{ required: true, message: '请输入cron表达式!' }]}]" @change="setCorn"></j-cron>-->
<j-cron ref="innerVueCron" v-decorator="['cronExpression', { initialValue: '* * * * * ? *' }]" @change="setCorn"></j-cron>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="参数">
<a-textarea placeholder="请输入参数" :rows="5" v-decorator="['parameter', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="描述">
<a-textarea placeholder="请输入描述" :rows="3" v-decorator="['description', {}]" />
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="状态">
<j-dict-select-tag type="radioButton" v-decorator="[ 'status', {'initialValue':0}]" :trigger-change="true" dictCode="quartz_status"/>
</a-form-item>
</a-form>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="任务类名" prop="jobClassName" hasFeedback >
<a-input placeholder="请输入任务类名" v-model="model.jobClassName" />
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="Cron表达式" prop="cronExpression">
<!-- <j-cron v-model="model.cronExpression"/>-->
<j-easy-cron v-model="model.cronExpression" />
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="参数" prop="parameter" >
<a-textarea placeholder="请输入参数" :rows="5" v-model="model.parameter" />
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="描述" prop="description">
<a-textarea placeholder="请输入描述" :rows="3" v-model="model.description" />
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="状态" prop="status">
<j-dict-select-tag type="radioButton" v-model="model.status" dictCode="quartz_status"/>
</a-form-model-item>
</a-form-model>
</a-spin>
</a-modal>
</template>
<script>
import { httpAction } from '@/api/manage'
import JCron from "@/components/jeecg/JCron";
import pick from 'lodash.pick'
// import moment from "moment"
// import JCron from "@/components/jeecg/JCron";
import cronValidator from "@/components/jeecg/JEasyCron/validator";
export default {
name: "QuartzJobModal",
components: {
JCron
// JCron,
},
data () {
return {
@ -84,13 +62,12 @@
value: ''
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {
cron: {
rules: [{
required: true, message: '请输入cron表达式!'
}]
}
cronExpression: [
{required: true, message: '请输入cron表达式!'},
{validator: cronValidator,}
],
jobClassName: [{required: true, message: '请输入任务类名!'}]
},
url: {
add: "/sys/quartzJob/add",
@ -101,19 +78,19 @@
created () {
},
methods: {
add () {
this.edit({});
add() {
// 统一设置默认值
this.edit({
cronExpression: '* * * * * ? *',
status: 0,
})
},
edit (record) {
let that = this;
that.form.resetFields();
this.model = Object.assign({},record);
console.log(this.model)
this.visible = true;
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model,'jobClassName','cronExpression','parameter','description','status'));
});
this.$refs.form.resetFields()
this.model = Object.assign({}, record)
})
},
close () {
this.$emit('close');
@ -122,14 +99,8 @@
handleOk () {
const that = this;
// 触发表单验证
this.form.validateFields((err, values) => {
console.log('values',values)
if (!err) {
if (typeof values.cronExpression == "undefined" || Object.keys(values.cronExpression).length==0 ) {
this.$message.warning('请输入cron表达式!');
return false;
}
this.$refs.form.validate((ok, err) => {
if (ok) {
that.confirmLoading = true;
let httpurl = '';
let method = '';
@ -140,20 +111,18 @@
httpurl+=this.url.edit;
method = 'put';
}
let formData = Object.assign(this.model, values);
//时间格式化
console.log('提交参数',formData)
httpAction(httpurl,formData,method).then((res)=>{
console.log('提交参数',this.model)
httpAction(httpurl,this.model,method).then((res)=>{
if(res.success){
that.$message.success(res.message);
that.$emit('ok');
that.close();
}else{
that.$message.warning(res.message);
}
}).finally(() => {
that.confirmLoading = false;
that.close();
})
}
@ -162,24 +131,6 @@
handleCancel () {
this.close()
},
setCorn(data){
console.log('data)',data);
this.$nextTick(() => {
this.model.cronExpression = data;
})
// console.log(Object.keys(data).length==0);
if (Object.keys(data).length==0) {
this.$message.warning('请输入cron表达式!');
}
},
validateCron(rule, value, callback){
if(!value){
callback()
}else if (Object.keys(value).length==0) {
callback("请输入cron表达式!");
}
},
}
}

View File

@ -9,41 +9,24 @@
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则名称">
<a-input placeholder="请输入规则名称" v-decorator="['ruleName', validatorRules.ruleName]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则Code">
<a-input placeholder="请输入规则Code" v-decorator="['ruleCode', validatorRules.ruleCode]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则描述">
<a-textarea placeholder="请输入规则描述" v-decorator="['ruleDescription', {}]"/>
</a-form-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则名称" prop="ruleName" >
<a-input placeholder="请输入规则名称" v-model="model.ruleName"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则Code" prop="ruleCode">
<a-input placeholder="请输入规则Code" v-model="model.ruleCode"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则描述" prop="ruleDescription">
<a-textarea placeholder="请输入规则描述" v-model="model.ruleDescription"/>
</a-form-model-item>
</a-form>
</a-form-model>
<!-- 规则设计 -->
<a-tabs v-model="tabs.activeKey">
<a-tab-pane tab="局部规则" :key="tabs.design.key" forceRender>
<a-alert type="info" showIcon message="局部规则按照你输入的位数有序的校验。"/>
<j-editable-table
ref="designTable"
dragSort
rowNumber
:maxHeight="240"
:columns="tabs.design.columns"
:dataSource="tabs.design.dataSource"
style="margin-top: 8px;"
>
<j-editable-table ref="designTable" dragSort rowNumber :maxHeight="240" :columns="tabs.design.columns" :dataSource="tabs.design.dataSource" style="margin-top: 8px;">
<template #action="props">
<my-action-button :rowEvent="props"/>
@ -51,16 +34,9 @@
</j-editable-table>
</a-tab-pane>
<a-tab-pane tab="全局规则" :key="tabs.global.key" forceRender>
<j-editable-table
ref="globalTable"
dragSort
rowNumber
actionButton
:maxHeight="240"
:columns="tabs.global.columns"
:dataSource="tabs.global.dataSource"
>
<j-editable-table ref="globalTable" dragSort rowNumber actionButton :maxHeight="240" :columns="tabs.global.columns" :dataSource="tabs.global.dataSource">
<template #actionButtonAfter>
<a-alert type="info" showIcon message="全局规则可校验用户输入的所有字符;全局规则的优先级比局部规则的要高。" style="margin-bottom: 8px;"/>
@ -146,15 +122,12 @@
sm: { span: 16 },
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {
ruleName: { rules: [{ required: true, message: '请输入规则名称!' },] },
ruleCode: {
rules: [
{ required: true, message: '请输入规则Code!' },
{ validator: (rule, value, callback) => validateDuplicateValue('sys_check_rule', 'rule_code', value, this.model.id, callback) }
]
},
ruleName: [{required: true, message: '请输入规则名称!'}],
ruleCode: [
{required: true, message: '请输入规则Code!'},
{validator: (rule, value, callback) => validateDuplicateValue('sys_check_rule', 'rule_code', value, this.model.id, callback)}
],
},
tabs: {
activeKey: 'design',
@ -272,14 +245,14 @@
this.edit({})
},
edit(record) {
this.form.resetFields()
this.tabs.activeKey = this.tabs.design.key
this.tabs.global.dataSource = []
this.tabs.design.dataSource = [{ digits: '', pattern: '', message: '' }]
this.model = Object.assign({}, record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'ruleName', 'ruleCode', 'ruleDescription'))
this.$refs.form.resetFields()
this.model = Object.assign({}, record)
// 子表数据
let ruleJson = this.model.ruleJson
if (ruleJson) {
@ -307,7 +280,7 @@
Promise.all([
// 主表单校验
alwaysResolve(new Promise((resolve, reject) => {
this.form.validateFields((error, values) => error ? reject(error) : resolve(values))
this.$refs.form.validate((ok, err) => ok ? resolve(this.model) : reject(err))
})),
// 局部规则子表校验
alwaysResolve(this.$refs.designTable.getValuesPromise),

View File

@ -10,40 +10,27 @@
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则名称">
<a-input placeholder="请输入规则名称" v-decorator="['ruleName', validatorRules.ruleName]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则Code">
<a-input placeholder="请输入规则Code" :disabled="disabledCode" v-decorator="['ruleCode', validatorRules.ruleCode]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则实现类">
<a-input placeholder="请输入规则实现类" v-decorator="['ruleClass', validatorRules.ruleClass]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="规则参数">
<a-textarea placeholder="请输入规则参数" :rows="5" v-decorator="['ruleParams', validatorRules.ruleParams]"/>
</a-form-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则名称" prop="ruleName">
<a-input placeholder="请输入规则名称" v-model="model.ruleName"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则Code" prop="ruleCode" >
<a-input placeholder="请输入规则Code" :disabled="disabledCode" v-model="model.ruleCode"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则实现类" prop="ruleClass" >
<a-input placeholder="请输入规则实现类" v-model="model.ruleClass"/>
</a-form-model-item>
<a-form-model-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则参数" prop="ruleParams">
<a-textarea placeholder="请输入规则参数" :rows="5" v-model="model.ruleParams"/>
</a-form-model-item>
</a-form>
</a-form-model>
</a-spin>
</a-modal>
</template>
<script>
import pick from 'lodash.pick'
import { httpAction } from '@/api/manage'
import { validateDuplicateValue } from '@/utils/util'
@ -59,35 +46,30 @@
wrapperCol: { xs: { span: 24 }, sm: { span: 16 } },
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {
ruleName: { rules: [{ required: true, message: '规则名称不能为空' }] },
ruleCode: {
rules: [
{ required: true, message: '规则Code不能为空' },
{ validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
]
},
ruleClass: { rules: [{ required: true, message: '规则实现类不能为空' }] },
ruleParams: {
rules: [{
validator: (rule, value, callback) => {
ruleName: [{ required: true, message: '规则名称不能为空' }],
ruleCode: [
{ required: true, message: '规则Code不能为空' },
{ validator: (rule, value, callback) => validateDuplicateValue('sys_fill_rule', 'rule_code', value, this.model.id, callback) }
],
ruleClass: [{ required: true, message: '规则实现类不能为空' }],
ruleParams: [{
validator: (rule, value, callback) => {
try {
let json = JSON.parse(value)
if (json instanceof Array) {
callback('只能传递JSON对象不能传递JSON数组')
} else if (json instanceof Object) {
callback()
} else {
callback('请输入JSON字符串')
}
} catch {
try {
let json = JSON.parse(value)
if (json instanceof Array) {
callback('只能传递JSON对象不能传递JSON数组')
} else if (json instanceof Object) {
callback()
} else {
callback('请输入JSON字符串')
}
} catch {
callback('请输入JSON字符串')
}
}]
},
}
}],
},
url: {
add: '/sys/fillRule/add',
@ -107,11 +89,10 @@
this.edit({})
},
edit(record) {
this.form.resetFields()
this.model = Object.assign({}, record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'ruleName', 'ruleCode', 'ruleClass', 'ruleParams'))
this.$refs.form.resetFields()
this.model = Object.assign({}, record)
})
},
close() {
@ -121,8 +102,8 @@
handleOk() {
const that = this
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
this.$refs.form.validate((ok, err) => {
if (ok) {
that.confirmLoading = true
let httpUrl = this.url.add, method = 'post'
if (this.model.id) {
@ -130,17 +111,16 @@
method = 'put'
}
let formData = Object.assign(this.model, values)
httpAction(httpUrl, formData, method).then((res) => {
httpAction(httpUrl, this.model, method).then((res) => {
if (res.success) {
that.$message.success(res.message)
that.$emit('ok')
that.close()
} else {
that.$message.warning(res.message)
}
}).finally(() => {
that.confirmLoading = false
that.close()
})
}
})

View File

@ -112,7 +112,7 @@
</a-form-model-item>
<a-form-model-item label="手机号码" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="phone">
<a-input placeholder="请输入手机号码" :disabled="isDisabledAuth('user:form:phone')" v-model="model.phone" />
<a-input placeholder="请输入手机号码" v-model="model.phone" />
</a-form-model-item>
<a-form-model-item label="座机" :labelCol="labelCol" :wrapperCol="wrapperCol" prop="telephone">
@ -168,7 +168,7 @@
confirmpassword: [{required: true, message: '请重新输入登录密码!',},
{ validator: this.compareToFirstPassword,}],
realname:[{ required: true, message: '请输入用户名称!' }],
phone: [{validator: this.validatePhone}],
phone: [{required: true, message: '请输入手机号!'}, {validator: this.validatePhone}],
email: [{validator: this.validateEmail}],
roles:{},
workNo:[ { required: true, message: '请输入工号' },

View File

@ -1,310 +1,131 @@
<template>
<div class="main">
<a-form :form="form" class="user-layout-login" ref="formLogin" id="formLogin">
<a-tabs
:activeKey="customActiveKey"
:tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }"
@change="handleTabClick">
<a-form-model class="user-layout-login" @keyup.enter.native="handleSubmit">
<a-tabs :activeKey="customActiveKey" :tabBarStyle="{ textAlign: 'center', borderBottom: 'unset' }" @change="handleTabClick">
<a-tab-pane key="tab1" tab="账号密码登录">
<a-form-item>
<a-input
size="large"
v-decorator="['username',validatorRules.username,{ validator: this.handleUsernameOrEmail }]"
type="text"
placeholder="请输入帐户名 / admin">
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
<a-form-item>
<a-input
v-decorator="['password',validatorRules.password]"
size="large"
type="password"
autocomplete="false"
placeholder="密码 / 123456">
<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="16">
<a-form-item>
<a-input
v-decorator="['inputCode',validatorRules.inputCode]"
size="large"
type="text"
@change="inputCodeChange"
placeholder="请输入验证码">
<a-icon slot="prefix" type="smile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-col>
<a-col :span="8" 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>
<login-account ref="alogin" @validateFail="validateFail" @success="requestSuccess" @fail="requestFailed"></login-account>
</a-tab-pane>
<a-tab-pane key="tab2" tab="手机号登录">
<a-form-item>
<a-input
v-decorator="['mobile',validatorRules.mobile]"
size="large"
type="text"
placeholder="手机号">
<a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
<a-row :gutter="16">
<a-col class="gutter-row" :span="16">
<a-form-item>
<a-input
v-decorator="['captcha',validatorRules.captcha]"
size="large"
type="text"
placeholder="请输入验证码">
<a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-col>
<a-col class="gutter-row" :span="8">
<a-button
class="getCaptcha"
tabindex="-1"
:disabled="state.smsSendBtn"
@click.stop.prevent="getCaptcha"
v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"></a-button>
</a-col>
</a-row>
<a-tab-pane key="tab2" tab="手机号登录">
<login-phone ref="plogin" @validateFail="validateFail" @success="requestSuccess" @fail="requestFailed"></login-phone>
</a-tab-pane>
</a-tabs>
<a-form-item>
<a-checkbox v-decorator="['rememberMe', {initialValue: true, valuePropName: 'checked'}]" >自动登录</a-checkbox>
<a-form-model-item>
<a-checkbox @change="handleRememberMeChange" default-checked>自动登录</a-checkbox>
<router-link :to="{ name: 'alteration'}" class="forge-password" style="float: right;">
忘记密码
</router-link>
<router-link :to="{ name: 'register'}" class="forge-password" style="float: right;margin-right: 10px" >
<router-link :to="{ name: 'register'}" class="forge-password" style="float: right;margin-right: 10px" >
注册账户
</router-link>
</a-form-item>
</a-form-model-item>
<a-form-item style="margin-top:24px">
<a-button
size="large"
type="primary"
htmlType="submit"
class="login-button"
:loading="loginBtn"
@click.stop.prevent="handleSubmit"
:disabled="loginBtn">确定
<a-button size="large" type="primary" htmlType="submit" class="login-button" :loading="loginBtn" @click.stop.prevent="handleSubmit" :disabled="loginBtn">确定
</a-button>
</a-form-item>
</a-form>
<two-step-captcha
v-if="requiredTwoStepCaptcha"
:visible="stepCaptchaVisible"
@success="stepCaptchaSuccess"
@cancel="stepCaptchaCancel"></two-step-captcha>
</a-form-model>
<two-step-captcha v-if="requiredTwoStepCaptcha" :visible="stepCaptchaVisible" @success="stepCaptchaSuccess" @cancel="stepCaptchaCancel"></two-step-captcha>
<login-select-tenant ref="loginSelect" @success="loginSelectOk"></login-select-tenant>
<third-login ref="thirdLogin"></third-login>
</div>
</template>
<script>
//import md5 from "md5"
import api from '@/api'
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { mapActions } from "vuex"
import { timeFix } from "@/utils/util"
import Vue from 'vue'
import { ACCESS_TOKEN ,ENCRYPTED_STRING} from "@/store/mutation-types"
import { putAction,postAction,getAction } from '@/api/manage'
import { encryption , getEncryptedString } from '@/utils/encryption/aesEncrypt'
import store from '@/store/'
import { USER_INFO } from "@/store/mutation-types"
import ThirdLogin from './third/ThirdLogin'
import LoginSelectTenant from "./LoginSelectTenant";
import LoginSelectTenant from "./LoginSelectTenant"
import TwoStepCaptcha from '@/components/tools/TwoStepCaptcha'
import { encryption , getEncryptedString } from '@/utils/encryption/aesEncrypt'
import { timeFix } from "@/utils/util"
import LoginAccount from './LoginAccount'
import LoginPhone from './LoginPhone'
export default {
components: {
LoginSelectTenant,
TwoStepCaptcha,
ThirdLogin
ThirdLogin,
LoginAccount,
LoginPhone
},
data () {
return {
customActiveKey: "tab1",
customActiveKey: 'tab1',
rememberMe: true,
loginBtn: false,
// login type: 0 email, 1 username, 2 telephone
loginType: 0,
requiredTwoStepCaptcha: false,
stepCaptchaVisible: false,
form: this.$form.createForm(this),
encryptedString:{
key:"",
iv:"",
},
state: {
time: 60,
smsSendBtn: false,
},
validatorRules:{
username:{rules: [{ required: true, message: '请输入用户名!'},{validator: this.handleUsernameOrEmail}]},
password:{rules: [{ required: true, message: '请输入密码!',validator: 'click'}]},
mobile:{rules: [{validator:this.validateMobile}]},
captcha:{rule: [{ required: true, message: '请输入验证码!'}]},
inputCode:{rules: [{ required: true, message: '请输入验证码!'}]}
},
verifiedCode:"",
inputCodeContent:"",
inputCodeNull:true,
currentUsername:"",
currdatetime:'',
randCodeImage:'',
requestCodeSuccess:false,
}
},
created () {
this.currdatetime = new Date().getTime();
created() {
Vue.ls.remove(ACCESS_TOKEN)
this.getRouterData();
this.handleChangeCheckCode();
// update-begin- --- author:scott ------ date:20190805 ---- for:密码加密逻辑暂时注释掉,有点问题
//this.getEncrypte();
// update-end- --- author:scott ------ date:20190805 ---- for:密码加密逻辑暂时注释掉,有点问题
this.rememberMe = true
},
methods: {
...mapActions(['Login', 'Logout', 'PhoneLogin']),
// handler
handleUsernameOrEmail (rule, value, callback) {
const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/;
if (regex.test(value)) {
this.loginType = 0
} else {
this.loginType = 1
}
callback()
},
handleTabClick (key) {
methods:{
handleTabClick(key){
this.customActiveKey = key
// this.form.resetFields()
},
handleRememberMeChange(e){
this.rememberMe = e.target.checked
},
/**跳转到登录页面的参数-账号获取*/
getRouterData(){
this.$nextTick(() => {
let temp = this.$route.params.username || this.$route.query.username || ''
if (temp) {
this.$refs.alogin.acceptUsername(temp)
}
})
},
//登录
handleSubmit () {
let that = this
let loginParams = {};
that.loginBtn = true;
// 使用账户密码登录
if (that.customActiveKey === 'tab1') {
that.form.validateFields([ 'username', 'password','inputCode', 'rememberMe' ], { force: true }, (err, values) => {
if (!err) {
loginParams.username = values.username
// update-begin- --- author:scott ------ date:20190805 ---- for:密码加密逻辑暂时注释掉,有点问题
//loginParams.password = md5(values.password)
//loginParams.password = encryption(values.password,that.encryptedString.key,that.encryptedString.iv)
loginParams.password = values.password
loginParams.remember_me = values.rememberMe
// update-begin- --- author:scott ------ date:20190805 ---- for:密码加密逻辑暂时注释掉,有点问题
loginParams.captcha = that.inputCodeContent
loginParams.checkKey = that.currdatetime
console.log("登录参数",loginParams)
that.Login(loginParams).then((res) => {
this.$refs.loginSelect.show(res.result)
}).catch((err) => {
that.requestFailed(err);
});
}else {
that.loginBtn = false;
}
})
// 使用手机号登录
this.loginBtn = true;
if (this.customActiveKey === 'tab1') {
// 使用账户密码登录
this.$refs.alogin.handleLogin(this.rememberMe)
} else {
that.form.validateFields([ 'mobile', 'captcha', 'rememberMe' ], { force: true }, (err, values) => {
if (!err) {
loginParams.mobile = values.mobile
loginParams.captcha = values.captcha
loginParams.remember_me = values.rememberMe
that.PhoneLogin(loginParams).then((res) => {
console.log(res.result);
this.$refs.loginSelect.show(res.result)
}).catch((err) => {
that.requestFailed(err);
})
}
})
//手机号码登录
this.$refs.plogin.handleLogin(this.rememberMe)
}
},
getCaptcha (e) {
e.preventDefault();
let that = this;
this.form.validateFields([ 'mobile' ], { force: true },(err,values) => {
if(!values.mobile){
that.cmsFailed("请输入手机号");
}else if (!err) {
this.state.smsSendBtn = true;
let interval = window.setInterval(() => {
if (that.state.time-- <= 0) {
that.state.time = 60;
that.state.smsSendBtn = false;
window.clearInterval(interval);
}
}, 1000);
const hide = this.$message.loading('验证码发送中..', 0);
let smsParams = {};
smsParams.mobile=values.mobile;
smsParams.smsmode="0";
postAction("/sys/sms",smsParams)
.then(res => {
if(!res.success){
setTimeout(hide, 0);
this.cmsFailed(res.message);
}
console.log(res);
setTimeout(hide, 500);
})
.catch(err => {
setTimeout(hide, 1);
clearInterval(interval);
that.state.time = 60;
that.state.smsSendBtn = false;
this.requestFailed(err);
});
}
}
);
// 校验失败
validateFail(){
this.loginBtn = false;
},
stepCaptchaSuccess () {
// 登录后台成功
requestSuccess(loginResult){
this.$refs.loginSelect.show(loginResult)
},
//登录后台失败
requestFailed (err) {
let description = ((err.response || {}).data || {}).message || err.message || "请求出现错误请稍后再试"
this.$notification[ 'error' ]({
message: '登录失败',
description: description,
duration: 4,
});
//账户密码登录错误后更新验证码
if(this.customActiveKey === 'tab1' && description.indexOf('密码错误')>0){
this.$refs.alogin.handleChangeCheckCode()
}
this.loginBtn = false;
},
loginSelectOk(){
this.loginSuccess()
},
stepCaptchaCancel () {
this.Logout().then(() => {
this.loginBtn = false
this.stepCaptchaVisible = false
})
},
handleChangeCheckCode(){
this.form.setFieldsValue({inputCode: ''});
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
})
},
//登录成功
loginSuccess () {
this.$router.push({ path: "/dashboard/analysis" }).catch(()=>{
console.log('登录跳转首页出错,这个错误从哪里来的')
@ -314,88 +135,44 @@
description: `${timeFix()},欢迎回来`,
});
},
cmsFailed(err){
this.$notification[ 'error' ]({
message: "登录失败",
description:err,
duration: 4,
});
},
requestFailed (err) {
let errMsg=((err.response || {}).data || {}).message || err.message || "请求出现错误请稍后再试";
this.$notification[ 'error' ]({
message: '登录失败',
description: errMsg,
duration: 4,
});
//密码错误后更新验证码
if(errMsg.indexOf('密码错误')>0){
this.handleChangeCheckCode();
}
this.loginBtn = false;
},
validateMobile(rule,value,callback){
if (!value || new RegExp(/^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$/).test(value)){
callback();
}else{
callback("您的手机号码格式不正确!");
}
},
validateInputCode(rule,value,callback){
if(!value || this.verifiedCode==this.inputCodeContent){
callback();
}else{
callback("您输入的验证码不正确!");
}
},
generateCode(value){
this.verifiedCode = value.toLowerCase()
},
inputCodeChange(e){
this.inputCodeContent = e.target.value
},
loginSelectOk(){
stepCaptchaSuccess () {
this.loginSuccess()
},
getRouterData(){
this.$nextTick(() => {
if (this.$route.params.username) {
this.form.setFieldsValue({
'username': this.$route.params.username
stepCaptchaCancel () {
this.Logout().then(() => {
this.loginBtn = false
this.stepCaptchaVisible = false
})
},
//获取密码加密规则
getEncrypte(){
var encryptedString = Vue.ls.get(ENCRYPTED_STRING);
if(encryptedString == null){
getEncryptedString().then((data) => {
this.encryptedString = data
});
}else{
this.encryptedString = encryptedString;
}
})
},
//获取密码加密规则
getEncrypte(){
var encryptedString = Vue.ls.get(ENCRYPTED_STRING);
if(encryptedString == null){
getEncryptedString().then((data) => {
this.encryptedString = data
});
}else{
this.encryptedString = encryptedString;
}
},
}
}
</script>
<style lang="less" scoped>
.user-layout-login {
label {
font-size: 14px;
}
.getCaptcha {
.getCaptcha {
display: block;
width: 100%;
height: 40px;
}
.forge-password {
.forge-password {
font-size: 14px;
}
@ -406,12 +183,12 @@
width: 100%;
}
.user-login-other {
.user-login-other {
text-align: left;
margin-top: 24px;
line-height: 22px;
.item-icon {
.item-icon {
font-size: 24px;
color: rgba(0,0,0,.2);
margin-left: 16px;
@ -419,17 +196,16 @@
cursor: pointer;
transition: color .3s;
&:hover {
&:hover {
color: #1890ff;
}
}
.register {
.register {
float: right;
}
}
}
</style>
<style>
.valid-error .ant-select-selection__placeholder{

View File

@ -0,0 +1,155 @@
<template>
<div>
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-model-item required prop="username">
<a-input v-model="model.username" size="large" placeholder="请输入帐户名 / jeecg">
<a-icon slot="prefix" type="user" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-model-item>
<a-form-model-item required prop="password">
<a-input v-model="model.password" size="large" type="password" autocomplete="false" placeholder="请输入密码 / 123456">
<a-icon slot="prefix" type="lock" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-model-item>
<a-row :gutter="0">
<a-col :span="16">
<a-form-model-item required prop="inputCode">
<a-input v-model="model.inputCode" size="large" type="text" placeholder="请输入验证码">
<a-icon slot="prefix" type="smile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-model-item>
</a-col>
<a-col :span="8" 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-model>
</div>
</template>
<script>
import { getAction } from '@/api/manage'
import Vue from 'vue'
import { mapActions } from 'vuex'
export default {
name: 'LoginAccount',
data(){
return {
requestCodeSuccess: false,
randCodeImage: '',
currdatetime: '',
loginType: 0,
model:{
username: '',
password: '',
inputCode: ''
},
validatorRules:{
username: [
{ required: true, message: '请输入用户名!' },
{ validator: this.handleUsernameOrEmail }
],
password: [{
required: true, message: '请输入密码!', validator: 'click'
}],
inputCode: [{
required: true, message: '请输入验证码!'
}]
}
}
},
created() {
this.handleChangeCheckCode();
},
methods:{
...mapActions(['Login']),
/**刷新验证码*/
handleChangeCheckCode(){
this.currdatetime = new Date().getTime();
this.model.inputCode = ''
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
})
},
// 判断登录类型
handleUsernameOrEmail (rule, value, callback) {
const regex = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+((\.[a-zA-Z0-9_-]{2,3}){1,2})$/;
if (regex.test(value)) {
this.loginType = 0
} else {
this.loginType = 1
}
callback()
},
/**
* 验证字段
* @param arr
* @param callback
*/
validateFields(arr, callback){
let promiseArray = []
for(let item of arr){
let p = new Promise((resolve, reject) => {
this.$refs['form'].validateField(item, (err)=>{
if(!err){
resolve();
}else{
reject(err);
}
})
});
promiseArray.push(p)
}
Promise.all(promiseArray).then(()=>{
callback()
}).catch(err=>{
callback(err)
})
},
acceptUsername(username){
this.model['username'] = username
},
//账号密码登录
handleLogin(rememberMe){
this.validateFields([ 'username', 'password', 'inputCode' ], (err)=>{
if(!err){
let loginParams = {
username: this.model.username,
password: this.model.password,
captcha: this.model.inputCode,
checkKey: this.currdatetime,
remember_me: rememberMe,
}
console.log("登录参数", loginParams)
this.Login(loginParams).then((res) => {
this.$emit('success', res.result)
}).catch((err) => {
this.$emit('fail', err)
});
}else{
this.$emit('validateFail')
}
})
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,172 @@
<template>
<div>
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-model-item required prop="mobile">
<a-input v-model="model.mobile" size="large" type="text" placeholder="请输入手机号">
<a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-model-item>
<a-row :gutter="16">
<a-col class="gutter-row" :span="16">
<a-form-model-item required prop="captcha">
<a-input v-model="model.captcha" size="large" type="text" placeholder="请输入验证码">
<a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-model-item>
</a-col>
<a-col class="gutter-row" :span="8">
<a-button
class="getCaptcha"
tabindex="-1"
:disabled="state.smsSendBtn"
@click.stop.prevent="getCaptcha"
v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"></a-button>
</a-col>
</a-row>
</a-form-model>
</div>
</template>
<script>
import { postAction } from '@/api/manage'
import { mapActions } from 'vuex'
export default {
name: 'LoginPhone',
data(){
return {
model:{
mobile: '',
captcha: ''
},
//手机号登录用
state: {
time: 60,
smsSendBtn: false,
},
validatorRules:{
mobile: [
{ required: true, message: '请输入手机号码!' },
{ validator: this.validateMobile }
],
captcha: [{
required: true, message: '请输入验证码!'
}]
}
}
},
methods:{
...mapActions(['PhoneLogin']),
handleLogin(rememberMe){
this.validateFields([ 'mobile', 'captcha' ], (err) => {
if (!err) {
let loginParams = {
mobile: this.model.mobile,
captcha: this.model.captcha,
remember_me: rememberMe
}
console.log("登录参数", loginParams)
this.PhoneLogin(loginParams).then((res) => {
this.$emit('success', res.result)
}).catch((err) => {
this.$emit('fail', err)
});
}else{
this.$emit('validateFail')
}
})
},
// 校验手机号
validateMobile(rule,value,callback){
if (!value || new RegExp(/^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$/).test(value)){
callback();
}else{
callback("您的手机号码格式不正确!");
}
},
//获取验证码
getCaptcha (e) {
e.preventDefault();
let that = this;
that.validateFields([ 'mobile' ], (err) => {
if (!err) {
that.state.smsSendBtn = true;
let interval = window.setInterval(() => {
if (that.state.time-- <= 0) {
that.state.time = 60;
that.state.smsSendBtn = false;
window.clearInterval(interval);
}
}, 1000);
const hide = that.$message.loading('验证码发送中..', 0);
let smsParams = {};
smsParams.mobile=that.model.mobile;
smsParams.smsmode="0";
postAction("/sys/sms",smsParams)
.then(res => {
if(!res.success){
setTimeout(hide, 0);
that.cmsFailed(res.message);
}
console.log(res);
setTimeout(hide, 500);
})
.catch(err => {
setTimeout(hide, 1);
clearInterval(interval);
that.state.time = 60;
that.state.smsSendBtn = false;
that.requestFailed(err);
});
}
}
);
},
cmsFailed(err){
this.$notification[ 'error' ]({
message: '获取验证码失败',
description:err,
duration: 4,
});
},
/**
* 验证字段
* @param arr
* @param callback
*/
validateFields(arr, callback){
let promiseArray = []
for(let item of arr){
let p = new Promise((resolve, reject) => {
this.$refs['form'].validateField(item, (err)=>{
if(!err){
resolve();
}else{
reject(err);
}
})
});
promiseArray.push(p)
}
Promise.all(promiseArray).then(()=>{
callback()
}).catch(err=>{
callback(err)
})
},
}
}
</script>
<style scoped>
.getCaptcha{
display: block;
width: 100%;
height: 40px;
}
</style>

View File

@ -10,8 +10,8 @@
<a-button type="primary" @click="selectOk">确认</a-button>
</template>
<a-form>
<a-form-item v-if="isMultiTenant" :labelCol="{span:4}" :wrapperCol="{span:20}" style="margin-bottom:10px" :validate-status="validate_status1">
<a-form-model>
<a-form-model-item v-if="isMultiTenant" :labelCol="{span:4}" :wrapperCol="{span:20}" style="margin-bottom:10px" :validate-status="validate_status1">
<a-tooltip placement="topLeft" >
<template slot="title">
<span>您有多个租户请选择登录租户</span>
@ -25,10 +25,10 @@
{{ d.name }}
</a-select-option>
</a-select>
</a-form-item>
</a-form-model-item>
<a-form-item v-if="isMultiDepart" :labelCol="{span:4}" :wrapperCol="{span:20}" style="margin-bottom:10px" :validate-status="validate_status2">
<a-form-model-item v-if="isMultiDepart" :labelCol="{span:4}" :wrapperCol="{span:20}" style="margin-bottom:10px" :validate-status="validate_status2">
<a-tooltip placement="topLeft" >
<template slot="title">
<span>您有多个部门,请选择登录部门</span>
@ -42,9 +42,9 @@
{{ d.departName }}
</a-select-option>
</a-select>
</a-form-item>
</a-form-model-item>
</a-form>
</a-form-model>
</a-modal>
</template>

View File

@ -2,7 +2,7 @@
<a-card :bordered="false" style="width: 130%;text-align: center;margin-left:-10%">
<a-steps class="steps" :current="currentTab">
<a-step title="手机验证"/>
<a-step title="密码"/>
<a-step title="更改密码"/>
<a-step title="完成"/>
</a-steps>
<div class="content">
@ -14,7 +14,6 @@
</template>
<script>
import Step1 from './Step1'
import Step2 from './Step2'
import Step3 from './Step3'
import Step4 from './Step4'
@ -22,7 +21,6 @@
export default {
name: "Alteration",
components: {
Step1,
Step2,
Step3,
Step4
@ -63,4 +61,12 @@
max-width: 750px;
margin: 16px auto;
}
</style>
/deep/ .password-retrieval-form{
max-width: 500px;
margin: 40px auto 0;
.ant-form-explain{
text-align: left;
}
}
</style>

View File

@ -1,31 +1,20 @@
<template>
<div>
<a-form :form="form" style="max-width: 500px; margin: 40px auto 0;" @keyup.enter.native="nextStep">
<a-form-item
label="手机"
:labelCol="{span: 5}"
:wrapperCol="{span: 19}"
>
<a-input
type="text"
autocomplete="false"
style="width:310px;margin-left:-10px"
v-decorator="['phone',{ rules: validatorRules.phone.rule}]"
placeholder="请输入手机号">
<a-icon slot="prefix" type="phone" :style="{ color: 'rgba(0,0,0,.25)'}"/>
</a-input>
</a-form-item>
<a-form-item
label="验证码"
:labelCol="{span: 5}"
:wrapperCol="{span: 19}"
v-if="show">
<a-row :gutter="16" style="margin-left: 2px">
<a-form-model ref="form" :model="model" :rules="validatorRules" class="password-retrieval-form" @keyup.enter.native="nextStep">
<a-form-model-item label="手机" required prop="phone" :labelCol="{span: 5}" :wrapperCol="{span: 19}">
<a-row :gutter="16">
<a-col class="gutter-row" :span="20">
<a-input v-model="model.phone" type="text" autocomplete="false" placeholder="请输入手机号">
<a-icon slot="prefix" type="phone" :style="{ color: 'rgba(0,0,0,.25)'}"/>
</a-input>
</a-col>
</a-row>
</a-form-model-item>
<a-form-model-item v-if="show" required prop="captcha" label="验证码" :labelCol="{span: 5}" :wrapperCol="{span: 19}">
<a-row :gutter="16">
<a-col class="gutter-row" :span="12">
<a-input
v-decorator="['captcha',validatorRules.captcha]"
type="text"
placeholder="手机短信验证码">
<a-input v-model="model.captcha" type="text" placeholder="手机短信验证码">
<a-icon slot="prefix" type="code" :style="{ color: 'rgba(0,0,0,.25)'}"/>
</a-input>
</a-col>
<a-col class="gutter-row" :span="8">
@ -37,12 +26,12 @@
v-text="!state.smsSendBtn && '获取验证码' || (state.time+' s')"></a-button>
</a-col>
</a-row>
</a-form-item>
<a-form-item :wrapperCol="{span: 19, offset: 5}">
</a-form-model-item>
<a-form-model-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" style="margin-left: 20px">下一步</a-button>
</a-form-item>
</a-form>
</a-form-model-item>
</a-form-model>
</div>
</template>
@ -54,7 +43,7 @@
props: ['userList'],
data() {
return {
form: this.$form.createForm(this),
model: {},
loading: false,
// accountName: this.userList.username,
dropList: "0",
@ -69,8 +58,13 @@
mobile: "",
},
validatorRules: {
captcha: {rule: [{required: true, message: '请输入短信验证码!'}, {validator: this.validateCaptcha}]},
phone: {rule: [{required: true, message: '请输入手机号码!'}, {validator: this.validatePhone}]},
phone: [
{ required: true, message: '请输入手机号码!' },
{ validator: this.validatePhone }
],
captcha: [
{ required: true, message: '请输入短信验证码!' }
]
},
}
},
@ -80,67 +74,59 @@
nextStep() {
let that = this
that.loading = true
this.form.validateFields((err, values) => {
console.log(values);
if (!err) {
if (that.dropList == "0") {
if (values.captcha == undefined) {
this.cmsFailed("请输入短信验证码!");
} else {
var params = {}
params.phone = values.phone;
params.smscode = values.captcha;
postAction("/sys/user/phoneVerification", params).then((res) => {
if (res.success) {
console.log(res);
var userList = {
username: res.result.username,
phone: values.phone,
smscode: res.result.smscode
};
setTimeout(function () {
that.$emit('nextStep', userList)
}, 0)
} else {
this.cmsFailed(res.message);
}
})
}
this.$refs['form'].validate((success) => {
if(success==true){
let params = {
phone: this.model.phone,
smscode: this.model.captcha
}
postAction("/sys/user/phoneVerification", params).then((res) => {
if (res.success) {
console.log(res);
let userList = {
username: res.result.username,
phone: params.phone,
smscode: res.result.smscode
};
setTimeout(function () {
that.$emit('nextStep', userList)
}, 0)
} else {
this.cmsFailed(res.message);
}
});
}
})
},
getCaptcha(e) {
e.preventDefault();
let that = this;
let phone=that.form.getFieldValue("phone")
if(!phone){
this.cmsFailed("手机号不能为空!");
return;
}
this.state.smsSendBtn = true;
let interval = window.setInterval(() => {
if (that.state.time-- <= 0) {
that.state.time = 60;
that.state.smsSendBtn = false;
window.clearInterval(interval);
const that = this
that.$refs['form'].validateField('phone', err=>{
if(!err){
that.state.smsSendBtn = true;
let interval = window.setInterval(() => {
if (that.state.time-- <= 0) {
that.state.time = 60;
that.state.smsSendBtn = false;
window.clearInterval(interval);
}
}, 1000);
const hide = that.$message.loading('验证码发送中..', 0);
let smsParams = {
mobile: that.model.phone,
smsmode: "2"
};
postAction("/sys/sms", smsParams).then(res => {
if (!res.success) {
setTimeout(hide, 1);
that.cmsFailed(res.message);
}
setTimeout(hide, 500);
})
}else{
that.cmsFailed(err);
}
}, 1000);
const hide = this.$message.loading('验证码发送中..', 0);
let smsParams = {
mobile: phone,
smsmode: "2"
};
postAction("/sys/sms", smsParams).then(res => {
if (!res.success) {
setTimeout(hide, 1);
this.cmsFailed(res.message);
}
setTimeout(hide, 500);
})
},
cmsFailed(err) {
@ -193,4 +179,4 @@
width: 100%;
height: 40px;
}
</style>
</style>

View File

@ -1,48 +1,29 @@
<template>
<div>
<a-form :form="form" style="max-width: 500px; margin: 40px auto 0;">
<a-form-item
label="账号名"
:labelCol="{span: 5}"
:wrapperCol="{span: 19}"
>
<a-input
type="text"
autocomplete="false" :value="accountName" disabled>
</a-input>
</a-form-item>
<a-form-item
label="新密码"
:labelCol="{span: 5}"
:wrapperCol="{span: 19}"
class="stepFormText">
<a-input
v-decorator="['password',validatorRules.password]"
type="password"
autocomplete="false">
</a-input>
</a-form-item>
<a-form-item
label="确认密码"
:labelCol="{span: 5}"
:wrapperCol="{span: 19}"
class="stepFormText">
<a-input
v-decorator="['confirmPassword',validatorRules.confirmPassword]"
type="password"
autocomplete="false">
</a-input>
</a-form-item>
<a-form-item :wrapperCol="{span: 19, offset: 5}">
<a-form-model ref="form" :model="model" :rules="validatorRules" class="password-retrieval-form">
<a-form-model-item label="账号名" v-bind="layout">
<a-input type="text" :value="accountName" disabled/>
</a-form-model-item>
<a-form-model-item prop="password" label="新密码" v-bind="layout" class="stepFormText">
<a-input v-model="model.password" type="password" autocomplete="false"/>
</a-form-model-item>
<a-form-model-item prop="confirmPassword" label="确认密码" v-bind="layout" class="stepFormText">
<a-input v-model="model.confirmPassword" type="password" autocomplete="false"/>
</a-form-model-item>
<a-form-model-item :wrapperCol="{span: 19, offset: 5}">
<a-button style="margin-left: 8px" @click="prevStep">上一步</a-button>
<a-button :loading="loading" type="primary" @click="nextStep" style="margin-left:20px">提交</a-button>
</a-form-item>
</a-form>
</a-form-model-item>
</a-form-model>
</div>
</template>
<script>
import { putAction,getAction } from '@/api/manage'
import { getAction } from '@/api/manage'
export default {
name: "Step3",
// components: {
@ -51,59 +32,61 @@
props: ['userList'],
data () {
return {
model:{},
layout: {
labelCol: { span: 5 },
wrapperCol: { span: 19 },
},
loading: false,
form: this.$form.createForm(this),
accountName: this.userList.username,
validatorRules: {
username: {rules: [{required: true, message: '用户名不能为空!'}]},
password: {
rules: [{
required: true,
pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,}$/,
message: '密码由8位数字大小写字母和特殊符号组成!!'
}, {validator: this.handlePasswordLevel}]
},
confirmPassword: {rules: [{required: true, message: '密码不能为空!'}, {validator: this.handlePasswordCheck}]},
},
password: [{
required: true, pattern: /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,.\/]).{8,}$/, message: '密码由8位数字大小写字母和特殊符号组成!!'
}],
confirmPassword: [
{ required: true, message: '密码不能为空!'},
{ validator: this.handlePasswordCheck}
]
}
}
},
methods: {
nextStep () {
let that = this
that.loading = true
this.form.validateFields((err, values) => {
if ( !err ){
var params={}
params.username=this.userList.username;
params.password=values.password;
params.smscode=this.userList.smscode;
params.phone= this.userList.phone;
getAction("/sys/user/passwordChange", params).then((res) => {
if(res.success){
var userList = {
username: this.userList.username
that.$refs['form'].validate(success => {
if (success === true) {
let params = {
username: that.userList.username,
password: that.model.password,
smscode: that.userList.smscode,
phone: that.userList.phone,
}
console.log(userList);
setTimeout(function () {
that.$emit('nextStep', userList)
}, 1500)
}else{
this.passwordFailed(res.message);
getAction("/sys/user/passwordChange", params).then((res) => {
if (res.success) {
let userList = {
username: that.userList.username
}
console.log(userList);
setTimeout(function() {
that.$emit('nextStep', userList)
}, 1500)
} else {
that.passwordFailed(res.message);
that.loading = false
}
})
} else {
that.loading = false
}
})
} else{
that.loading = false
}
})
},
prevStep () {
this.$emit('prevStep', this.userList)
},
handlePasswordCheck (rule, value, callback) {
let password = this.form.getFieldValue('password')
let password = this.model['password']
if (value && password && value.trim() !== password.trim()) {
callback(new Error('两次密码不一致'))
}

View File

@ -1,12 +1,10 @@
<template>
<div class="main user-layout-register">
<h3><span>注册</span></h3>
<a-form ref="formRegister" :autoFormCreate="(form)=>{this.form = form}" id="formRegister">
<a-form-item
fieldDecoratorId="username"
:fieldDecoratorOptions="{rules: [{ required: false}, { validator: this.checkUsername }]}">
<a-input size="large" type="text" autocomplete="false" placeholder="请输入用户名"></a-input>
</a-form-item>
<a-form-model ref="form" :model="model" :rules="validatorRules">
<a-form-model-item prop="username">
<a-input v-model="model.username" size="large" type="text" autocomplete="false" placeholder="请输入用户名"/>
</a-form-model-item>
<a-popover placement="rightTop" trigger="click" :visible="state.passwordLevelChecked">
<template slot="content">
@ -18,33 +16,30 @@
</div>
</div>
</template>
<a-form-item
fieldDecoratorId="password"
:fieldDecoratorOptions="{rules: [{ required: false}, { validator: this.handlePasswordLevel }]}">
<a-input size="large" type="password" @click="handlePasswordInputClick" autocomplete="false" placeholder="至少8位密码区分大小写"></a-input>
</a-form-item>
<a-form-model-item prop="password">
<a-input
v-model="model.password"
size="large"
type="password"
@click="handlePasswordInputClick"
autocomplete="false"
placeholder="至少8位密码区分大小写">
</a-input>
</a-form-model-item>
</a-popover>
<a-form-item
fieldDecoratorId="password2"
:fieldDecoratorOptions="{rules: [{ required: false}, { validator: this.handlePasswordCheck }]}">
<a-form-model-item prop="password2">
<a-input v-model="model.password2" size="large" type="password" autocomplete="false" placeholder="确认密码"></a-input>
</a-form-model-item>
<a-input size="large" type="password" autocomplete="false" placeholder="确认密码"></a-input>
</a-form-item>
<!-- <a-form-item-->
<!-- fieldDecoratorId="email">-->
<!-- <a-input size="large" type="text" placeholder="邮箱"></a-input>-->
<!-- </a-form-item>-->
<a-form-item
fieldDecoratorId="mobile"
:fieldDecoratorOptions="{rules: [{ required: false}, { validator: this.handlePhoneCheck }]}">
<a-input size="large" placeholder="11 位手机号">
<a-form-model-item prop="mobile">
<a-input v-model="model.mobile" size="large" placeholder="11 位手机号">
<a-select slot="addonBefore" size="large" defaultValue="+86">
<a-select-option value="+86">+86</a-select-option>
<a-select-option value="+87">+87</a-select-option>
</a-select>
</a-input>
</a-form-item>
</a-form-model-item>
<!--<a-input-group size="large" compact>
<a-select style="width: 20%" size="large" defaultValue="+86">
<a-select-option value="+86">+86</a-select-option>
@ -55,13 +50,11 @@
<a-row :gutter="16">
<a-col class="gutter-row" :span="16">
<a-form-item
fieldDecoratorId="captcha"
:fieldDecoratorOptions="{rules: [{ required: false}, { validator: this.handleCaptchaCheck }]}">
<a-input size="large" type="text" placeholder="验证码">
<a-form-model-item prop="captcha">
<a-input v-model="model.captcha" size="large" type="text" placeholder="验证码">
<a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col class="gutter-row" :span="8">
<a-button
@ -73,7 +66,7 @@
</a-col>
</a-row>
<a-form-item>
<a-form-model-item>
<a-button
size="large"
type="primary"
@ -84,9 +77,9 @@
:disabled="registerBtn">注册
</a-button>
<router-link class="login" :to="{ name: 'login' }">使用已有账户登录</router-link>
</a-form-item>
</a-form-model-item>
</a-form>
</a-form-model>
</div>
</template>
@ -120,8 +113,29 @@
mixins: [mixinDevice],
data() {
return {
form: null,
model: {},
validatorRules: {
username: [
{ required: false },
{ validator: this.checkUsername }
],
password: [
{ required: false},
{ validator: this.handlePasswordLevel }
],
password2: [
{ required: false },
{ validator: this.handlePasswordCheck }
],
mobile: [
{ required: false },
{ validator: this.handlePhoneCheck }
],
captcha: [
{ required: false },
{ validator: this.handleCaptchaCheck }
]
},
state: {
time: 60,
smsSendBtn: false,
@ -162,7 +176,7 @@
}
},
handleEmailCheck(rule, value, callback) {
var params = {
let params = {
email: value,
};
checkOnlyUser(params).then((res) => {
@ -174,7 +188,6 @@
})
},
handlePasswordLevel(rule, value, callback) {
let level = 0
let reg = /^(?=.*[a-zA-Z])(?=.*\d)(?=.*[~!@#$%^&*()_+`\-={}:";'<>?,./]).{8,}$/;
if (!reg.test(value)) {
@ -208,7 +221,7 @@
},
handlePasswordCheck(rule, value, callback) {
let password = this.form.getFieldValue('password')
let password = this.model['password']
//console.log('value', value)
if (value === undefined) {
callback(new Error('请输入密码'))
@ -252,9 +265,10 @@
},
handleSubmit() {
this.form.validateFields((err, values) => {
if (!err) {
var register = {
this.$refs['form'].validate((success) => {
if (success==true) {
let values = this.model
let register = {
username: values.username,
password: values.password,
phone: values.mobile,
@ -274,7 +288,7 @@
getCaptcha(e) {
e.preventDefault()
let that = this
this.form.validateFields(['mobile'], {force: true}, (err, values) => {
this.$refs['form'].validateField(['mobile'], (err) => {
if (!err) {
this.state.smsSendBtn = true;
let interval = window.setInterval(() => {
@ -284,9 +298,9 @@
window.clearInterval(interval);
}
}, 1000);
const hide = this.$message.loading('验证码发送中..', 0);
const hide = this.$message.loading('验证码发送中..', 3);
const params = {
mobile: values.mobile,
mobile: this.model.mobile,
smsmode: "1"
};
postAction("/sys/sms", params).then((res) => {

View File

@ -21,25 +21,25 @@
},
data () {
return {
form: {},
model: {},
}
},
computed: {
email () {
let v = this.form ? this.form.username || this.form.mobile : ' XXX '
let v = this.model ? this.model.username || this.model.mobile : ' XXX '
let title = `你的账户${v} 注册成功`
this.username = v;
return title
}
},
created () {
this.form = this.$route.params
this.model = this.$route.params
},
methods: {
goHomeHandle () {
let params={};
params.username=this.form.username;
params.password=this.form.password;
params.username=this.model.username;
params.password=this.model.password;
console.log(params);
this.$router.push({name:'login',params})
},

View File

@ -148,7 +148,7 @@ export const JeecgThirdLoginMixin = {
const hide = this.$message.loading('验证码发送中..', 0)
let smsParams = {}
smsParams.mobile = this.thirdPhone
smsParams.smsmode = '1'
smsParams.smsmode = '0'
postAction('/sys/sms', smsParams).then(res => {
if (!res.success) {
setTimeout(hide, 0)

View File

@ -37,10 +37,10 @@
</a-button>
</template>
<div class="ant-modal-confirm-body-wrapper">
<a-form-item>
<a-form-model-item>
<span>绑定手机号</span>
</a-form-item>
<a-form-item>
</a-form-model-item>
<a-form-model-item>
<a-input
size="large"
type="text"
@ -48,11 +48,11 @@
v-model="thirdPhone">
<a-icon slot="prefix" type="mobile" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-form-model-item>
<a-row :gutter="16">
<a-col class="gutter-row" :span="16">
<a-form-item>
<a-form-model-item>
<a-input
size="large"
type="text"
@ -60,7 +60,7 @@
v-model="thirdCaptcha">
<a-icon slot="prefix" type="mail" :style="{ color: 'rgba(0,0,0,.25)' }"/>
</a-input>
</a-form-item>
</a-form-model-item>
</a-col>
<a-col class="gutter-row" :span="8">
<a-button

View File

@ -888,10 +888,10 @@
cssnano-preset-default "^4.0.0"
postcss "^7.0.0"
"@jeecg/antd-online-mini@2.4.3-RC":
version "2.4.3-RC"
resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-2.4.3-RC.tgz#573f07254b63ffa7549df9dfc23cc8647734c275"
integrity sha512-U/0W++7Og6SmqKqnDc0VFLDeEYmXCH4bMN+tUtAVLqwTxyln12cBSD7zD5zja1dWOxyrvt6pYC5cnGpH7CUVVQ==
"@jeecg/antd-online-mini@2.4.5-RC":
version "2.4.5-RC"
resolved "https://registry.npmjs.org/@jeecg/antd-online-mini/-/antd-online-mini-2.4.5-RC.tgz#88740572f352a8baae694a83305f289b54e603c4"
integrity sha512-2Wxj7wr+j3YGU5W1NtOWGM/qv1bd28UVBAxMCIZ/vY12w8+qnaNK9IKri6oJmqYiB40UA9EUqsxE9iMlRfv3Ww==
"@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.4"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.4.tgz#854e90985dc0fa9ccc6d681c104560c00eaaf855"
integrity sha512-f9/4I34Yq562X70aspSFzw4BCuKRICzDvIocXpsYCC+B9+kjB8db246fpSMtV/1anqxcmGHf7Vh0XJ+ovHJKMw==
version "1.7.5"
resolved "https://registry.npmjs.org/ant-design-vue/-/ant-design-vue-1.7.5.tgz#e7ed04cb358adc56be02c7453d2db026d2c405e3"
integrity sha512-QVrirCz6eetzui+Dv/ujMqUwIr8e/2H+pKr4VC3mpc+cHkw6L9cBRJK7DhfO7GTyK4EWKkEH3lfLWlD/XjUoQQ==
dependencies:
"@ant-design/icons" "^2.1.1"
"@ant-design/icons-vue" "^2.0.0"
@ -2895,6 +2895,14 @@ cache-loader@^2.0.1:
normalize-path "^3.0.0"
schema-utils "^1.0.0"
call-bind@^1.0.0:
version "1.0.2"
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"
call-me-maybe@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
@ -3660,6 +3668,14 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
cron-parser@^2.10.0:
version "2.18.0"
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"
cross-env@^5.0.5:
version "5.2.1"
resolved "https://registry.npmjs.org/cross-env/-/cross-env-5.2.1.tgz#b2c76c1ca7add66dc874d11798466094f551b34d"
@ -5678,6 +5694,15 @@ get-caller-file@^2.0.1:
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.2:
version "1.1.1"
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"
has-symbols "^1.0.1"
get-stdin@^4.0.1:
version "4.0.1"
resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@ -6591,6 +6616,14 @@ is-mobile@^2.2.1:
resolved "https://registry.npmjs.org/is-mobile/-/is-mobile-2.2.2.tgz#f6c9c5d50ee01254ce05e739bdd835f1ed4e9954"
integrity sha512-wW/SXnYJkTjs++tVK5b6kVITZpAZPtUrt9SF80vvxGiF/Oywal+COk1jlRkiVq15RFNEQKQY31TkV24/1T5cVg==
is-nan@^1.3.0:
version "1.3.2"
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"
is-negative-zero@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
@ -7637,6 +7670,18 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.0, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"
moment-timezone@^0.5.31:
version "0.5.33"
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"
"moment@>= 2.9.0":
version "2.29.1"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
moment@^2.21.0:
version "2.27.0"
resolved "https://registry.npmjs.org/moment/-/moment-2.27.0.tgz#8bff4e3e26a236220dfe3e36de756b6ebaa0105d"

View File

@ -1,7 +1,7 @@
Jeecg-Boot 低代码开发平台
===============
当前最新版本: 2.4.3发布日期20210322
当前最新版本: 2.4.5发布日期20210607
## 后端技术架构

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,81 +0,0 @@
-- redis监控菜单sql
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 ('1352200630711652354', 'f0675b52d89100ee88472b6800754a08', 'redis监控', '{{ window._CONFIG[\'domianURL\'] }}/jmreport/view/1352160857479581696', 'layouts/IframePageView', NULL, NULL, '1', NULL, '1', '1.00', '0', '', '1', '1', '0', '0', NULL, '1', '0', '0', 'admin', '2021-01-21 18:25:28', 'admin', '2021-01-22 16:49:43', '0');
-- 新增开关字典
INSERT INTO `sys_dict` (`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `type`) VALUES ('1356445645198135298', '开关', 'is_open', '', '0', 'admin', '2021-02-02 11:33:38', 'admin', '2021-02-02 15:28:12', '0');
INSERT INTO `sys_dict_item` (`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('1356445705549975553', '1356445645198135298', '是', 'Y', '', '1', '1', 'admin', '2021-02-02 11:33:52', 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 ('1356445754212290561', '1356445645198135298', '否', 'N', '', '1', '1', 'admin', '2021-02-02 11:34:04', NULL, NULL);
-- author:liusq----date:20210202----for: 新增开关字典
-- 是否字典
INSERT INTO `sys_dict`(`id`, `dict_name`, `dict_code`, `description`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`, `type`) VALUES ('a7adbcd86c37f7dbc9b66945c82ef9e6', '1是0否', 'yn', '', 0, 'admin', '2019-05-22 19:29:29', NULL, NULL, 0);
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('51222413e5906cdaf160bb5c86fb827c', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '是', '1', '', 1, 1, 'admin', '2019-05-22 19:29:45', NULL, NULL);
INSERT INTO `sys_dict_item`(`id`, `dict_id`, `item_text`, `item_value`, `description`, `sort_order`, `status`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES ('c5700a71ad08994d18ad1dacc37a71a9', 'a7adbcd86c37f7dbc9b66945c82ef9e6', '否', '0', '', 1, 1, 'admin', '2019-05-22 19:29:55', NULL, NULL);
-- author:scott----date:20210202----for: 是否字典
-- 精简积木报表的示例表由22个减至4个----
drop table rep_demo_dadong;
drop table rep_demo_daibu;
drop table rep_demo_deliveryorder;
drop table rep_demo_huizong;
drop table rep_demo_income;
drop table rep_demo_jiehsao;
drop table rep_demo_kaoqin;
drop table rep_demo_salesrate;
drop table rep_demo_xiaoshou;
drop table xianlu1_wxtl;
drop table xianlu_wxtl;
drop table yanshi_duozu;
drop table yanshi_jdcx;
drop table yanshi_qipaosandian;
drop table yanshi_sandian;
drop table yanshi_tima;
drop table yanshi_wxtl;
drop table yanshi_yipan;
alter table yanshi_dxtj rename to rep_demo_dxtj;
delete from jimu_report_data_source where report_id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db where jimu_report_id in('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db_param where jimu_report_head_id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
delete from jimu_report_db_field where jimu_report_db_id not in (select id from jimu_report_db);
delete from jimu_report where id in ('1331429368098066432','1333962561053396992','1334028738995818496','1334074491629867008','1337271712059887616','1339478701846433792','1339859143477039104','8e69f5396643b199c92d6f401be4d833');
-- 添加一对多JVxeTable案例
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 ('1365187528377102337', '2a470fc0c3954d9dbb61de6d80846549', '一对多JVxeTable', '/jeecg/JeecgOrderMainListForJVxeTable', 'jeecg/JeecgOrderMainListForJVxeTable', NULL, NULL, 1, NULL, '1', 2.00, 0, NULL, 1, 1, 0, 0, NULL, '1', 0, 0, 'admin', '2021-02-26 14:30:45', 'admin', '2021-02-26 14:32:05', 0);
-- 积木权限表
CREATE TABLE `jimu_report_share` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键',
`report_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '在线excel设计器id',
`preview_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '预览地址',
`preview_lock` varchar(4) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码锁',
`last_update_time` datetime(0) NULL DEFAULT NULL COMMENT '最后更新时间',
`term_of_validity` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '有效期(0:永久有效1:1天2:7天)',
`status` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '是否过期(0未过期1已过期)',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '积木报表预览权限表';
-- 积木报表链接表
CREATE TABLE `jimu_report_link` (
`id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '主键id',
`report_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '积木设计器id',
`parameter` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '参数',
`eject_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '弹出方式0 当前页面 1 新窗口',
`link_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '链接名称',
`api_method` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '请求方法0-get,1-post',
`link_type` varchar(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '链接方式(0 网络报表 1 网络连接 2 图表联动)',
`api_url` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '外网api',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '超链接配置表';
update jimu_report_link set link_type = '0';

View File

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

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base</artifactId>
<version>2.4.3</version>
<version>2.4.5</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -114,7 +114,7 @@
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>hibernate-re</artifactId>
<version>2.4.3-RC</version>
<version>2.4.5-RC</version>
</dependency>
<!--mysql-->

View File

@ -0,0 +1,47 @@
package org.jeecg.common.api.desform;
import org.jeecg.common.system.vo.DictModel;
import java.util.List;
import java.util.Map;
/**
* 表单设计器【System】翻译API接口
*
* @author sunjianlei
*/
public interface ISysTranslateAPI {
/**
* 查询分类字典翻译
*/
List<String> categoryLoadDictItem(String ids);
/**
* 根据字典code加载字典text
*
* @param dictCode 顺序tableName,text,code
* @param keys 要查询的key
* @return
*/
List<String> dictLoadDictItem(String dictCode, String keys);
/**
* 获取字典数据
*
* @param dictCode 顺序tableName,text,code
* @param dictCode 要查询的key
* @return
*/
List<DictModel> dictGetDictItems(String dictCode);
/**
* 【JSearchSelectTag下拉搜索组件专用接口】
* 大数据量的字典表 走异步加载 即前端输入内容过滤数据
*
* @param dictCode 字典code格式table,text,code
* @return
*/
List<DictModel> dictLoadDict(String dictCode, String keyword, Integer pageSize);
}

View File

@ -23,6 +23,11 @@ public class MessageDTO implements Serializable {
*/
protected String toUser;
/**
* 发送给所有人
*/
protected boolean toAll;
/**
* 消息主题
*/

View File

@ -1,12 +1,12 @@
package org.jeecg.common.api.vo;
import java.io.Serializable;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.jeecg.common.constant.CommonConstant;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
/**
* 接口返回数据格式
@ -112,7 +112,16 @@ public class Result<T> implements Serializable {
r.setResult(data);
return r;
}
public static<T> Result<T> error(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(false);
r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}

View File

@ -14,16 +14,15 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.ModuleType;
import org.jeecg.modules.base.service.BaseCommonService;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.IPUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.modules.base.service.BaseCommonService;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

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