mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
Compare commits
129 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 812cbce06b | |||
| 3b92086047 | |||
| 0fa24b8518 | |||
| 7e7ea37857 | |||
| e550843fd1 | |||
| 3cb20a6a43 | |||
| 0acea1abff | |||
| 9760185bf6 | |||
| 75be8dd5b1 | |||
| a7893c4941 | |||
| ef97f700ab | |||
| 6fb01abbc5 | |||
| fe1b58ade2 | |||
| 1998867ca6 | |||
| a351204865 | |||
| 4da1948cb0 | |||
| 55ebea88af | |||
| 4b830b37c9 | |||
| 537cc05601 | |||
| 8f9f27c550 | |||
| 57f72dd4d0 | |||
| e2e19fa456 | |||
| 98a5148e52 | |||
| c2ae049ad7 | |||
| b44acde9b5 | |||
| db467f22a7 | |||
| 53c91fc349 | |||
| 92028a7e44 | |||
| 8ce40fa3d4 | |||
| 05c7f76484 | |||
| 71a1e9a63b | |||
| 0606aa560a | |||
| 1bbca48ba8 | |||
| d0c15f2302 | |||
| c9ff5d51b4 | |||
| cce5d785e4 | |||
| 234022d905 | |||
| ab529aaf6c | |||
| 07c6d1a23d | |||
| 8f780e180e | |||
| 87f17b9fc5 | |||
| 5a93d001b4 | |||
| d822552e0c | |||
| 832fa30cc9 | |||
| 3af1b390f1 | |||
| a3695151dc | |||
| c269d7637f | |||
| 664413e5d7 | |||
| 986f909628 | |||
| 221940cc5f | |||
| 2a392fb738 | |||
| 2b47cd0c34 | |||
| 1900f3fe77 | |||
| 37fe6fea69 | |||
| 3fbb5ee4ad | |||
| b1958fd295 | |||
| 8b3d83ae0b | |||
| 081c2615be | |||
| f97c675771 | |||
| 1beddbf8e8 | |||
| 8921e84303 | |||
| 43329545d8 | |||
| 50333488a5 | |||
| 624444e3b9 | |||
| 1cd0f2627d | |||
| 18007b0524 | |||
| d92861ad77 | |||
| 8a6181b108 | |||
| 6840772959 | |||
| 1bc7ee3345 | |||
| 5f25f726c2 | |||
| 9b14d2d6a5 | |||
| 130f2bc4be | |||
| 832bc376be | |||
| 432385fc14 | |||
| 4d5ac2b518 | |||
| b83c26b8fd | |||
| b59fc67696 | |||
| af3afc7263 | |||
| 63c3dd26f5 | |||
| 892196fe9a | |||
| bbda918cde | |||
| 003ec82f48 | |||
| 4992faf66c | |||
| 34c612d9e4 | |||
| 844f1e228c | |||
| cf5fde80d4 | |||
| 49ea36c50f | |||
| f6e2b67c61 | |||
| 69a4a7df6d | |||
| 649f99664e | |||
| ef762ff21f | |||
| 02abd5c803 | |||
| 5e7342b27d | |||
| ca95d7090c | |||
| cdec71999a | |||
| 7990eff7a4 | |||
| 280f8c26ac | |||
| 4e05eaa4c5 | |||
| c6645ed800 | |||
| a674340c5e | |||
| cbd8890cb3 | |||
| b25fb55d10 | |||
| 88646bea1b | |||
| 9915b84808 | |||
| 35c1214de3 | |||
| 434b1a7e63 | |||
| 5c7d84117c | |||
| 4b9890205e | |||
| d5898139b5 | |||
| bf438069e2 | |||
| c5965b10d8 | |||
| da5ace3397 | |||
| 4674097078 | |||
| fea1607f1b | |||
| d2336bc5e1 | |||
| cf0d29557a | |||
| bb4c3c86b0 | |||
| 84a6e28677 | |||
| 5b70c1d8b8 | |||
| f942abe09b | |||
| ad4eb300f0 | |||
| 064a5c4a0e | |||
| 6d688a157b | |||
| d612af5e3b | |||
| e1c0b0fa38 | |||
| ac0979c824 | |||
| 78ba7ccba8 | |||
| b8ff5ebd43 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -2,3 +2,4 @@
|
||||
*.css linguist-language=Java
|
||||
*.html linguist-language=Java
|
||||
*.vue linguist-language=Java
|
||||
*.sql linguist-language=Java
|
||||
|
||||
7
.github/ISSUE_TEMPLATE.md
vendored
7
.github/ISSUE_TEMPLATE.md
vendored
@ -9,4 +9,9 @@
|
||||
|
||||
|
||||
|
||||
友情提示: 未按格式要求发帖,会直接删掉。
|
||||
#### 友情提示(为了提高issue处理效率):
|
||||
- 未按格式要求发帖,会被直接删掉;
|
||||
- 请自己初判问题描述是否清楚,是否方便我们调查处理;
|
||||
- 针对问题请说明是Online在线功能(需说明用的主题模板),还是生成的代码功能;
|
||||
- 描述过于简单或模糊,导致无法处理的,会被直接删掉;
|
||||
|
||||
|
||||
63
README.md
63
README.md
@ -1,18 +1,19 @@
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
JEECG BOOT 低代码开发平台(前后端分离版本)
|
||||
===============
|
||||
|
||||
当前最新版本: 2.4.2(发布日期:2021-01-26)
|
||||
当前最新版本: 3.0(发布日期:2021-11-01)
|
||||
|
||||
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[](http://www.jeecg.com)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://jeecg.blog.csdn.net)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
|
||||
@ -23,7 +24,7 @@ JEECG BOOT 低代码开发平台(前后端分离版本)
|
||||
|
||||
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
|
||||
|
||||
JeecgBoot 是一款基于代码生成器的`低代码`开发平台!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE), 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
|
||||
JeecgBoot 是一款基于代码生成器的`低代码平台`!前后端分离架构 SpringBoot2.x,SpringCloud,Ant Design&Vue,Mybatis-plus,Shiro,JWT,支持微服务。强大的代码生成器让前后端代码一键生成,实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE), 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
|
||||
|
||||
JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`:Online表单开发、Online报表、报表配置能力、在线图表设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力(可插拔)等等!
|
||||
|
||||
@ -44,14 +45,16 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
|
||||
- 技术官网: [http://www.jeecg.com](http://www.jeecg.com)
|
||||
|
||||
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
|
||||
|
||||
- 微服务启动: [单体升级为微服务启动文档2.4+](http://doc.jeecg.com/2043906)
|
||||
|
||||
- 在线演示 : [http://boot.jeecg.com](http://boot.jeecg.com)
|
||||
|
||||
- 在线演示(VUE3beta版):[http://boot3.jeecg.com](http://boot3.jeecg.com)
|
||||
|
||||
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
|
||||
|
||||
- 视频教程 :[JeecgBoot入门视频](http://www.jeecg.com/doc/video)
|
||||
|
||||
- 微服务启动: [单体升级为微服务启动文档2.4+](http://doc.jeecg.com/2043906)
|
||||
|
||||
- 常见问题: [入门常见问题Q&A](http://jeecg.com/doc/qa)
|
||||
|
||||
- 更新日志: [版本日志](http://www.jeecg.com/doc/log)
|
||||
@ -61,19 +64,19 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
交流互动
|
||||
-----------------------------------
|
||||
|
||||
- QQ交流群 : ③816531124、②769925425(满)、①284271917(满)
|
||||
- QQ交流群 : ⑤860162132、④774126647(满)、③816531124(满)、②769925425(满)、①284271917(满)
|
||||
|
||||
- 反馈问题: [反馈问题,请按格式发Issues](https://github.com/zhangdaiscott/jeecg-boot/issues/new)
|
||||
|
||||
- 参与开源: [欢迎加入JEECG开源团队,共同进步!!](http://www.jeecg.com/doc/join)
|
||||
|
||||
- Online一分钟: [1分钟快速学习](https://jeecg.blog.csdn.net/article/details/106078912)
|
||||
- Online一分钟: [1分钟快速学习](https://my.oschina.net/jeecg/blog/3083313)
|
||||
|
||||
|
||||
为什么选择JEECG-BOOT?
|
||||
-----------------------------------
|
||||
* 1.采用最新主流前后分离框架(Springboot+Mybatis+antd),容易上手; 代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发;
|
||||
* 2.支持微服务SpringCloud Alibaba(Nacos、Gateway、Sentinel、Skywarking),提供切换机制支持单体和微服务自由切换
|
||||
* 2.支持微服务SpringCloud Alibaba(Nacos、Gateway、Sentinel、Skywalking),提供切换机制支持单体和微服务自由切换
|
||||
* 3.开发效率高,采用代码生成器,单表、树列表、一对多、一对一等数据模型,增删改查功能一键生成,菜单配置直接使用;
|
||||
* 4.代码生成器提供强大模板机制,支持自定义模板,目前提供四套风格模板(单表两套、树模型一套、一对多三套)
|
||||
* 5.代码生成器非常智能,在线业务建模、在线配置、所见即所得支持23种类控件,一键生成前后端代码,大幅度提升开发效率,不再为重复工作发愁。
|
||||
@ -90,7 +93,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.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;
|
||||
@ -129,21 +132,37 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
|
||||
- 依赖管理:Maven
|
||||
|
||||
- 数据库:MySQL5.7+ & Oracle 11g & Sqlserver2017
|
||||
|
||||
- 缓存:Redis
|
||||
|
||||
- 数据库脚本:MySQL5.7+ & Oracle 11g & Sqlserver2017(默认只提供这三个库脚本,其他库需要自己转)
|
||||
|
||||
| 数据库 | 支持 |
|
||||
| --- | --- |
|
||||
| MySQL | √ |
|
||||
| Oracle11g | √ |
|
||||
| Sqlserver2017 | √ |
|
||||
| PostgreSQL | √ |
|
||||
| DB2、Informix | √ |
|
||||
| MariaDB | √ |
|
||||
| SQLite、Hsqldb、Derby、H2 | √ |
|
||||
| 达梦、人大金仓、神通 | √ |
|
||||
| 华为高斯、虚谷、瀚高数据库 | √ |
|
||||
| 阿里云PolarDB、PPAS、HerdDB | √ |
|
||||
| Hive、HBase、CouchBase | √ |
|
||||
|
||||
|
||||
#### 后端
|
||||
- 基础框架:Spring Boot 2.3.5.RELEASE
|
||||
|
||||
- 微服务框架: Spring Cloud Alibaba 2.2.3.RELEASE
|
||||
|
||||
- 持久层框架:Mybatis-plus 3.4.1
|
||||
- 持久层框架:Mybatis-plus 3.4.3.1、Minidao
|
||||
|
||||
- 报表工具: jimureport 1.3.78
|
||||
|
||||
- 安全框架:Apache Shiro 1.7.0,Jwt 3.11.0
|
||||
|
||||
- 微服务技术栈:Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywarking
|
||||
- 微服务技术栈:Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
|
||||
|
||||
- 数据库连接池:阿里巴巴Druid 1.1.22
|
||||
|
||||
@ -328,7 +347,7 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
|
||||
8、服务监控 SpringBootAdmin√
|
||||
|
||||
9、链路跟踪 Skywarking [参考文档](https://www.kancloud.cn/zhangdaiscott/jeecgcloud/1771670)
|
||||
9、链路跟踪 Skywalking [参考文档](https://www.kancloud.cn/zhangdaiscott/jeecgcloud/1771670)
|
||||
|
||||
10、消息中间件 RabbitMQ √
|
||||
|
||||
@ -336,7 +355,7 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
|
||||
12、分布式事务 Seata
|
||||
|
||||
13、分布式日志 elk + kafa
|
||||
13、分布式日志 elk + kafka
|
||||
|
||||
14、支持 docker-compose、k8s、jenkins
|
||||
|
||||
@ -382,11 +401,11 @@ Jeecg-Boot低代码开发平台,可以应用在任何J2EE项目的开发中,
|
||||
- 拉取项目代码
|
||||
```bash
|
||||
git clone https://github.com/zhangdaiscott/jeecg-boot.git
|
||||
cd jeecg-boot/ant-design-jeecg-vue
|
||||
cd jeecg-boot/ant-design-vue-jeecg
|
||||
```
|
||||
|
||||
1. 安装node.js
|
||||
2. 切换到ant-design-jeecg-vue文件夹下
|
||||
2. 切换到ant-design-vue-jeecg文件夹下
|
||||
```
|
||||
# 安装yarn
|
||||
npm install -g yarn
|
||||
@ -487,9 +506,9 @@ yarn run lint
|
||||
|
||||
- [Vue](https://cn.vuejs.org/v2/guide)
|
||||
|
||||
- [路由/菜单说明](https://gitee.com/jeecg/jeecg-boot/tree/v1.1/ant-design-jeecg-vue/src/router/README.md)
|
||||
- [路由/菜单说明](https://gitee.com/jeecg/jeecg-boot/tree/v1.1/ant-design-vue-jeecg/src/router/README.md)
|
||||
|
||||
- [ANTD 默认配置项](https://gitee.com/jeecg/jeecg-boot/blob/v1.1/ant-design-jeecg-vue/src/defaultSettings.js)
|
||||
- [ANTD 默认配置项](https://gitee.com/jeecg/jeecg-boot/blob/v1.1/ant-design-vue-jeecg/src/defaultSettings.js)
|
||||
|
||||
- 其他待补充...
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_PLATFORM_NAME=JeecgBoot 企业级快速开发平台
|
||||
VUE_APP_PLATFORM_NAME=JeecgBoot 企业级低代码平台
|
||||
VUE_APP_SSO=false
|
||||
@ -1,4 +1,4 @@
|
||||
NODE_ENV=production
|
||||
VUE_APP_API_BASE_URL=https://bootapi.jeecg.com
|
||||
VUE_APP_API_BASE_URL=http://localhost:8080/jeecg-boot
|
||||
VUE_APP_CAS_BASE_URL=http://localhost:8888/cas
|
||||
VUE_APP_ONLINE_BASE_URL=http://fileview.jeecg.com/onlinePreview
|
||||
@ -1,13 +1,13 @@
|
||||
Ant Design Jeecg Vue
|
||||
====
|
||||
|
||||
当前最新版本: 2.4.2(发布日期:20210126)
|
||||
当前最新版本: 3.0.0(发布日期:2021-11-01)
|
||||
|
||||
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一贯的强大!!
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ Jeecg-boot 的前段UI框架,采用前后端分离方案,提供强大代码
|
||||
- 拉取项目代码
|
||||
```bash
|
||||
git clone https://github.com/zhangdaiscott/jeecg-boot.git
|
||||
cd jeecg-boot/ant-design-jeecg-vue
|
||||
cd jeecg-boot/ant-design-vue-jeecg
|
||||
```
|
||||
|
||||
- 安装依赖
|
||||
@ -93,9 +93,9 @@ yarn run lint
|
||||
|
||||
- [Vue](https://cn.vuejs.org/v2/guide)
|
||||
|
||||
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/router/README.md)
|
||||
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/router/README.md)
|
||||
|
||||
- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/defaultSettings.js)
|
||||
- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/defaultSettings.js)
|
||||
|
||||
- 其他待补充...
|
||||
|
||||
|
||||
8395
ant-design-vue-jeecg/package-lock.json
generated
8395
ant-design-vue-jeecg/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vue-antd-jeecg",
|
||||
"version": "2.4.2",
|
||||
"version": "3.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
|
||||
@ -10,8 +10,8 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@jeecg/antd-online-mini": "2.4.22-beta",
|
||||
"ant-design-vue": "^1.7.2",
|
||||
"@jeecg/antd-online-mini": "3.0.0-beta",
|
||||
"@antv/data-set": "^0.11.4",
|
||||
"viser-vue": "^2.4.8",
|
||||
"axios": "^0.18.0",
|
||||
@ -39,11 +39,12 @@
|
||||
"tinymce": "^5.3.2",
|
||||
"@toast-ui/editor": "^2.1.2",
|
||||
"vue-area-linkage": "^5.1.0",
|
||||
"area-data": "^5.0.6",
|
||||
"china-area-data": "^5.0.1",
|
||||
"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
|
||||
}
|
||||
|
||||
4
ant-design-vue-jeecg/public/index.html
vendored
4
ant-design-vue-jeecg/public/index.html
vendored
@ -5,7 +5,7 @@
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>JeecgBoot 企业级快速开发平台</title>
|
||||
<title>JeecgBoot 企业级低代码平台</title>
|
||||
<link rel="icon" href="<%= BASE_URL %>logo.png">
|
||||
<script src="<%= BASE_URL %>cdn/babel-polyfill/polyfill_7_2_5.js"></script>
|
||||
<style>
|
||||
@ -251,7 +251,7 @@
|
||||
<div id="loader"></div>
|
||||
<div class="loader-section section-left"></div>
|
||||
<div class="loader-section section-right"></div>
|
||||
<div class="load_title">正在加载 JeecgBoot 快速开发平台,请耐心等待
|
||||
<div class="load_title">正在加载 JeecgBoot 低代码平台,请耐心等待
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -38,6 +38,7 @@ const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermission
|
||||
|
||||
// 部门管理
|
||||
const queryDepartTreeList = (params)=>getAction("/sys/sysDepart/queryTreeList",params);
|
||||
const queryDepartTreeSync = (params)=>getAction("/sys/sysDepart/queryDepartTreeSync",params);
|
||||
const queryIdTree = (params)=>getAction("/sys/sysDepart/queryIdTree",params);
|
||||
const queryParentName = (params)=>getAction("/sys/sysDepart/queryParentName",params);
|
||||
const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
|
||||
@ -52,7 +53,6 @@ const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/sa
|
||||
const queryMyDepartTreeList = (params)=>getAction("/sys/sysDepart/queryMyDeptTreeList",params);
|
||||
|
||||
//日志管理
|
||||
//const getLogList = (params)=>getAction("/sys/log/list",params);
|
||||
const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
|
||||
const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
|
||||
|
||||
@ -69,7 +69,7 @@ export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItem
|
||||
function getDictItemsFromCache(dictCode) {
|
||||
if (Vue.ls.get(UI_CACHE_DB_DICT_DATA) && Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode]) {
|
||||
let dictItems = Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode];
|
||||
console.log("-----------getDictItemsFromCache----------dictCode="+dictCode+"---- dictItems=",dictItems)
|
||||
//console.log("-----------getDictItemsFromCache----------dictCode="+dictCode+"---- dictItems=",dictItems)
|
||||
return dictItems;
|
||||
}
|
||||
}
|
||||
@ -101,6 +101,8 @@ export const transitRESTful = {
|
||||
}
|
||||
|
||||
export {
|
||||
// imgView,
|
||||
// doMian,
|
||||
addRole,
|
||||
editRole,
|
||||
checkRoleCode,
|
||||
@ -124,6 +126,7 @@ export {
|
||||
getPermissionRuleList,
|
||||
queryPermissionRule,
|
||||
queryDepartTreeList,
|
||||
queryDepartTreeSync,
|
||||
queryIdTree,
|
||||
queryParentName,
|
||||
searchByKeywords,
|
||||
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -12,9 +12,9 @@
|
||||
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else :title="file.name">
|
||||
<a-icon type="paper-clip" style="color:red;"/>
|
||||
<span style="color:red;margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
@ -179,8 +179,19 @@
|
||||
value['responseName'] = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else {
|
||||
value['status'] = 'error'
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
|
||||
@ -10,20 +10,9 @@
|
||||
<template v-else-if="file['path']">
|
||||
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError"/>
|
||||
</template>
|
||||
<template slot="addonBefore" style="width: 30px">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
|
||||
@ -196,8 +185,19 @@
|
||||
value['responseName'] = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else {
|
||||
value['status'] = 'error'
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
|
||||
@ -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')
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -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')
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -39,5 +39,5 @@ UserMenu.vue:首页右上侧的内容
|
||||

|
||||
####16.trend包 趋势显示组件(如下图)
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@ -1,5 +1,4 @@
|
||||
import { pcaa } from 'area-data'
|
||||
|
||||
import Vue from 'vue'
|
||||
/**
|
||||
* 省市区
|
||||
*/
|
||||
@ -8,18 +7,23 @@ export default class Area {
|
||||
* 构造器
|
||||
* @param express
|
||||
*/
|
||||
constructor() {
|
||||
constructor(pcaa) {
|
||||
if(!pcaa){
|
||||
pcaa = Vue.prototype.$Jpcaa;
|
||||
}
|
||||
let arr = []
|
||||
const province = pcaa['86']
|
||||
Object.keys(province).map(key=>{
|
||||
arr.push({id:key, text:province[key], pid:'86'});
|
||||
arr.push({id:key, text:province[key], pid:'86', index:1});
|
||||
const city = pcaa[key];
|
||||
Object.keys(city).map(key2=>{
|
||||
arr.push({id:key2, text:city[key2], pid:key});
|
||||
arr.push({id:key2, text:city[key2], pid:key, index:2});
|
||||
const qu = pcaa[key2];
|
||||
Object.keys(qu).map(key3=>{
|
||||
arr.push({id:key3, text:qu[key3], pid:key2});
|
||||
})
|
||||
if(qu){
|
||||
Object.keys(qu).map(key3=>{
|
||||
arr.push({id:key3, text:qu[key3], pid:key2, index:3});
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
this.all = arr;
|
||||
@ -45,33 +49,35 @@ export default class Area {
|
||||
return ''
|
||||
}
|
||||
let arr = []
|
||||
this.getAreaBycode(code,arr);
|
||||
this.getAreaBycode(code, arr, 3);
|
||||
return arr.join('/')
|
||||
}
|
||||
|
||||
getRealCode(code){
|
||||
let arr = []
|
||||
this.getPcode(code, arr)
|
||||
this.getPcode(code, arr, 3)
|
||||
return arr;
|
||||
}
|
||||
|
||||
getPcode(id, arr){
|
||||
getPcode(id, arr, index){
|
||||
for(let item of this.all){
|
||||
if(item.id === id){
|
||||
if(item.id === id && item.index == index){
|
||||
arr.unshift(id)
|
||||
if(item.pid != '86'){
|
||||
this.getPcode(item.pid,arr)
|
||||
this.getPcode(item.pid, arr, --index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAreaBycode(code,arr){
|
||||
//console.log("this.all.length",this.all)
|
||||
getAreaBycode(code, arr, index){
|
||||
for(let item of this.all){
|
||||
if(item.id === code){
|
||||
if(item.id === code && item.index == index){
|
||||
arr.unshift(item.text);
|
||||
this.getAreaBycode(item.pid,arr)
|
||||
if(item.pid != '86'){
|
||||
this.getAreaBycode(item.pid, arr, --index)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,4 +32,15 @@ export const cutStrByFullLength = (str = '', maxLength) => {
|
||||
}
|
||||
return pre
|
||||
}, '')
|
||||
}
|
||||
|
||||
// 下划线转换驼峰
|
||||
export function underLinetoHump(name) {
|
||||
return name.replace(/\_(\w)/g, function(all, letter){
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
}
|
||||
// 驼峰转换下划线
|
||||
export function humptoUnderLine(name) {
|
||||
return name.replace(/([A-Z])/g,"_$1").toLowerCase();
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -48,7 +48,6 @@
|
||||
props:{
|
||||
disabled: Boolean,
|
||||
value: [String, Number],
|
||||
dict: String,
|
||||
dictOptions: Array,
|
||||
async: Boolean,
|
||||
placeholder:{
|
||||
@ -56,6 +55,11 @@
|
||||
default:"请选择",
|
||||
required:false
|
||||
},
|
||||
dict:{
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
popContainer:{
|
||||
type:String,
|
||||
default:'',
|
||||
@ -65,7 +69,11 @@
|
||||
type: Number,
|
||||
default: 10,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
getPopupContainer: {
|
||||
type:Function,
|
||||
default: null
|
||||
},
|
||||
},
|
||||
data(){
|
||||
this.loadData = debounce(this.loadData, 800);//消抖
|
||||
@ -182,16 +190,20 @@
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//异步一开始也加载一点数据
|
||||
this.loading=true
|
||||
getAction(`/sys/dict/loadDict/${this.dict}`,{pageSize: this.pageSize, keyword:''}).then(res=>{
|
||||
this.loading=false
|
||||
if(res.success){
|
||||
this.options = res.result
|
||||
}else{
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
})
|
||||
if(!this.dict){
|
||||
console.error('搜索组件未配置字典项')
|
||||
}else{
|
||||
//异步一开始也加载一点数据
|
||||
this.loading=true
|
||||
getAction(`/sys/dict/loadDict/${this.dict}`,{pageSize: this.pageSize, keyword:''}).then(res=>{
|
||||
this.loading=false
|
||||
if(res.success){
|
||||
this.options = res.result
|
||||
}else{
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
filterOption(input, option) {
|
||||
@ -226,7 +238,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)
|
||||
|
||||
@ -29,7 +29,6 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { pcaa } from 'area-data'
|
||||
import Area from '@/components/_util/Area'
|
||||
|
||||
export default {
|
||||
@ -53,7 +52,7 @@
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pcaa,
|
||||
pcaa: this.$Jpcaa,
|
||||
innerValue: [],
|
||||
usedListeners: ['change'],
|
||||
enums: {
|
||||
@ -114,7 +113,7 @@
|
||||
/** 通过地区code获取子级 */
|
||||
loadDataByCode(value) {
|
||||
let options = []
|
||||
let data = pcaa[value]
|
||||
let data = this.pcaa[value]
|
||||
if (data) {
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
@ -139,7 +138,7 @@
|
||||
},
|
||||
initAreaData(){
|
||||
if(!this.areaData){
|
||||
this.areaData = new Area();
|
||||
this.areaData = new Area(this.$Jpcaa);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -400,6 +400,10 @@
|
||||
.null-tip-hidden{
|
||||
display: none;
|
||||
}
|
||||
/**选中样式偶然出现高度不够的情况*/
|
||||
.CodeMirror-selected{
|
||||
min-height: 19px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 全屏样式 */
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
:showTime="showTime"
|
||||
:format="dateFormat"
|
||||
:getCalendarContainer="getCalendarContainer"
|
||||
/>
|
||||
v-bind="$attrs"/>
|
||||
</template>
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
|
||||
281
ant-design-vue-jeecg/src/components/jeecg/JEasyCron/EasyCron.vue
Normal file
281
ant-design-vue-jeecg/src/components/jeecg/JEasyCron/EasyCron.vue
Normal file
@ -0,0 +1,281 @@
|
||||
<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)
|
||||
this.assignInput()
|
||||
},
|
||||
minute() {
|
||||
if (this.second === '*') {
|
||||
this.second = '0'
|
||||
}
|
||||
},
|
||||
hour() {
|
||||
if (this.minute === '*') {
|
||||
this.minute = '0'
|
||||
}
|
||||
},
|
||||
day(day) {
|
||||
if (day !== '?' && this.hour === '*') {
|
||||
this.hour = '0'
|
||||
}
|
||||
},
|
||||
week(week) {
|
||||
if (week !== '?' && this.hour === '*') {
|
||||
this.hour = '0'
|
||||
}
|
||||
},
|
||||
month() {
|
||||
if (this.day === '?' && this.week === '*') {
|
||||
this.week = '1'
|
||||
} else if (this.week === '?' && this.day === '*') {
|
||||
this.day = '1'
|
||||
}
|
||||
},
|
||||
year() {
|
||||
if (this.month === '*') {
|
||||
this.month = '1'
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.formatValue()
|
||||
this.$nextTick(() => {
|
||||
this.calTriggerListInner()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
assignInput() {
|
||||
Object.assign(this.inputValues, {
|
||||
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,
|
||||
})
|
||||
},
|
||||
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]
|
||||
this.assignInput()
|
||||
},
|
||||
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>
|
||||
@ -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>
|
||||
@ -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
|
||||
@ -0,0 +1,6 @@
|
||||
// 原开源项目地址:https://gitee.com/toktok/easy-cron
|
||||
|
||||
import InputCron from './InputCron.vue'
|
||||
|
||||
InputCron.name = 'JEasyCron'
|
||||
export default InputCron
|
||||
@ -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
|
||||
}
|
||||
101
ant-design-vue-jeecg/src/components/jeecg/JEasyCron/tabs/day.vue
Normal file
101
ant-design-vue-jeecg/src/components/jeecg/JEasyCron/tabs/day.vue
Normal 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 of specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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>
|
||||
@ -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 specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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>
|
||||
@ -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 specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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>
|
||||
@ -0,0 +1,162 @@
|
||||
// 主要用于日和星期的互斥使用
|
||||
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:
|
||||
if (this.valueList.length === 0) {
|
||||
this.valueList.push(this.minValue)
|
||||
}
|
||||
result.push(this.valueList.join(','))
|
||||
break
|
||||
default:
|
||||
result.push(this.DEFAULT_VALUE)
|
||||
break
|
||||
}
|
||||
return result.length > 0 ? result.join('') : this.DEFAULT_VALUE
|
||||
},
|
||||
// 指定值范围区间,介于最小值和最大值之间
|
||||
specifyRange() {
|
||||
let range = []
|
||||
for (let i = this.minValue; i <= this.maxValue; i++) {
|
||||
range.push(i)
|
||||
}
|
||||
return range
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 of specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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>
|
||||
@ -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 specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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>
|
||||
@ -0,0 +1,118 @@
|
||||
<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 specifyRange">
|
||||
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</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 = {
|
||||
'周一': 1,
|
||||
'周二': 2,
|
||||
'周三': 3,
|
||||
'周四': 4,
|
||||
'周五': 5,
|
||||
'周六': 6,
|
||||
// 按照国人习惯,将周日放到每周的最后一天
|
||||
'周日': 7,
|
||||
}
|
||||
|
||||
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 = 1
|
||||
this.maxValue = 7
|
||||
this.valueRange.start = 1
|
||||
this.valueRange.end = 7
|
||||
this.valueLoop.start = 2
|
||||
this.valueLoop.interval = 1
|
||||
this.parseProp(this.prop)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import "mixin.less";
|
||||
</style>
|
||||
@ -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>
|
||||
@ -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
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- JEditableTable -->
|
||||
<!-- @version 1.6.1 -->
|
||||
<!-- @version 1.6.2 -->
|
||||
<!-- @author sjlei -->
|
||||
<template>
|
||||
<a-spin :spinning="loading">
|
||||
@ -11,7 +11,33 @@
|
||||
<a-col>
|
||||
<!-- 操作按钮 -->
|
||||
<div v-if="actionButton" class="action-button">
|
||||
<a-button v-if="buttonPermission('add')" type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button>
|
||||
<a-button-group v-if="buttonPermission('add')">
|
||||
<a-button type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button>
|
||||
<a-popover v-if="addButtonSettings" placement="right" overlayClassName="j-add-btn-settings">
|
||||
<a-row slot="title">
|
||||
<a-col :span="12">选项</a-col>
|
||||
<a-col :span="12" style="text-align: right;">
|
||||
<a-tooltip title="保存为默认值">
|
||||
<a-button type="link" icon="save" size="small" style="position: relative;left:4px;" @click="onAddButtonSettingsSave"/>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<template slot="content">
|
||||
<a-form-model layout="horizontal" :labelCol="{span:8}" :wrapperCol="{span:16}">
|
||||
<a-form-model-item label="添加行数">
|
||||
<a-input-number v-model="settings.addRowNum" :min="1"/>
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="添加位置">
|
||||
<a-input-number v-model="settings.addIndex" :min="0" :max="rows.length"/>
|
||||
<p style="font-size: 12px;color:#aaa;line-height: 14px;text-align: right;margin: 0;">0 = 最底部</p>
|
||||
</a-form-model-item>
|
||||
<a-divider style="margin: 8px 0;"/>
|
||||
<a-checkbox v-model="settings.addScrollToBottom">添加后滚动到底部</a-checkbox>
|
||||
</a-form-model>
|
||||
</template>
|
||||
<a-button icon="setting" type="primary"></a-button>
|
||||
</a-popover>
|
||||
</a-button-group>
|
||||
<span class="gap"></span>
|
||||
<template v-if="selectedRowIds.length>0">
|
||||
<a-popconfirm
|
||||
@ -215,7 +241,7 @@
|
||||
:value="departCompValues[id]"
|
||||
:placeholder="replaceProps(col, col.placeholder)"
|
||||
:trigger-change="true"
|
||||
:multi="true"
|
||||
:multi="isMultipleSelect(col)"
|
||||
@change="(v)=>handleChangeDepartCommon(v,id,row,col)"
|
||||
/>
|
||||
<span
|
||||
@ -239,7 +265,7 @@
|
||||
:value="userCompValues[id]"
|
||||
:placeholder="replaceProps(col, col.placeholder)"
|
||||
:trigger-change="true"
|
||||
:multi="true"
|
||||
:multi="isMultipleSelect(col)"
|
||||
@change="(v)=>handleChangeUserCommon(v,id,row,col)"
|
||||
/>
|
||||
<span
|
||||
@ -277,8 +303,33 @@
|
||||
>{{ jdateValues[id] }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<!-- time -->
|
||||
<template v-else-if="col.type === formTypes.time">
|
||||
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
|
||||
<j-time
|
||||
v-if="isEditRow(row, col)"
|
||||
:id="id"
|
||||
:key="i"
|
||||
v-bind="buildProps(row,col)"
|
||||
style="width: 100%;"
|
||||
:value="jdateValues[id]"
|
||||
:getCalendarContainer="getParentContainer"
|
||||
:placeholder="replaceProps(col, col.placeholder)"
|
||||
allowClear
|
||||
@change="(v)=>handleChangeJDateCommon(v,id,row,col)"
|
||||
/>
|
||||
<span
|
||||
v-else
|
||||
class="j-td-span no-edit"
|
||||
:class="{disabled: buildProps(row,col).disabled}"
|
||||
@click="handleEditRow(row, col)"
|
||||
>{{ jdateValues[id] }}</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<!-- input_pop -->
|
||||
<template v-else-if="col.type === formTypes.input_pop">
|
||||
<template v-else-if="col.type === formTypes.input_pop||col.type === 'textarea'">
|
||||
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
|
||||
<j-input-pop
|
||||
v-if="isEditRow(row, col)"
|
||||
@ -318,7 +369,7 @@
|
||||
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@ -350,7 +401,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 +432,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
|
||||
@ -395,7 +448,7 @@
|
||||
|
||||
<!-- update-beign-author:taoyan date:0827 for:文件/图片逻辑新增 -->
|
||||
<div v-else-if="col.type === formTypes.file" :key="i">
|
||||
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
|
||||
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading" style="color:red;"/>
|
||||
@ -407,9 +460,9 @@
|
||||
<span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else :title="file.name">
|
||||
<a-icon type="paper-clip" style="color:red;"/>
|
||||
<span style="color:red;margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
<span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
@ -434,7 +487,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div :hidden="uploadValues[id] != null">
|
||||
<div :hidden="hasUploadValue(id)">
|
||||
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
|
||||
<a-upload
|
||||
name="file"
|
||||
@ -454,7 +507,7 @@
|
||||
</div>
|
||||
|
||||
<div v-else-if="col.type === formTypes.image" :key="i">
|
||||
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
|
||||
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<template v-if="!uploadValues[id] || !(uploadValues[id]['url'] || uploadValues[id]['path'] || uploadValues[id]['message'])">
|
||||
<a-icon type="loading"/>
|
||||
@ -462,20 +515,9 @@
|
||||
<template v-else-if="uploadValues[id]['path']">
|
||||
<img class="j-editable-image" :src="getCellImageView(id)" alt="无图片" @click="handleMoreOperation(id,'img',col)"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError(id)"/>
|
||||
</template>
|
||||
<template slot="addonBefore" style="width: 30px">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError(id)">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" :getPopupContainer="getParentContainer" style="margin-left: 10px;">
|
||||
@ -503,7 +545,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div :hidden="uploadValues[id] != null">
|
||||
<div :hidden="hasUploadValue(id)">
|
||||
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
|
||||
<a-upload
|
||||
name="file"
|
||||
@ -595,6 +637,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 +684,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 +741,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
|
||||
@ -709,6 +778,11 @@
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示添加按钮选项
|
||||
addButtonSettings: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否显示行号
|
||||
rowNumber: {
|
||||
type: Boolean,
|
||||
@ -814,6 +888,7 @@
|
||||
metaCheckboxValues: {},
|
||||
multiSelectValues: {},
|
||||
searchSelectValues: {},
|
||||
searchSelectAsyncValues: {},
|
||||
// 绑定左侧选择框已选择的id
|
||||
selectedRowIds: [],
|
||||
// 存储被删除行的id
|
||||
@ -836,7 +911,16 @@
|
||||
lastPushTimeMap: new Map(),
|
||||
number:0,
|
||||
//不显示的按钮编码
|
||||
excludeCode:[]
|
||||
excludeCode:[],
|
||||
// 选项配置
|
||||
settings: {
|
||||
// 添加行数
|
||||
addRowNum: 1,
|
||||
// 添加位置(下标),0 = 最底部
|
||||
addIndex: 0,
|
||||
// 添加后滚动到底部
|
||||
addScrollToBottom: false,
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -851,6 +935,7 @@
|
||||
event.stopPropagation()
|
||||
}
|
||||
}
|
||||
this.getSavedAddButtonSettings()
|
||||
},
|
||||
// 计算属性
|
||||
computed: {
|
||||
@ -1003,7 +1088,11 @@
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 判断文件/图片是否存在
|
||||
hasUploadValue(id){
|
||||
let flag = this.uploadValues[id] != null && this.uploadValues[id].toString().length>0
|
||||
return flag;
|
||||
},
|
||||
getElement(id, noCaseId = false) {
|
||||
if (!this.el[id]) {
|
||||
this.el[id] = document.getElementById((noCaseId ? '' : this.caseId) + id)
|
||||
@ -1048,6 +1137,10 @@
|
||||
this.inputValues = []
|
||||
this.rows = []
|
||||
this.deleteIds = []
|
||||
this.selectedRowIds = []
|
||||
this.tooltips = {}
|
||||
this.notPassedIds = []
|
||||
// 重置values
|
||||
this.selectValues = {}
|
||||
this.checkboxValues = {}
|
||||
this.jdateValues = {}
|
||||
@ -1055,14 +1148,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 +1231,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) => {
|
||||
@ -1207,7 +1303,7 @@
|
||||
selectValues[inputId] = undefined
|
||||
}
|
||||
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
|
||||
jdateValues[inputId] = sourceValue
|
||||
|
||||
} else if (column.type === FormTypes.slot) {
|
||||
@ -1219,12 +1315,14 @@
|
||||
departCompValues[inputId] = sourceValue
|
||||
} else if (column.type === FormTypes.sel_user) {
|
||||
userCompValues[inputId] = sourceValue
|
||||
} else if (column.type === FormTypes.input_pop) {
|
||||
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
|
||||
jInputPopValues[inputId] = sourceValue
|
||||
} else if (column.type === FormTypes.radio) {
|
||||
radioValues[inputId] = sourceValue
|
||||
} 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 +1343,8 @@
|
||||
status: 'done',
|
||||
path: sourceValue
|
||||
}
|
||||
} else {
|
||||
uploadValues[inputId] = null
|
||||
}
|
||||
} else {
|
||||
value[column.key] = sourceValue
|
||||
@ -1309,6 +1409,7 @@
|
||||
this.radioValues = radioValues
|
||||
this.multiSelectValues = multiSelectValues
|
||||
this.searchSelectValues = searchSelectValues
|
||||
this.searchSelectAsyncValues = searchSelectAsyncValues
|
||||
// 重新计算所有统计列
|
||||
this.recalcAllStatisticsColumns()
|
||||
// 更新到 dom
|
||||
@ -1370,22 +1471,18 @@
|
||||
let tbody = this.getElement('tbody')
|
||||
let offsetHeight = tbody.offsetHeight
|
||||
let realScrollTop = tbody.scrollTop + offsetHeight
|
||||
if (forceScrollToBottom === false) {
|
||||
// 只有滚动条在底部的时候才自动滚动
|
||||
if (!((tbody.scrollHeight - realScrollTop) <= 10)) {
|
||||
return
|
||||
}
|
||||
if (forceScrollToBottom) {
|
||||
this.$nextTick(() => {
|
||||
this.resetScrollTop(this.$refs.scrollView.scrollHeight)
|
||||
})
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
tbody.scrollTop = tbody.scrollHeight
|
||||
})
|
||||
},
|
||||
/**
|
||||
* 在指定位置添加一行
|
||||
* @param insertIndex 添加位置下标
|
||||
* @param num 添加的行数,默认1
|
||||
*/
|
||||
insert(insertIndex, num = 1) {
|
||||
insert(insertIndex, num = 1, forceScrollToBottom = false) {
|
||||
if (this.checkTooFastClick('insert', 1500)) {
|
||||
return
|
||||
}
|
||||
@ -1413,6 +1510,12 @@
|
||||
num, insertIndex,
|
||||
target: this
|
||||
})
|
||||
// 设置滚动条位置
|
||||
if (forceScrollToBottom) {
|
||||
this.$nextTick(() => {
|
||||
this.resetScrollTop(this.$refs.scrollView.scrollHeight)
|
||||
})
|
||||
}
|
||||
},
|
||||
/** 删除被选中的行 */
|
||||
removeSelectedRows() {
|
||||
@ -1514,7 +1617,7 @@
|
||||
value[column.key] = selected
|
||||
}
|
||||
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
|
||||
value[column.key] = this.jdateValues[inputId]
|
||||
|
||||
} else if (column.type === FormTypes.sel_depart) {
|
||||
@ -1523,7 +1626,7 @@
|
||||
} else if (column.type === FormTypes.sel_user) {
|
||||
value[column.key] = this.userCompValues[inputId]
|
||||
|
||||
} else if (column.type === FormTypes.input_pop) {
|
||||
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
|
||||
value[column.key] = this.jInputPopValues[inputId]
|
||||
|
||||
} else if (column.type === FormTypes.upload) {
|
||||
@ -1543,6 +1646,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 +1776,7 @@
|
||||
radioValues: this.radioValues,
|
||||
multiSelectValues: this.multiSelectValues,
|
||||
searchSelectValues: this.searchSelectValues,
|
||||
searchSelectAsyncValues: this.searchSelectAsyncValues,
|
||||
})
|
||||
},
|
||||
/** 设置某行某列的值 */
|
||||
@ -1716,13 +1822,13 @@
|
||||
}
|
||||
this.$set(this.checkboxValues, key, sourceValue)
|
||||
edited = true
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
|
||||
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
|
||||
edited = this.setOneValue(this.jdateValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.sel_depart) {
|
||||
edited = this.setOneValue(this.departCompValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.sel_user) {
|
||||
edited = this.setOneValue(this.userCompValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.input_pop) {
|
||||
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
|
||||
edited = this.setOneValue(this.jInputPopValues, modelKey, newValue)
|
||||
} else if (column.type === FormTypes.slot) {
|
||||
edited = this.setOneValue(this.slotValues, modelKey, newValue)
|
||||
@ -1736,6 +1842,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
|
||||
}
|
||||
@ -2048,7 +2156,12 @@
|
||||
|
||||
},
|
||||
handleClickAdd() {
|
||||
this.add()
|
||||
let {addRowNum, addIndex, addScrollToBottom} = this.settings
|
||||
if (addIndex <= 0) {
|
||||
this.add(addRowNum, addScrollToBottom)
|
||||
} else {
|
||||
this.insert(addIndex, addRowNum, addScrollToBottom)
|
||||
}
|
||||
},
|
||||
handleConfirmDelete() {
|
||||
this.removeSelectedRows()
|
||||
@ -2059,6 +2172,29 @@
|
||||
clearSelection() {
|
||||
this.selectedRowIds = []
|
||||
},
|
||||
// 获取当前选中的行
|
||||
getSelection() {
|
||||
return this.selectedRowIds.map(id => this.getCleanId(id))
|
||||
},
|
||||
// 设置当前选中的行
|
||||
async setSelection(selectedRowIds) {
|
||||
if (Array.isArray(selectedRowIds) && selectedRowIds.length > 0) {
|
||||
// 兼容IE
|
||||
await this.getElementPromise('tbody')
|
||||
await this.$nextTick()
|
||||
this.selectedRowIds = selectedRowIds.map(id => {
|
||||
let temp = id
|
||||
if (!this.hasCaseId(id)) {
|
||||
temp = this.caseId + id
|
||||
}
|
||||
return temp
|
||||
})
|
||||
}
|
||||
},
|
||||
// 切换全选状态
|
||||
toggleSelectionAll() {
|
||||
this.handleChangeCheckedAll()
|
||||
},
|
||||
/** 用于搜索下拉框中的内容 */
|
||||
handleSelectFilterOption(input, option, column) {
|
||||
if (column.allowSearch === true || column.allowInput === true) {
|
||||
@ -2266,11 +2402,7 @@
|
||||
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
|
||||
|
||||
// 触发valueChange 事件
|
||||
if (showTime) {
|
||||
this.elemValueChange(FormTypes.datetime, row, column, value)
|
||||
} else {
|
||||
this.elemValueChange(FormTypes.date, row, column, value)
|
||||
}
|
||||
this.elemValueChange(column.type, row, column, value)
|
||||
},
|
||||
//部门组件值改变
|
||||
handleChangeDepartCommon(value, id, row, column){
|
||||
@ -2306,7 +2438,21 @@
|
||||
value['responseName'] = file.response[column.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[column.responseName]
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
// 如果文件上传,被拦截器拦下,还会返回最外层的status = done
|
||||
// 但是内部的success会返回false并携带异常信息
|
||||
// 整个上传操作还是失败的
|
||||
// https://github.com/zhangdaiscott/jeecg-boot/issues/2691
|
||||
if (file.response.success) {
|
||||
value['path'] = file.response[column.responseName]
|
||||
} else {
|
||||
value['status'] = 'error'
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value['path'] = file.response[column.responseName]
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
@ -2368,6 +2514,25 @@
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
/** 添加按钮设置保存为默认值 */
|
||||
onAddButtonSettingsSave() {
|
||||
let obj = {
|
||||
addRowNum: this.settings.addRowNum,
|
||||
addIndex: this.settings.addIndex,
|
||||
addScrollToBottom: this.settings.addScrollToBottom,
|
||||
}
|
||||
this.$ls.set('jet-add-btn-settings', obj)
|
||||
this.$message.success('保存成功')
|
||||
},
|
||||
/** 获取保存的添加按钮默认值 */
|
||||
getSavedAddButtonSettings() {
|
||||
let obj= this.$ls.get('jet-add-btn-settings')
|
||||
if (obj) {
|
||||
Object.assign(this.settings, obj)
|
||||
}
|
||||
},
|
||||
|
||||
/** 记录用到数据绑定的组件的值 */
|
||||
bindValuesChange(value, id, key) {
|
||||
this.$set(this[key], id, value)
|
||||
@ -2619,6 +2784,11 @@
|
||||
if (col.type === FormTypes.select && (col.allowInput === true || col.allowSearch === true)) {
|
||||
props['showSearch'] = true
|
||||
}
|
||||
if (col.type === FormTypes.sel_depart || col.type === FormTypes.sel_user) {
|
||||
let { storeField, textField } = this.getStoreAndTextField(col)
|
||||
props['store'] = storeField
|
||||
props['text'] = textField
|
||||
}
|
||||
|
||||
// 判断是否是禁用的列
|
||||
props['disabled'] = (typeof col['disabled'] === 'boolean' ? col['disabled'] : props['disabled'])
|
||||
@ -2636,6 +2806,42 @@
|
||||
return props
|
||||
},
|
||||
|
||||
/**获取部门选择 、用户选择的存储字段、展示字段*/
|
||||
getStoreAndTextField(col){
|
||||
let storeField = '', textField = ''
|
||||
if(col.type === FormTypes.sel_depart){
|
||||
storeField = 'id'
|
||||
textField = 'departName'
|
||||
}else if(col.type === FormTypes.sel_user){
|
||||
storeField = 'username'
|
||||
textField = 'realname'
|
||||
}
|
||||
if(col.fieldExtendJson){
|
||||
// online逻辑
|
||||
let tempJson = JSON.parse(col.fieldExtendJson)
|
||||
if(tempJson){
|
||||
if(tempJson.store){
|
||||
storeField = tempJson.store
|
||||
}
|
||||
if(tempJson.text){
|
||||
textField = tempJson.text
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// 实际开发逻辑
|
||||
if(col.store){
|
||||
storeField = col.store
|
||||
}
|
||||
if(col.text){
|
||||
textField = col.text
|
||||
}
|
||||
}
|
||||
return {
|
||||
storeField,
|
||||
textField
|
||||
}
|
||||
},
|
||||
|
||||
/** 辅助方法:防止过快点击,如果点击过快的话就返回 true */
|
||||
checkTooFastClick(key = 'default', ms = 300) {
|
||||
let nowTime = Date.now()
|
||||
@ -2704,7 +2910,7 @@
|
||||
}
|
||||
this.setOneValue(this.popupValues, id, popupValue)
|
||||
// 做单个表单验证
|
||||
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
|
||||
this.validateOneInput(popupValue, row, column, this.notPassedIds, true, 'change')
|
||||
// 触发valueChange 事件
|
||||
this.elemValueChange('input', row, column, value)
|
||||
},
|
||||
@ -2731,6 +2937,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
|
||||
},
|
||||
@ -2844,6 +3055,21 @@
|
||||
}else{
|
||||
return this.excludeCode.indexOf(code)<0
|
||||
}
|
||||
},
|
||||
// 判断用户、部门组件是否多选
|
||||
isMultipleSelect(column){
|
||||
let jsonStr = column.fieldExtendJson
|
||||
if(jsonStr){
|
||||
// online
|
||||
let config = JSON.parse(jsonStr)
|
||||
if(config && config['multiSelect']==false){
|
||||
return false
|
||||
}
|
||||
}else if(column.multi==false){
|
||||
// 实际开发
|
||||
return false
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
},
|
||||
@ -2992,6 +3218,8 @@
|
||||
border-bottom: @border;
|
||||
transition: background-color 300ms;
|
||||
width: 100%;
|
||||
height: 61px;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
@ -3101,6 +3329,12 @@
|
||||
}
|
||||
}
|
||||
|
||||
.comp-normal {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.j-td-span {
|
||||
position: relative;
|
||||
padding: 4px 11px;
|
||||
@ -3220,3 +3454,19 @@
|
||||
}
|
||||
|
||||
</style>
|
||||
<style lang="less">
|
||||
// 新增按钮配置气泡的样式
|
||||
.j-add-btn-settings {
|
||||
width: 240px;
|
||||
|
||||
.ant-form {
|
||||
.ant-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
.ant-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -128,12 +128,23 @@
|
||||
this.reload()
|
||||
}
|
||||
})
|
||||
//update--begin--autor:liusq-----date:20210316------for:富文本编辑器tab父组件可能导致的赋值问题------
|
||||
this.reload()
|
||||
//update--end--autor:liusq-----date:20210316------for:富文本编辑器tab父组件可能导致的赋值问题------
|
||||
}else{
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:富文本编辑器切换tab无法修改------
|
||||
let tabLayout = getVmParentByName(this, 'TabLayout')
|
||||
tabLayout.excuteCallback(()=>{
|
||||
this.reload()
|
||||
})
|
||||
//update--begin--autor:liusq-----date:20210713------for:处理特殊情况excuteCallback不能使用------
|
||||
try {
|
||||
tabLayout.excuteCallback(() => {
|
||||
this.reload()
|
||||
})
|
||||
} catch (error) {
|
||||
if (tabLayout) {
|
||||
this.reload()
|
||||
}
|
||||
}
|
||||
//update--end--autor:liusq-----date:20210713------for:处理特殊情况excuteCallback不能使用------
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:文本编辑器切换tab无法修改------
|
||||
}
|
||||
},
|
||||
|
||||
@ -11,13 +11,13 @@
|
||||
:beforeUpload="beforeUpload"
|
||||
:disabled="disabled"
|
||||
:isMultiple="isMultiple"
|
||||
:showUploadList="isMultiple"
|
||||
|
||||
@change="handleChange"
|
||||
@preview="handlePreview"
|
||||
:class="!isMultiple?'imgupload':''">
|
||||
<div :style="{'width':(!isMultiple?'104px':'auto'),'height':(!isMultiple?'104px':'auto')}">
|
||||
<img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="width:100%;height:100%"/>
|
||||
<div v-else class="iconp">
|
||||
:class="[!isMultiple?'imgupload':'', (!isMultiple && picUrl)?'image-upload-single-over':'' ]">
|
||||
<div>
|
||||
<!--<img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="width:100%;height:100%"/>-->
|
||||
<div class="iconp">
|
||||
<a-icon :type="uploadLoading ? 'loading' : 'plus'" />
|
||||
<div class="ant-upload-text">{{ text }}</div>
|
||||
</div>
|
||||
@ -189,7 +189,7 @@
|
||||
path = ''
|
||||
}
|
||||
let arr = [];
|
||||
if(!this.isMultiple){
|
||||
if(!this.isMultiple && uploadFiles.length>0){
|
||||
arr.push(uploadFiles[uploadFiles.length-1].response.message)
|
||||
}else{
|
||||
for(let a=0;a<uploadFiles.length;a++){
|
||||
@ -231,8 +231,9 @@
|
||||
* https://github.com/zhangdaiscott/jeecg-boot/issues/1810
|
||||
* https://github.com/zhangdaiscott/jeecg-boot/issues/1779
|
||||
*/
|
||||
/deep/ .imgupload .ant-upload-select{display:block}
|
||||
/deep/ .imgupload .ant-upload.ant-upload-select-picture-card{ width:120px;height: 120px;}
|
||||
/deep/ .imgupload .iconp{padding:32px;}
|
||||
|
||||
/deep/ .imgupload .iconp{padding:20px;}
|
||||
/* update--end--autor:lvdandan-----date:20201016------for:j-image-upload图片组件单张图片详情回显空白*/
|
||||
|
||||
/deep/ .image-upload-single-over .ant-upload-select{display: none}
|
||||
</style>
|
||||
|
||||
@ -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() {},
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,10 +8,11 @@
|
||||
v-on="$listeners"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
destroyOnClose
|
||||
>
|
||||
|
||||
<slot></slot>
|
||||
|
||||
<!--有设置标题-->
|
||||
<template v-if="!isNoTitle" slot="title">
|
||||
<a-row class="j-modal-title-row" type="flex">
|
||||
<a-col class="left">
|
||||
@ -22,6 +23,14 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
<!--没有设置标题-->
|
||||
<template v-else slot="title">
|
||||
<a-row class="j-modal-title-row" type="flex">
|
||||
<a-col v-if="switchFullscreen" class="right" @click="toggleFullscreen">
|
||||
<a-button class="ant-modal-close ant-modal-close-x" ghost type="link" :icon="fullscreenButtonIcon"/>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<!-- 处理 scopedSlots -->
|
||||
<template v-for="slotName of scopedSlotsKeys" :slot="slotName">
|
||||
@ -38,10 +47,10 @@
|
||||
|
||||
<script>
|
||||
|
||||
import { getClass, getStyle } from '@/utils/props-util'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
import { getClass, getStyle } from '@/utils/props-util'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
export default {
|
||||
name: 'JModal',
|
||||
props: {
|
||||
title: String,
|
||||
@ -160,8 +169,8 @@
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
|
||||
.j-modal-box {
|
||||
|
||||
&.fullscreen {
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -190,7 +199,6 @@
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
}
|
||||
|
||||
&.no-title.no-footer {
|
||||
.ant-modal-body {
|
||||
height: 100%;
|
||||
@ -217,6 +225,12 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
&.no-title{
|
||||
.ant-modal-header {
|
||||
padding: 0px 24px;
|
||||
border-bottom: 0px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
124
ant-design-vue-jeecg/src/components/jeecg/JModal/JPrompt.vue
Normal file
124
ant-design-vue-jeecg/src/components/jeecg/JModal/JPrompt.vue
Normal file
@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<j-modal :visible="visible" :confirmLoading="loading" :after-close="afterClose" v-bind="modalProps" @ok="onOk" @cancel="onCancel">
|
||||
<a-spin :spinning="loading">
|
||||
<div v-html="content"></div>
|
||||
<a-form-model ref="form" :model="model" :rules="rules">
|
||||
<a-form-model-item prop="input">
|
||||
<a-input ref="input" v-model="model.input" v-bind="inputProps" @pressEnter="onInputPressEnter"/>
|
||||
</a-form-model-item>
|
||||
</a-form-model>
|
||||
</a-spin>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import pick from 'lodash.pick'
|
||||
|
||||
export default {
|
||||
name: 'JPrompt',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
content: '',
|
||||
// 弹窗参数
|
||||
modalProps: {
|
||||
title: '',
|
||||
},
|
||||
inputProps: {
|
||||
placeholder: '',
|
||||
},
|
||||
// form model
|
||||
model: {
|
||||
input: '',
|
||||
},
|
||||
// 校验
|
||||
rule: [],
|
||||
// 回调函数
|
||||
callback: {},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rules() {
|
||||
return {
|
||||
input: this.rule
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
show(options) {
|
||||
this.content = options.content
|
||||
if (Array.isArray(options.rule)) {
|
||||
this.rule = options.rule
|
||||
}
|
||||
if (options.defaultValue != null) {
|
||||
this.model.input = options.defaultValue
|
||||
}
|
||||
// 取出常用的弹窗参数
|
||||
let pickModalProps = pick(options, 'title', 'centered', 'cancelText', 'closable', 'mask', 'maskClosable', 'okText', 'okType', 'okButtonProps', 'cancelButtonProps', 'width', 'wrapClassName', 'zIndex', 'dialogStyle', 'dialogClass')
|
||||
this.modalProps = Object.assign({}, pickModalProps, options.modalProps)
|
||||
// 取出常用的input参数
|
||||
let pickInputProps = pick(options, 'placeholder', 'allowClear')
|
||||
this.inputProps = Object.assign({}, pickInputProps, options.inputProps)
|
||||
// 回调函数
|
||||
this.callback = pick(options, 'onOk', 'onOkAsync', 'onCancel')
|
||||
this.visible = true
|
||||
this.$nextTick(() => this.$refs.input.focus())
|
||||
},
|
||||
|
||||
onOk() {
|
||||
this.$refs.form.validate((ok, err) => {
|
||||
if (ok) {
|
||||
let event = {value: this.model.input, target: this}
|
||||
// 异步方法优先级高于同步方法
|
||||
if (typeof this.callback.onOkAsync === 'function') {
|
||||
this.callback.onOkAsync(event)
|
||||
} else if (typeof this.callback.onOk === 'function') {
|
||||
this.callback.onOk(event)
|
||||
this.close()
|
||||
} else {
|
||||
this.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
onCancel() {
|
||||
if (typeof this.callback.onCancel === 'function') {
|
||||
this.callback.onCancel(this.model.input)
|
||||
}
|
||||
this.close()
|
||||
},
|
||||
|
||||
onInputPressEnter() {
|
||||
this.onOk()
|
||||
},
|
||||
|
||||
close() {
|
||||
this.visible = this.loading ? this.visible : false
|
||||
},
|
||||
|
||||
forceClose() {
|
||||
this.visible = false
|
||||
},
|
||||
|
||||
showLoading() {
|
||||
this.loading = true
|
||||
},
|
||||
hideLoading() {
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
afterClose(e) {
|
||||
if (typeof this.modalProps.afterClose === 'function') {
|
||||
this.modalProps.afterClose(e)
|
||||
}
|
||||
this.$emit('after-close', e)
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
18
ant-design-vue-jeecg/src/components/jeecg/JModal/index.js
Normal file
18
ant-design-vue-jeecg/src/components/jeecg/JModal/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
import JModal from './JModal'
|
||||
import JPrompt from './JPrompt'
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.component(JModal.name, JModal)
|
||||
|
||||
const JPromptExtend = Vue.extend(JPrompt)
|
||||
Vue.prototype.$JPrompt = function (options = {}) {
|
||||
// 创建prompt实例
|
||||
const vm = new JPromptExtend().$mount()
|
||||
vm.show(options)
|
||||
// 关闭后销毁
|
||||
vm.$on('after-close', () => vm.$destroy())
|
||||
return vm
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -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,
|
||||
@ -167,9 +173,11 @@
|
||||
let tempDestArr = []
|
||||
for(let rw of rows){
|
||||
let val = rw[orgFieldsArr[i]]
|
||||
if(!val){
|
||||
// update--begin--autor:liusq-----date:20210713------for:处理val等于0的情况issues/I3ZL4T------
|
||||
if(typeof val=='undefined'|| val==null || val.toString()==""){
|
||||
val = ""
|
||||
}
|
||||
// update--end--autor:liusq-----date:20210713------for:处理val等于0的情况issues/I3ZL4T------
|
||||
tempDestArr.push(val)
|
||||
}
|
||||
res[destFieldsArr[i]] = tempDestArr.join(",")
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
|
||||
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear>
|
||||
<a-select-option
|
||||
v-for="(item,index) in options"
|
||||
v-for="(item,index) in selectOptions"
|
||||
:key="index"
|
||||
:getPopupContainer="getParentContainer"
|
||||
:value="item.value">
|
||||
@ -12,6 +12,8 @@
|
||||
|
||||
<script>
|
||||
//option {label:,value:}
|
||||
import { getAction } from '@api/manage'
|
||||
|
||||
export default {
|
||||
name: 'JSelectMultiple',
|
||||
props: {
|
||||
@ -31,7 +33,8 @@
|
||||
},
|
||||
options:{
|
||||
type: Array,
|
||||
required: true
|
||||
default:()=>[],
|
||||
required: false
|
||||
},
|
||||
triggerChange:{
|
||||
type: Boolean,
|
||||
@ -48,12 +51,22 @@
|
||||
default:'',
|
||||
required:false
|
||||
},
|
||||
dictCode:{
|
||||
type:String,
|
||||
required:false
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
arrayValue:!this.value?[]:this.value.split(this.spliter)
|
||||
arrayValue:!this.value?[]:this.value.split(this.spliter),
|
||||
dictOptions: [],
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
selectOptions(){
|
||||
return this.dictOptions.length > 0 ? this.dictOptions : this.options
|
||||
},
|
||||
},
|
||||
watch:{
|
||||
value (val) {
|
||||
if(!val){
|
||||
@ -63,6 +76,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
if (this.dictCode) {
|
||||
this.loadDictOptions()
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
onChange (selectedValue) {
|
||||
if(this.triggerChange){
|
||||
@ -77,7 +95,18 @@
|
||||
}else{
|
||||
return document.querySelector(this.popContainer)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 根据字典code查询字典项
|
||||
loadDictOptions(){
|
||||
getAction(`/sys/dict/getDictItems/${this.dictCode}`,{}).then(res=>{
|
||||
if (res.success) {
|
||||
this.dictOptions = res.result.map(item => ({value: item.value, label: item.text}))
|
||||
} else {
|
||||
console.error('getDictItems error: : ', res)
|
||||
this.dictOptions = []
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@ -379,7 +379,7 @@
|
||||
this.$message.warn("不能查询空条件")
|
||||
}
|
||||
},
|
||||
emitCallback(event = {}) {
|
||||
emitCallback(event = {}, loadStatus = true) {
|
||||
let { params = [], matchType = this.matchType } = event
|
||||
this.superQueryFlag = (params && params.length > 0)
|
||||
for (let param of params) {
|
||||
@ -388,7 +388,7 @@
|
||||
}
|
||||
}
|
||||
console.debug('---高级查询参数--->', { params, matchType })
|
||||
this.$emit(this.callback, params, matchType)
|
||||
this.$emit(this.callback, params, matchType, loadStatus)
|
||||
},
|
||||
handleCancel() {
|
||||
this.close()
|
||||
@ -412,8 +412,9 @@
|
||||
this.queryParamsModel.splice(index, 1)
|
||||
},
|
||||
handleSelected(node, item) {
|
||||
let { type, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef
|
||||
let { type, dbType, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef
|
||||
item['type'] = type
|
||||
item['dbType'] = dbType
|
||||
item['options'] = options
|
||||
item['dictCode'] = dictCode
|
||||
item['dictTable'] = dictTable
|
||||
@ -427,9 +428,13 @@
|
||||
handleOpen() {
|
||||
this.show()
|
||||
},
|
||||
handleOutReset(loadStaus=true) {
|
||||
this.resetLine()
|
||||
this.emitCallback({}, loadStaus)
|
||||
},
|
||||
handleReset() {
|
||||
this.resetLine()
|
||||
this.emitCallback()
|
||||
this.emitCallback({}, true)
|
||||
},
|
||||
handleSave() {
|
||||
let queryParams = this.removeEmptyObject(this.queryParamsModel)
|
||||
|
||||
@ -12,12 +12,12 @@
|
||||
|
||||
<a-upload
|
||||
name="file"
|
||||
:multiple="true"
|
||||
:multiple="multiple"
|
||||
:action="uploadAction"
|
||||
:headers="headers"
|
||||
:data="{'biz':bizPath}"
|
||||
:fileList="fileList"
|
||||
:beforeUpload="beforeUpload"
|
||||
:beforeUpload="doBeforeUpload"
|
||||
@change="handleChange"
|
||||
:disabled="disabled"
|
||||
:returnUrl="returnUrl"
|
||||
@ -135,6 +135,13 @@
|
||||
required:false,
|
||||
default: true
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
beforeUpload: {
|
||||
type: Function
|
||||
},
|
||||
},
|
||||
watch:{
|
||||
value:{
|
||||
@ -238,7 +245,7 @@
|
||||
}
|
||||
this.$emit('change', path);
|
||||
},
|
||||
beforeUpload(file){
|
||||
doBeforeUpload(file){
|
||||
this.uploadGoOn=true
|
||||
var fileType = file.type;
|
||||
if(this.fileType===FILE_TYPE_IMG){
|
||||
@ -248,7 +255,10 @@
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//TODO 扩展功能验证文件大小
|
||||
// 扩展 beforeUpload 验证
|
||||
if (typeof this.beforeUpload === 'function') {
|
||||
return this.beforeUpload(file)
|
||||
}
|
||||
return true
|
||||
},
|
||||
handleChange(info) {
|
||||
|
||||
@ -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,11 @@ export default {
|
||||
// 是否异步删除行,如果你要实现异步删除,那么需要把这个选项开启,
|
||||
// 在remove事件里调用confirmRemove方法才会真正删除(除非删除的全是新增的行)
|
||||
asyncRemove: PropTypes.bool.def(false),
|
||||
// 是否一直显示组件,如果为false则只有点击的时候才出现组件
|
||||
// 注:该参数不能动态修改;如果行、列字段多的情况下,会根据机器性能造成不同程度的卡顿。
|
||||
alwaysEdit: PropTypes.bool.def(false),
|
||||
// 联动配置,数组,详情配置见文档
|
||||
linkageConfig: PropTypes.array.def(() => []),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -148,7 +153,10 @@ export default {
|
||||
// 允许执行刷新特效的行ID
|
||||
reloadEffectRowKeysMap: {},
|
||||
//配置了但是没有授权的按钮和列 集合
|
||||
excludeCode:[]
|
||||
excludeCode:[],
|
||||
// 联动下拉选项(用于隔离不同的下拉选项)
|
||||
// 内部联动配置,map
|
||||
_innerLinkageConfig: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -175,6 +183,18 @@ export default {
|
||||
renderOptions.target = this
|
||||
}
|
||||
}
|
||||
// 处理联动列,联动列只能作用于 select 组件
|
||||
if (column.$type === JVXETypes.select && this._innerLinkageConfig != null) {
|
||||
// 判断当前列是否是联动列
|
||||
if (this._innerLinkageConfig.has(column.key)) {
|
||||
renderOptions.linkage = {
|
||||
config: this._innerLinkageConfig.get(column.key),
|
||||
getLinkageOptionsSibling: this.getLinkageOptionsSibling,
|
||||
getLinkageOptionsAsync: this.getLinkageOptionsAsync,
|
||||
linkageSelectChange: this.linkageSelectChange,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (column.editRender) {
|
||||
Object.assign(column.editRender, renderOptions)
|
||||
}
|
||||
@ -275,15 +295,21 @@ export default {
|
||||
immediate: true,
|
||||
async handler() {
|
||||
let vxe = await getRefPromise(this, 'vxe')
|
||||
// 阻断vue监听大数据,提高性能
|
||||
|
||||
// 开启了排序就自动计算排序值
|
||||
if (this.dragSort) {
|
||||
this.dataSource.forEach((data, idx) => {
|
||||
this.dataSource.forEach((data, idx) => {
|
||||
// 开启了排序就自动计算排序值
|
||||
if (this.dragSort) {
|
||||
this.$set(data, this.dragSortKey, idx + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
// 处理联动回显数据
|
||||
if (this._innerLinkageConfig != null) {
|
||||
for (let configItem of this._innerLinkageConfig.values()) {
|
||||
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 阻断vue监听大数据,提高性能
|
||||
vxe.loadData(this.dataSource)
|
||||
|
||||
// TODO 解析disabledRows
|
||||
@ -354,7 +380,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
|
||||
@ -466,7 +492,40 @@ export default {
|
||||
this._innerColumns = _innerColumns
|
||||
this._innerEditRules = _innerEditRules
|
||||
}
|
||||
}
|
||||
},
|
||||
// watch linkageConfig
|
||||
// 整理多级联动配置
|
||||
linkageConfig: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (Array.isArray(this.linkageConfig) && this.linkageConfig.length > 0) {
|
||||
// 获取联动的key顺序
|
||||
let getLcKeys = (key, arr) => {
|
||||
let col = this._innerColumns.find(col => col.key === key)
|
||||
if (col) {
|
||||
arr.push(col.key)
|
||||
if (col.linkageKey) {
|
||||
return getLcKeys(col.linkageKey, arr)
|
||||
}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
let configMap = new Map()
|
||||
this.linkageConfig.forEach(lc => {
|
||||
let keys = getLcKeys(lc.key, [])
|
||||
// 多个key共享一个,引用地址
|
||||
let configItem = {
|
||||
...lc, keys,
|
||||
optionsMap: new Map()
|
||||
}
|
||||
keys.forEach(k => configMap.set(k, configItem))
|
||||
})
|
||||
this._innerLinkageConfig = configMap
|
||||
} else {
|
||||
this._innerLinkageConfig = null
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
},
|
||||
@ -665,7 +724,24 @@ export default {
|
||||
async loadNewData(dataSource) {
|
||||
if (Array.isArray(dataSource)) {
|
||||
let {xTable} = this.$refs.vxe.$refs
|
||||
return await xTable.loadData(dataSource)
|
||||
// issues/2784
|
||||
// 先清空所有数据
|
||||
xTable.loadData([])
|
||||
|
||||
dataSource.forEach((data, idx) => {
|
||||
// 开启了排序就自动计算排序值
|
||||
if (this.dragSort) {
|
||||
this.$set(data, this.dragSortKey, idx + 1)
|
||||
}
|
||||
// 处理联动回显数据
|
||||
if (this._innerLinkageConfig != null) {
|
||||
for (let configItem of this._innerLinkageConfig.values()) {
|
||||
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
|
||||
}
|
||||
}
|
||||
})
|
||||
// 再新增
|
||||
return xTable.insertAt(dataSource)
|
||||
}
|
||||
return []
|
||||
},
|
||||
@ -790,6 +866,7 @@ export default {
|
||||
* 添加一行或多行
|
||||
*
|
||||
* @param rows
|
||||
* @param isOnlJs 是否是onlineJS增强触发的
|
||||
* @return
|
||||
*/
|
||||
async addRows(rows = {}, isOnlJs) {
|
||||
@ -889,6 +966,89 @@ export default {
|
||||
this.$emit(name, event)
|
||||
},
|
||||
|
||||
/** 【多级联动】获取同级联动下拉选项 */
|
||||
getLinkageOptionsSibling(row, col, config, request) {
|
||||
// 如果当前列不是顶级列
|
||||
let key = ''
|
||||
if (col.key !== config.key) {
|
||||
// 就找出联动上级列
|
||||
let idx = config.keys.findIndex(k => col.key === k)
|
||||
let parentKey = config.keys[idx - 1]
|
||||
key = row[parentKey]
|
||||
// 如果联动上级列没有选择数据,就直接返回空数组
|
||||
if (key === '' || key == null) {
|
||||
return []
|
||||
}
|
||||
} else {
|
||||
key = 'root'
|
||||
}
|
||||
let options = config.optionsMap.get(key)
|
||||
if (!Array.isArray(options)) {
|
||||
if (request) {
|
||||
let parent = key === 'root' ? '' : key
|
||||
return this.getLinkageOptionsAsync(config, parent)
|
||||
} else {
|
||||
options = []
|
||||
}
|
||||
}
|
||||
return options
|
||||
},
|
||||
/** 【多级联动】获取联动下拉选项(异步) */
|
||||
getLinkageOptionsAsync(config, parent) {
|
||||
return new Promise(resolve => {
|
||||
let key = parent ? parent : 'root'
|
||||
let options
|
||||
if (config.optionsMap.has(key)) {
|
||||
options = config.optionsMap.get(key)
|
||||
if (options instanceof Promise) {
|
||||
options.then(opt => {
|
||||
config.optionsMap.set(key, opt)
|
||||
resolve(opt)
|
||||
})
|
||||
} else {
|
||||
resolve(options)
|
||||
}
|
||||
} else if (typeof config.requestData === 'function') {
|
||||
// 调用requestData方法,通过传入parent来获取子级
|
||||
let promise = config.requestData(parent)
|
||||
config.optionsMap.set(key, promise)
|
||||
promise.then(opt => {
|
||||
config.optionsMap.set(key, opt)
|
||||
resolve(opt)
|
||||
})
|
||||
} else {
|
||||
resolve([])
|
||||
}
|
||||
})
|
||||
},
|
||||
// 【多级联动】 用于回显数据,自动填充 optionsMap
|
||||
autoSetLinkageOptionsByData(data, parent, config, level) {
|
||||
if (level === 0) {
|
||||
this.getLinkageOptionsAsync(config, '')
|
||||
} else {
|
||||
this.getLinkageOptionsAsync(config, parent)
|
||||
}
|
||||
if (config.keys.length - 1 > level) {
|
||||
let value = data[config.keys[level]]
|
||||
if (value) {
|
||||
this.autoSetLinkageOptionsByData(data, value, config, level + 1)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 【多级联动】联动组件change时,清空下级组件
|
||||
linkageSelectChange(row, col, config, value) {
|
||||
if (col.linkageKey) {
|
||||
this.getLinkageOptionsAsync(config, value)
|
||||
let idx = config.keys.findIndex(k => k === col.key)
|
||||
let values = {}
|
||||
for (let i = idx; i < config.keys.length; i++) {
|
||||
values[config.keys[i]] = ''
|
||||
}
|
||||
// 清空后几列的数据
|
||||
this.setValues([{rowKey: row.id, values}])
|
||||
}
|
||||
},
|
||||
|
||||
/** 加载数据字典并合并到 options */
|
||||
_loadDictConcatToOptions(column) {
|
||||
initDictOptions(column.dictCode).then((res) => {
|
||||
@ -1076,11 +1236,20 @@ 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})
|
||||
}
|
||||
// update-begin--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
|
||||
if (col.$type === JVXETypes.select && this._innerLinkageConfig != null) {
|
||||
// 判断当前列是否是联动列
|
||||
if (this._innerLinkageConfig.has(col.key)) {
|
||||
let configItem = this._innerLinkageConfig.get(col.key)
|
||||
this.getLinkageOptionsAsync(configItem, '')
|
||||
}
|
||||
}
|
||||
// update-end--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
|
||||
})
|
||||
return record
|
||||
},
|
||||
|
||||
@ -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))
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
:multi="multi"
|
||||
:rootOpened="rootOpened"
|
||||
:depart-id="departIds"
|
||||
:store="storeField()"
|
||||
:text="textField()"
|
||||
@ok="handleOK"
|
||||
@initComp="initComp"/>
|
||||
<span style="display: inline-block;height:100%;padding-left:14px" v-if="departIds" >
|
||||
@ -68,7 +70,7 @@
|
||||
}
|
||||
},
|
||||
multi(){
|
||||
if(this.cellProps.multi==false){
|
||||
if(this.cellProps.multi==false || this.originColumn.multi===false){
|
||||
return false
|
||||
}else{
|
||||
return true
|
||||
@ -101,15 +103,17 @@
|
||||
handleEmpty(){
|
||||
this.handleOK('')
|
||||
},
|
||||
handleOK(rows, idstr) {
|
||||
handleOK(rows) {
|
||||
let value = ''
|
||||
if (!rows && rows.length <= 0) {
|
||||
this.departNames = ''
|
||||
this.departIds = ''
|
||||
} else {
|
||||
value = rows.map(row => row[this.customReturnField]).join(',')
|
||||
this.departNames = rows.map(row => row['departName']).join(',')
|
||||
this.departIds = idstr
|
||||
let storeField = this.storeField();
|
||||
let textField = this.textField();
|
||||
value = rows.map(row => row[storeField]).join(',')
|
||||
this.departNames = rows.map(row => row[textField]).join(',')
|
||||
this.departIds = value
|
||||
}
|
||||
this.handleChangeCommon(this.departIds)
|
||||
},
|
||||
@ -118,6 +122,34 @@
|
||||
},
|
||||
handleChange(value) {
|
||||
this.handleChangeCommon(value)
|
||||
},
|
||||
storeField(){
|
||||
if(this.originColumn){
|
||||
const str = this.originColumn.fieldExtendJson
|
||||
if(str){
|
||||
let json = JSON.parse(str)
|
||||
if(json && json.store){
|
||||
return json.store
|
||||
}
|
||||
}else if(this.originColumn.store){
|
||||
return this.originColumn.store
|
||||
}
|
||||
}
|
||||
return 'id'
|
||||
},
|
||||
textField(){
|
||||
if(this.originColumn){
|
||||
const str = this.originColumn.fieldExtendJson
|
||||
if(str){
|
||||
let json = JSON.parse(str)
|
||||
if(json && json.text){
|
||||
return json.text
|
||||
}
|
||||
}else if(this.originColumn.text){
|
||||
return this.originColumn.text
|
||||
}
|
||||
}
|
||||
return 'departName'
|
||||
}
|
||||
},
|
||||
enhanced: {
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
return this.rowIndex === 0
|
||||
},
|
||||
disabledMoveDown() {
|
||||
return this.rowIndex === (this.rows.length - 1)
|
||||
return this.rowIndex === (this.fullDataLength - 1)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@ -7,11 +7,16 @@
|
||||
v-bind="selectProps"
|
||||
style="width: 100%;"
|
||||
@blur="handleBlur"
|
||||
@change="handleChangeCommon"
|
||||
@change="handleChange"
|
||||
@search="handleSearchSelect"
|
||||
>
|
||||
|
||||
<template v-for="option of originColumn.options">
|
||||
<div v-if="loading" slot="notFoundContent">
|
||||
<a-icon type="loading" />
|
||||
<span> 加载中…</span>
|
||||
</div>
|
||||
|
||||
<template v-for="option of selectOptions">
|
||||
<a-select-option :key="option.value" :value="option.value" :disabled="option.disabled">
|
||||
<span>{{option.text || option.label || option.title|| option.value}}</span>
|
||||
</a-select-option>
|
||||
@ -23,10 +28,18 @@
|
||||
<script>
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
|
||||
import { filterDictText } from '@comp/dict/JDictSelectUtil'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data(){
|
||||
return {
|
||||
loading: false,
|
||||
// 异步加载的options(用于多级联动)
|
||||
asyncOptions: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectProps() {
|
||||
let props = {...this.cellProps}
|
||||
@ -37,6 +50,32 @@
|
||||
}
|
||||
return props
|
||||
},
|
||||
// 下拉选项
|
||||
selectOptions() {
|
||||
if (this.asyncOptions) {
|
||||
return this.asyncOptions
|
||||
}
|
||||
let {linkage} = this.renderOptions
|
||||
if (linkage) {
|
||||
let {getLinkageOptionsSibling, config} = linkage
|
||||
let res = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
|
||||
// 当返回Promise时,说明是多级联动
|
||||
if (res instanceof Promise) {
|
||||
this.loading = true
|
||||
res.then(opt => {
|
||||
this.asyncOptions = opt
|
||||
this.loading = false
|
||||
}).catch(e => {
|
||||
console.error(e)
|
||||
this.loading = false
|
||||
})
|
||||
} else {
|
||||
this.asyncOptions = null
|
||||
return res
|
||||
}
|
||||
}
|
||||
return this.originColumn.options
|
||||
},
|
||||
},
|
||||
created() {
|
||||
let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
|
||||
@ -54,6 +93,15 @@
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleChange(value) {
|
||||
// 处理下级联动
|
||||
let linkage = this.renderOptions.linkage
|
||||
if (linkage) {
|
||||
linkage.linkageSelectChange(this.row, this.originColumn, linkage.config, value)
|
||||
}
|
||||
this.handleChangeCommon(value)
|
||||
},
|
||||
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur(value) {
|
||||
let {allowInput, options} = this.originColumn
|
||||
@ -116,9 +164,32 @@
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-select'),
|
||||
editActived(event) {
|
||||
dispatchEvent.call(this, event, 'ant-select')
|
||||
},
|
||||
},
|
||||
translate: {
|
||||
enabled: true,
|
||||
async handler(value,) {
|
||||
let options
|
||||
let {linkage} = this.renderOptions
|
||||
// 判断是否是多级联动,如果是就通过接口异步翻译
|
||||
if (linkage) {
|
||||
let {getLinkageOptionsSibling, config} = linkage
|
||||
options = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
|
||||
if (options instanceof Promise) {
|
||||
return new Promise(resolve => {
|
||||
options.then(opt => {
|
||||
resolve(filterDictText(opt, value))
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
options = this.column.own.options
|
||||
}
|
||||
return filterDictText(options, value)
|
||||
},
|
||||
},
|
||||
translate: {enabled: true},
|
||||
getValue(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(',')
|
||||
|
||||
@ -23,7 +23,9 @@
|
||||
autofocus: '.ant-input',
|
||||
},
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'anticon-fullscreen'),
|
||||
editActived(event) {
|
||||
dispatchEvent.call(this, event, 'anticon-fullscreen')
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<a-time-picker
|
||||
ref="timePicker"
|
||||
:value="innerDateValue"
|
||||
allowClear
|
||||
dropdownClassName="j-vxe-date-picker"
|
||||
style="min-width: 0;"
|
||||
v-bind="cellProps"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeTimeCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerDateValue: null,
|
||||
dateFormat: 'HH:mm:ss'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val == null || val === '') {
|
||||
this.innerDateValue = null
|
||||
} else {
|
||||
this.innerDateValue = moment(val, this.dateFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(mom, dateStr) {
|
||||
this.handleChangeCommon(dateStr)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived(event) {
|
||||
dispatchEvent.call(this, event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event))
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -14,7 +14,7 @@
|
||||
<a-tooltip v-else-if="file.status === 'done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-tooltip v-else :title="file.message||'上传失败'">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
@ -118,8 +118,17 @@
|
||||
value['responseName'] = file.response[col.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[col.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
if (typeof file.response.success === 'boolean') {
|
||||
if (file.response.success) {
|
||||
value['path'] = file.response[col.responseName]
|
||||
} else {
|
||||
value['status'] = 'error'
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
} else {
|
||||
// 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
|
||||
value['path'] = file.response[col.responseName]
|
||||
}
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
|
||||
@ -16,6 +16,8 @@
|
||||
:multi="multi"
|
||||
:user-ids="userIds"
|
||||
@ok="selectOK"
|
||||
:store="storeField"
|
||||
:text="textField"
|
||||
@initComp="initComp"/>
|
||||
<span style="display: inline-block;height:100%;padding-left:14px" v-if="userIds" >
|
||||
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ userNames }}</span>
|
||||
@ -77,6 +79,30 @@
|
||||
}else{
|
||||
return true
|
||||
}
|
||||
},
|
||||
storeField(){
|
||||
if(this.originColumn){
|
||||
const str = this.originColumn.fieldExtendJson
|
||||
if(str){
|
||||
let json = JSON.parse(str)
|
||||
if(json && json.store){
|
||||
return json.store
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'username'
|
||||
},
|
||||
textField(){
|
||||
if(this.originColumn){
|
||||
const str = this.originColumn.fieldExtendJson
|
||||
if(str){
|
||||
let json = JSON.parse(str)
|
||||
if(json && json.text){
|
||||
return json.text
|
||||
}
|
||||
}
|
||||
}
|
||||
return 'realname'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@ -102,12 +128,8 @@
|
||||
this.userNames = ''
|
||||
this.userIds = ''
|
||||
} else {
|
||||
let temp = ''
|
||||
for (let item of rows) {
|
||||
temp += ',' + item.realname
|
||||
}
|
||||
this.userNames = temp.substring(1)
|
||||
this.userIds = idstr
|
||||
this.userIds = rows.map(row => row[this.storeField]).join(',')
|
||||
this.userNames = rows.map(row => row[this.textField]).join(',')
|
||||
}
|
||||
this.handleChangeCommon(this.userIds)
|
||||
},
|
||||
|
||||
@ -6,6 +6,7 @@ import JVxeSlotCell from './components/cells/JVxeSlotCell'
|
||||
import JVxeNormalCell from './components/cells/JVxeNormalCell'
|
||||
import JVxeInputCell from './components/cells/JVxeInputCell'
|
||||
import JVxeDateCell from './components/cells/JVxeDateCell'
|
||||
import JVxeTimeCell from './components/cells/JVxeTimeCell'
|
||||
import JVxeSelectCell from './components/cells/JVxeSelectCell'
|
||||
import JVxeCheckboxCell from './components/cells/JVxeCheckboxCell'
|
||||
import JVxeUploadCell from './components/cells/JVxeUploadCell'
|
||||
@ -32,6 +33,7 @@ export const AllCells = {
|
||||
...mapCell(JVXETypes.selectMultiple, JVxeSelectCell), // 下拉多选
|
||||
...mapCell(JVXETypes.date, JVxeDateCell),
|
||||
...mapCell(JVXETypes.datetime, JVxeDateCell),
|
||||
...mapCell(JVXETypes.time, JVxeTimeCell),
|
||||
...mapCell(JVXETypes.upload, JVxeUploadCell),
|
||||
...mapCell(JVXETypes.textarea, JVxeTextareaCell),
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ export const JVXETypes = {
|
||||
select: 'select',
|
||||
date: 'date',
|
||||
datetime: 'datetime',
|
||||
time: 'time',
|
||||
checkbox: 'checkbox',
|
||||
upload: 'upload',
|
||||
// 下拉搜索
|
||||
|
||||
@ -36,6 +36,9 @@ export default {
|
||||
rows() {
|
||||
return this.params.data
|
||||
},
|
||||
fullDataLength() {
|
||||
return this.params.$table.tableFullData.length
|
||||
},
|
||||
rowIndex() {
|
||||
return this.params.rowIndex
|
||||
},
|
||||
@ -70,6 +73,16 @@ export default {
|
||||
props['disabled'] = true
|
||||
}
|
||||
|
||||
// update-begin-author:taoyan date:20211011 for: online表单,附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
|
||||
let jsonStr = col['fieldExtendJson']
|
||||
if(jsonStr){
|
||||
let fieldExtendJson = JSON.parse(jsonStr)
|
||||
if(fieldExtendJson && fieldExtendJson['multiSelect']==false){
|
||||
props['multi'] = false
|
||||
}
|
||||
}
|
||||
// update-end-author:taoyan date:20211011 for: online表单,附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
|
||||
|
||||
return props
|
||||
},
|
||||
},
|
||||
@ -99,7 +112,13 @@ export default {
|
||||
|
||||
// 判断是否启用翻译
|
||||
if (this.renderType === JVXERenderType.spaner && this.enhanced.translate.enabled) {
|
||||
this.innerValue = this.enhanced.translate.handler.call(this, value)
|
||||
let res = this.enhanced.translate.handler.call(this, value)
|
||||
// 异步翻译,目前仅【多级联动】使用
|
||||
if (res instanceof Promise) {
|
||||
res.then(value => this.innerValue = value)
|
||||
} else {
|
||||
this.innerValue = res
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -291,6 +310,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,7 +321,9 @@ export function dispatchEvent({cell, $event}, className, handler) {
|
||||
handler(element[0])
|
||||
} else {
|
||||
// 模拟触发点击事件
|
||||
element[0].dispatchEvent($event)
|
||||
if($event){
|
||||
element[0].dispatchEvent($event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 10)
|
||||
|
||||
@ -141,6 +141,33 @@ export async function validateFormAndTables(form, cases, autoJumpTab) {
|
||||
return dataMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性验证主表单和所有的次表单
|
||||
* @param form 主表单 form 对象
|
||||
* @param cases 接收一个数组,每项都是一个JVxeTable实例
|
||||
* @param autoJumpTab
|
||||
* @returns {Promise<any>}
|
||||
* @author sunjianlei
|
||||
*/
|
||||
export async function validateFormModelAndTables(form,formData, cases, autoJumpTab) {
|
||||
if (!(form && typeof form.validate === 'function')) {
|
||||
throw `form 参数需要的是一个form对象,而传入的却是${typeof form}`
|
||||
}
|
||||
let dataMap = {}
|
||||
let values = await new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
form.validate((valid,obj) => {
|
||||
valid ?resolve(formData): reject({error: VALIDATE_FAILED, originError: valid})
|
||||
})
|
||||
})
|
||||
Object.assign(dataMap, {formValue: values})
|
||||
// 验证所有子表的表单
|
||||
let subData = await validateTables(cases, autoJumpTab)
|
||||
// 合并最终数据
|
||||
dataMap = Object.assign(dataMap, {tablesValue: subData})
|
||||
return dataMap
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证并获取一个或多个表格的所有值
|
||||
*
|
||||
|
||||
@ -26,18 +26,24 @@ 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'
|
||||
import JSelectMultiUser from '../jeecgbiz/JSelectMultiUser.vue'
|
||||
import JSelectPosition from '../jeecgbiz/JSelectPosition.vue'
|
||||
import JSelectRole from '../jeecgbiz/JSelectRole.vue'
|
||||
import JSelectUserByDep from '../jeecgbiz/JSelectUserByDep.vue'
|
||||
//引入需要全局注册的js函数和变量
|
||||
import { Modal, notification,message } from 'ant-design-vue'
|
||||
import lodash_object from 'lodash'
|
||||
import debounce from 'lodash/debounce'
|
||||
import pick from 'lodash.pick'
|
||||
import data from 'china-area-data'
|
||||
|
||||
export default {
|
||||
install(Vue) {
|
||||
Vue.use(JModal)
|
||||
Vue.component('JMarkdownEditor', JMarkdownEditor)
|
||||
Vue.component(JModal.name, JModal)
|
||||
Vue.component('JPopupOnlReport', JPopupOnlReport)
|
||||
Vue.component('JFilePop', JFilePop)
|
||||
Vue.component('JInputPop', JInputPop)
|
||||
@ -71,5 +77,15 @@ export default {
|
||||
Vue.component('JSelectPosition', JSelectPosition)
|
||||
Vue.component('JSelectRole', JSelectRole)
|
||||
Vue.component('JSelectUserByDep', JSelectUserByDep)
|
||||
Vue.component(JEasyCron.name, JEasyCron)
|
||||
|
||||
//注册全局js函数和变量
|
||||
Vue.prototype.$Jnotification = notification
|
||||
Vue.prototype.$Jmodal = Modal
|
||||
Vue.prototype.$Jmessage = message
|
||||
Vue.prototype.$Jlodash = lodash_object
|
||||
Vue.prototype.$Jdebounce= debounce
|
||||
Vue.prototype.$Jpick = pick
|
||||
Vue.prototype.$Jpcaa = data
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@
|
||||
<a-icon type="close" @click="visible=false"/>
|
||||
</span>
|
||||
</div>
|
||||
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange">
|
||||
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange" :placeholder="placeholder">
|
||||
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
|
||||
</a-input>
|
||||
<div slot="content">
|
||||
@ -53,6 +53,10 @@
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
placeholder:{
|
||||
type:String,
|
||||
required:false
|
||||
}
|
||||
|
||||
},
|
||||
data(){
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-modal
|
||||
title="corn表达式"
|
||||
title="cron表达式"
|
||||
:width="modalWidth"
|
||||
:visible="visible"
|
||||
:confirmLoading="confirmLoading"
|
||||
|
||||
@ -74,11 +74,12 @@
|
||||
import {filterObj} from '@/utils/util'
|
||||
import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
|
||||
import { httpGroupRequest } from '@/api/GroupRequest.js'
|
||||
import md5 from 'md5'
|
||||
|
||||
const MODAL_WIDTH = 1200;
|
||||
export default {
|
||||
name: 'JPopupOnlReport',
|
||||
props: ['multi', 'code', 'groupId', 'param'],
|
||||
props: ['multi', 'code', 'sorter', 'groupId', 'param'],
|
||||
components:{
|
||||
},
|
||||
data(){
|
||||
@ -122,8 +123,9 @@
|
||||
cgRpConfigId:"",
|
||||
modalWidth:MODAL_WIDTH,
|
||||
tableScroll:{x:true},
|
||||
dynamicParam:{}
|
||||
|
||||
dynamicParam:{},
|
||||
// 排序字段,默认无排序
|
||||
iSorter: null,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@ -136,10 +138,35 @@
|
||||
param:{
|
||||
deep:true,
|
||||
handler(){
|
||||
this.dynamicParamHandler()
|
||||
this.loadData();
|
||||
// update--begin--autor:liusq-----date:20210706------for:JPopup组件在modal中使用报错#2729------
|
||||
if(this.visible){
|
||||
this.dynamicParamHandler()
|
||||
this.loadData();
|
||||
}
|
||||
// update--begin--autor:liusq-----date:20210706------for:JPopup组件在modal中使用报错#2729------
|
||||
},
|
||||
}
|
||||
},
|
||||
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 +194,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 +284,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 +319,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()
|
||||
@ -342,11 +383,20 @@
|
||||
combineRowKey(record){
|
||||
let res = ''
|
||||
Object.keys(record).forEach(key=>{
|
||||
res+=record[key]
|
||||
//update-begin---author:liusq Date:20210203 for:pop选择器列主键问题 issues/I29P9Q------------
|
||||
if(key=='id'){
|
||||
res=record[key]+res
|
||||
}else{
|
||||
res+=record[key]
|
||||
}
|
||||
//update-end---author:liusq Date:20210203 for:pop选择器列主键问题 issues/I29P9Q------------
|
||||
})
|
||||
if(res.length>50){
|
||||
// update-begin---author:taoyan Date:20211025 for:jpopup 表格key重复BUG /issues/3121
|
||||
res = md5(res)
|
||||
/*if(res.length>50){
|
||||
res = res.substring(0,50)
|
||||
}
|
||||
}*/
|
||||
// update-end---author:taoyan Date:20211025 for:jpopup 表格key重复BUG /issues/3121
|
||||
return res
|
||||
},
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="components-input-demo-presuffix">
|
||||
<!---->
|
||||
<a-input @click="openModal" placeholder="请点击选择部门" v-model="departNames" readOnly :disabled="disabled">
|
||||
<a-input @click="openModal" placeholder="请点击选择部门" v-model="textVals" readOnly :disabled="disabled">
|
||||
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
|
||||
<a-icon v-if="departIds" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||
<a-icon v-if="storeVals" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||
</a-input>
|
||||
|
||||
<j-select-depart-modal
|
||||
@ -11,7 +11,10 @@
|
||||
:modal-width="modalWidth"
|
||||
:multi="multi"
|
||||
:rootOpened="rootOpened"
|
||||
:depart-id="departIds"
|
||||
:depart-id="value"
|
||||
:store="storeField"
|
||||
:text="textField"
|
||||
:treeOpera="treeOpera"
|
||||
@ok="handleOK"
|
||||
@initComp="initComp"/>
|
||||
</div>
|
||||
@ -19,6 +22,7 @@
|
||||
|
||||
<script>
|
||||
import JSelectDepartModal from './modal/JSelectDepartModal'
|
||||
import { underLinetoHump } from '@/components/_util/StringUtil'
|
||||
export default {
|
||||
name: 'JSelectDepart',
|
||||
components:{
|
||||
@ -52,57 +56,70 @@
|
||||
// 自定义返回字段,默认返回 id
|
||||
customReturnField: {
|
||||
type: String,
|
||||
default: 'id'
|
||||
default: ''
|
||||
},
|
||||
backDepart: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 存储字段 [key field]
|
||||
store: {
|
||||
type: String,
|
||||
default: 'id',
|
||||
required: false
|
||||
},
|
||||
// 显示字段 [label field]
|
||||
text: {
|
||||
type: String,
|
||||
default: 'departName',
|
||||
required: false
|
||||
},
|
||||
treeOpera: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
visible:false,
|
||||
confirmLoading:false,
|
||||
departNames:"",
|
||||
departIds:''
|
||||
storeVals: '', //[key values]
|
||||
textVals: '' //[label values]
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
storeField(){
|
||||
let field = this.customReturnField
|
||||
if(!field){
|
||||
field = this.store;
|
||||
}
|
||||
return underLinetoHump(field)
|
||||
},
|
||||
textField(){
|
||||
return underLinetoHump(this.text)
|
||||
}
|
||||
},
|
||||
mounted(){
|
||||
this.departIds = this.value
|
||||
this.storeVals = this.value
|
||||
},
|
||||
watch:{
|
||||
value(val){
|
||||
//update-begin-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2
|
||||
// if (this.customReturnField === 'id') {
|
||||
this.departIds = val
|
||||
// }
|
||||
//update-end-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2
|
||||
this.storeVals = val
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
initComp(departNames){
|
||||
this.departNames = departNames
|
||||
//update-begin-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
|
||||
//TODO 当返回字段为部门名称时会有问题,因为部门名称不唯一
|
||||
//返回字段不为id时,根据返回字段获取id
|
||||
if(this.customReturnField !== 'id' && this.value){
|
||||
const dataList = this.$refs.innerDepartSelectModal.dataList;
|
||||
console.log('this.value',this.value)
|
||||
this.departIds = this.value.split(',').map(item => {
|
||||
const data = dataList.filter(d=>d[this.customReturnField] === item)
|
||||
return data.length > 0 ? data[0].id : ''
|
||||
}).join(',')
|
||||
}
|
||||
//update-end-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
|
||||
this.backDeparInfo()
|
||||
initComp(textVals){
|
||||
this.textVals = textVals
|
||||
},
|
||||
//返回选中的部门信息
|
||||
backDeparInfo(){
|
||||
if(this.backDepart===true){
|
||||
if(this.departIds && this.departIds.length>0){
|
||||
let arr1 = this.departIds.split(',')
|
||||
let arr2 = this.departNames.split(',')
|
||||
let arr1 = this.storeVals.split(',')
|
||||
let arr2 = this.textVals.split(',')
|
||||
let info = []
|
||||
for(let i=0;i<arr1.length;i++){
|
||||
info.push({
|
||||
@ -117,17 +134,22 @@
|
||||
openModal(){
|
||||
this.$refs.innerDepartSelectModal.show()
|
||||
},
|
||||
handleOK(rows, idstr) {
|
||||
let value = ''
|
||||
handleOK(rows) {
|
||||
if (!rows && rows.length <= 0) {
|
||||
this.departNames = ''
|
||||
this.departIds = ''
|
||||
this.textVals = ''
|
||||
this.storeVals = ''
|
||||
} else {
|
||||
value = rows.map(row => row[this.customReturnField]).join(',')
|
||||
this.departNames = rows.map(row => row['departName']).join(',')
|
||||
this.departIds = idstr
|
||||
let arr1 = []
|
||||
let arr2 = []
|
||||
for(let dep of rows){
|
||||
arr1.push(dep[this.storeField])
|
||||
arr2.push(dep[this.textField])
|
||||
}
|
||||
this.storeVals = arr1.join(',')
|
||||
this.textVals = arr2.join(',')
|
||||
}
|
||||
this.$emit("change", value)
|
||||
this.$emit("change", this.storeVals)
|
||||
this.backDeparInfo()
|
||||
},
|
||||
getDepartNames(){
|
||||
return this.departNames
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-input-search
|
||||
v-model="userNames"
|
||||
v-model="textVals"
|
||||
placeholder="请先选择用户"
|
||||
readOnly
|
||||
unselectable="on"
|
||||
@search="onSearchDepUser">
|
||||
<a-button slot="enterButton" :disabled="disabled">选择用户</a-button>
|
||||
</a-input-search>
|
||||
<j-select-user-by-dep-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" @ok="selectOK" :user-ids="value" @initComp="initComp"/>
|
||||
<j-select-user-by-dep-modal
|
||||
ref="selectModal"
|
||||
:modal-width="modalWidth"
|
||||
:multi="multi"
|
||||
@ok="selectOK"
|
||||
:user-ids="value"
|
||||
:store="storeField"
|
||||
:text="textField"
|
||||
@initComp="initComp"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
|
||||
import { underLinetoHump } from '@/components/_util/StringUtil'
|
||||
|
||||
export default {
|
||||
name: 'JSelectUserByDep',
|
||||
@ -42,20 +51,44 @@
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 存储字段 [key field]
|
||||
store: {
|
||||
type: String,
|
||||
default: 'username',
|
||||
required: false
|
||||
},
|
||||
// 显示字段 [label field]
|
||||
text: {
|
||||
type: String,
|
||||
default: 'realname',
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
userIds: "",
|
||||
userNames: ""
|
||||
storeVals: '', //[key values]
|
||||
textVals: '' //[label values]
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
storeField(){
|
||||
let field = this.customReturnField
|
||||
if(!field){
|
||||
field = this.store;
|
||||
}
|
||||
return underLinetoHump(field)
|
||||
},
|
||||
textField(){
|
||||
return underLinetoHump(this.text)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.userIds = this.value
|
||||
this.storeVals = this.value
|
||||
},
|
||||
watch: {
|
||||
value(val) {
|
||||
this.userIds = val
|
||||
this.storeVals = val
|
||||
}
|
||||
},
|
||||
model: {
|
||||
@ -63,15 +96,15 @@
|
||||
event: 'change'
|
||||
},
|
||||
methods: {
|
||||
initComp(userNames) {
|
||||
this.userNames = userNames
|
||||
initComp(textVals) {
|
||||
this.textVals = textVals
|
||||
},
|
||||
//返回选中的用户信息
|
||||
backDeparInfo(){
|
||||
if(this.backUser===true){
|
||||
if(this.userIds && this.userIds.length>0){
|
||||
let arr1 = this.userIds.split(',')
|
||||
let arr2 = this.userNames.split(',')
|
||||
if(this.storeVals && this.storeVals.length>0){
|
||||
let arr1 = this.storeVals.split(',')
|
||||
let arr2 = this.textVals.split(',')
|
||||
let info = []
|
||||
for(let i=0;i<arr1.length;i++){
|
||||
info.push({
|
||||
@ -86,21 +119,22 @@
|
||||
onSearchDepUser() {
|
||||
this.$refs.selectModal.showModal()
|
||||
},
|
||||
selectOK(rows, idstr) {
|
||||
selectOK(rows) {
|
||||
console.log("当前选中用户", rows)
|
||||
console.log("当前选中用户ID", idstr)
|
||||
if (!rows) {
|
||||
this.userNames = ''
|
||||
this.userIds = ''
|
||||
this.storeVals = ''
|
||||
this.textVals = ''
|
||||
} else {
|
||||
let temp = ''
|
||||
let temp1 = []
|
||||
let temp2 = []
|
||||
for (let item of rows) {
|
||||
temp += ',' + item.realname
|
||||
temp1.push(item[this.storeField])
|
||||
temp2.push(item[this.textField])
|
||||
}
|
||||
this.userNames = temp.substring(1)
|
||||
this.userIds = idstr
|
||||
this.storeVals = temp1.join(',')
|
||||
this.textVals = temp2.join(',')
|
||||
}
|
||||
this.$emit("change", this.userIds)
|
||||
this.$emit("change", this.storeVals)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleSubmit"
|
||||
@cancel="handleCancel"
|
||||
@update:fullscreen="isFullscreen"
|
||||
wrapClassName="j-depart-select-modal"
|
||||
switchFullscreen
|
||||
cancelText="关闭">
|
||||
@ -13,9 +14,9 @@
|
||||
<a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" />
|
||||
<a-tree
|
||||
checkable
|
||||
class="my-dept-select-tree"
|
||||
:class="treeScreenClass"
|
||||
:treeData="treeData"
|
||||
:checkStrictly="true"
|
||||
:checkStrictly="checkStrictly"
|
||||
@check="onCheck"
|
||||
@select="onSelect"
|
||||
@expand="onExpand"
|
||||
@ -32,8 +33,23 @@
|
||||
<span v-else>{{title}}</span>
|
||||
</template>
|
||||
</a-tree>
|
||||
|
||||
</a-spin>
|
||||
<!--底部父子关联操作和确认取消按钮-->
|
||||
<template slot="footer" v-if="treeOpera && multi">
|
||||
<div class="drawer-bootom-button">
|
||||
<a-dropdown style="float: left" :trigger="['click']" placement="topCenter">
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="1" @click="switchCheckStrictly(1)">父子关联</a-menu-item>
|
||||
<a-menu-item key="2" @click="switchCheckStrictly(2)">取消关联</a-menu-item>
|
||||
</a-menu>
|
||||
<a-button>
|
||||
树操作 <a-icon type="up" />
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<a-button @click="handleCancel" type="primary" style="margin-right: 0.8rem">关闭</a-button>
|
||||
<a-button @click="handleSubmit" type="primary" >确认</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</j-modal>
|
||||
</template>
|
||||
|
||||
@ -41,7 +57,7 @@
|
||||
import { queryDepartTreeList } from '@/api/api'
|
||||
export default {
|
||||
name: 'JSelectDepartModal',
|
||||
props:['modalWidth','multi','rootOpened','departId'],
|
||||
props:['modalWidth','multi','rootOpened','departId', 'store', 'text','treeOpera'],
|
||||
data(){
|
||||
return {
|
||||
visible:false,
|
||||
@ -52,7 +68,9 @@
|
||||
dataList:[],
|
||||
checkedKeys:[],
|
||||
checkedRows:[],
|
||||
searchValue:""
|
||||
searchValue:"",
|
||||
checkStrictly: true,
|
||||
fullscreen:false
|
||||
}
|
||||
},
|
||||
created(){
|
||||
@ -64,15 +82,18 @@
|
||||
},
|
||||
visible: {
|
||||
handler() {
|
||||
if (this.departId) {
|
||||
this.checkedKeys = this.departId.split(",");
|
||||
// console.log('this.departId', this.departId)
|
||||
} else {
|
||||
this.checkedKeys = [];
|
||||
}
|
||||
this.initDepartComponent(true)
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
treeScreenClass() {
|
||||
return {
|
||||
'my-dept-select-tree': true,
|
||||
'fullscreen': this.fullscreen,
|
||||
}
|
||||
},
|
||||
},
|
||||
methods:{
|
||||
show(){
|
||||
this.visible=true
|
||||
@ -80,6 +101,7 @@
|
||||
this.checkedKeys=[]
|
||||
},
|
||||
loadDepart(){
|
||||
// 这个方法是找到所有的部门信息
|
||||
queryDepartTreeList().then(res=>{
|
||||
if(res.success){
|
||||
let arr = [...res.result]
|
||||
@ -92,20 +114,23 @@
|
||||
}
|
||||
})
|
||||
},
|
||||
initDepartComponent(){
|
||||
let names = ''
|
||||
initDepartComponent(flag){
|
||||
let arr = []
|
||||
//该方法两个地方用 1.visible改变事件重新设置选中项 2.组件编辑页面回显
|
||||
let fieldName = flag==true?'key':this.text
|
||||
if(this.departId){
|
||||
let currDepartId = this.departId
|
||||
let arr2 = this.departId.split(',')
|
||||
for(let item of this.dataList){
|
||||
if(currDepartId.indexOf(item.key)>=0){
|
||||
names+=","+item.title
|
||||
if(arr2.indexOf(item[this.store])>=0){
|
||||
arr.push(item[fieldName])
|
||||
}
|
||||
}
|
||||
if(names){
|
||||
names = names.substring(1)
|
||||
}
|
||||
}
|
||||
this.$emit("initComp",names)
|
||||
if(flag==true){
|
||||
this.checkedKeys = [...arr]
|
||||
}else{
|
||||
this.$emit("initComp", arr.join(','))
|
||||
}
|
||||
},
|
||||
reWriterWithSlot(arr){
|
||||
for(let item of arr){
|
||||
@ -129,8 +154,11 @@
|
||||
}
|
||||
}
|
||||
this.expandedKeys=[...keys]
|
||||
//全部keys
|
||||
//this.allTreeKeys = [...keys]
|
||||
}else{
|
||||
this.expandedKeys=[]
|
||||
//this.allTreeKeys = []
|
||||
}
|
||||
},
|
||||
onCheck (checkedKeys,info) {
|
||||
@ -139,25 +167,32 @@
|
||||
this.checkedKeys = [...arr]
|
||||
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
|
||||
}else{
|
||||
this.checkedKeys = checkedKeys.checked
|
||||
if(this.checkStrictly){
|
||||
this.checkedKeys = checkedKeys.checked
|
||||
}else{
|
||||
this.checkedKeys = checkedKeys
|
||||
}
|
||||
this.checkedRows = this.getCheckedRows(this.checkedKeys)
|
||||
}
|
||||
},
|
||||
onSelect(selectedKeys,info) {
|
||||
let keys = []
|
||||
keys.push(selectedKeys[0])
|
||||
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
|
||||
this.checkedKeys = [...keys]
|
||||
this.checkedRows=[info.node.dataRef]
|
||||
}else{
|
||||
let currKey = info.node.dataRef.key
|
||||
if(this.checkedKeys.indexOf(currKey)>=0){
|
||||
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
|
||||
//取消关联的情况下才走onSelect的逻辑
|
||||
if(this.checkStrictly){
|
||||
let keys = []
|
||||
keys.push(selectedKeys[0])
|
||||
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
|
||||
this.checkedKeys = [...keys]
|
||||
this.checkedRows=[info.node.dataRef]
|
||||
}else{
|
||||
this.checkedKeys.push(...keys)
|
||||
let currKey = info.node.dataRef.key
|
||||
if(this.checkedKeys.indexOf(currKey)>=0){
|
||||
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
|
||||
}else{
|
||||
this.checkedKeys.push(...keys)
|
||||
}
|
||||
}
|
||||
this.checkedRows = this.getCheckedRows(this.checkedKeys)
|
||||
}
|
||||
this.checkedRows = this.getCheckedRows(this.checkedKeys)
|
||||
},
|
||||
onExpand (expandedKeys) {
|
||||
this.expandedKeys = expandedKeys
|
||||
@ -167,7 +202,9 @@
|
||||
if(!this.checkedKeys || this.checkedKeys.length==0){
|
||||
this.$emit("ok",'')
|
||||
}else{
|
||||
this.$emit("ok",this.checkedRows,this.checkedKeys.join(","))
|
||||
let checkRow = this.getCheckedRows(this.checkedKeys)
|
||||
let keyStr = this.checkedKeys.join(",")
|
||||
this.$emit("ok", checkRow, keyStr)
|
||||
}
|
||||
this.handleClear()
|
||||
},
|
||||
@ -233,6 +270,16 @@
|
||||
}
|
||||
}
|
||||
return rows
|
||||
},
|
||||
switchCheckStrictly (v) {
|
||||
if(v==1){
|
||||
this.checkStrictly = false
|
||||
}else if(v==2){
|
||||
this.checkStrictly = true
|
||||
}
|
||||
},
|
||||
isFullscreen(val){
|
||||
this.fullscreen=val
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,8 +289,22 @@
|
||||
<style lang="less" scoped>
|
||||
// 限制部门选择树高度,避免部门太多时点击确定不便
|
||||
.my-dept-select-tree{
|
||||
height: 350px;
|
||||
height:350px;
|
||||
|
||||
&.fullscreen{
|
||||
height: calc(100vh - 250px);
|
||||
}
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.drawer-bootom-button {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
border-top: 1px solid #e8e8e8;
|
||||
padding: 10px 16px;
|
||||
text-align: right;
|
||||
left: 0;
|
||||
background: #fff;
|
||||
border-radius: 0 0 2px 2px;
|
||||
}
|
||||
</style>
|
||||
@ -56,13 +56,14 @@
|
||||
</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',
|
||||
components: {},
|
||||
props: ['modalWidth', 'multi', 'userIds'],
|
||||
props: ['modalWidth', 'multi', 'userIds', 'store', 'text'],
|
||||
data() {
|
||||
return {
|
||||
queryParam: {
|
||||
@ -106,6 +107,7 @@
|
||||
],
|
||||
scrollTrigger: {},
|
||||
dataSource: [],
|
||||
selectionRows: [],
|
||||
selectedRowKeys: [],
|
||||
selectUserRows: [],
|
||||
selectUserIds: [],
|
||||
@ -157,45 +159,43 @@
|
||||
if (this.userIds) {
|
||||
// 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
|
||||
let values = this.userIds.split(',') + ','
|
||||
getUserList({
|
||||
username: values,
|
||||
pageNo: 1,
|
||||
pageSize: values.length
|
||||
}).then((res) => {
|
||||
if (res.success) {
|
||||
let selectedRowKeys = []
|
||||
let realNames = []
|
||||
res.result.records.forEach(user => {
|
||||
realNames.push(user['realname'])
|
||||
let param = {[this.store]: values}
|
||||
getAction('/sys/user/getMultiUser', param).then((list)=>{
|
||||
this.selectionRows = []
|
||||
let selectedRowKeys = []
|
||||
let textArray = []
|
||||
if(list && list.length>0){
|
||||
for(let user of list){
|
||||
textArray.push(user[this.text])
|
||||
selectedRowKeys.push(user['id'])
|
||||
})
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.$emit('initComp', realNames.join(','))
|
||||
this.selectionRows.push(user)
|
||||
}
|
||||
}
|
||||
this.selectedRowKeys = selectedRowKeys
|
||||
this.$emit('initComp', textArray.join(','))
|
||||
})
|
||||
|
||||
} else {
|
||||
// JSelectUserByDep组件bug issues/I16634
|
||||
this.$emit('initComp', '')
|
||||
// 前端用户选择单选无法置空的问题 #2610
|
||||
this.selectedRowKeys = []
|
||||
}
|
||||
},
|
||||
async loadData(arg) {
|
||||
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);
|
||||
@ -253,35 +254,32 @@
|
||||
handleSubmit() {
|
||||
let that = this;
|
||||
this.getSelectUserRows();
|
||||
that.$emit('ok', that.selectUserRows, that.selectUserIds);
|
||||
that.$emit('ok', that.selectUserRows);
|
||||
that.searchReset(0)
|
||||
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);
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -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 */
|
||||
|
||||
@ -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
|
||||
@ -171,7 +165,7 @@
|
||||
|
||||
// update-begin-author:sunjianlei date:20200120 for: 动态更改页面标题
|
||||
changeTitle(title) {
|
||||
let projectTitle = "Jeecg-Boot 企业级快速开发平台"
|
||||
let projectTitle = "Jeecg-Boot 企业级低代码平台"
|
||||
// 首页特殊处理
|
||||
if (this.$route.path === indexKey) {
|
||||
document.title = projectTitle
|
||||
@ -225,10 +219,20 @@
|
||||
cacheRouterArray.splice(cacheRouterArray.findIndex(item => item === componentName), 1)
|
||||
Vue.ls.set(CACHE_INCLUDED_ROUTES, cacheRouterArray)
|
||||
}
|
||||
this.emitPageClosed(removeRoute[0])
|
||||
}
|
||||
//update-end--Author:scott Date:20201015 for:路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||
|
||||
},
|
||||
// 触发 page-closed (页面关闭)全局事件
|
||||
emitPageClosed(closedRoute) {
|
||||
this.$root.$emit('page-closed', {
|
||||
closedRoute,
|
||||
pageList: this.pageList,
|
||||
linkList: this.linkList,
|
||||
activePage: this.activePage
|
||||
})
|
||||
},
|
||||
onContextmenu(e) {
|
||||
const pagekey = this.getPageKey(e.target)
|
||||
if (pagekey !== null) {
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="desc">
|
||||
Jeecg Boot 是中国最具影响力的 企业级 快速开发平台
|
||||
Jeecg Boot 是中国最具影响力的 企业级 低代码平台
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
|
||||
@click="toggle"/>
|
||||
|
||||
<span v-if="device === 'desktop'">欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
|
||||
<span v-if="device === 'desktop'">欢迎进入 Jeecg-Boot 企业级低代码平台</span>
|
||||
<span v-else>Jeecg-Boot</span>
|
||||
|
||||
<user-menu :theme="theme"/>
|
||||
@ -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
|
||||
@ -50,7 +52,6 @@
|
||||
import UserMenu from '../tools/UserMenu'
|
||||
import SMenu from '../menu/'
|
||||
import Logo from '../tools/Logo'
|
||||
|
||||
import { mixin } from '@/utils/mixin.js'
|
||||
|
||||
export default {
|
||||
@ -156,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>
|
||||
|
||||
@ -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: 修复动态功能测试菜单、带参数菜单标题错误、展开错误的问题
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="logo">
|
||||
<router-link :to="{name:'dashboard'}">
|
||||
<router-link :to="routerLinkTo">
|
||||
|
||||
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
|
||||
<img v-if="navTheme === 'dark'" src="~@/assets/logo-white.png" alt="logo">
|
||||
@ -28,7 +28,12 @@
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
// 点击Logo跳转地址
|
||||
routerLinkTo: {
|
||||
type: Object,
|
||||
default: () => ({name: 'dashboard'}),
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -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: 解决外部链接打开失败的问题
|
||||
|
||||
@ -347,6 +347,21 @@ export const constantRouterMap = [
|
||||
// ]
|
||||
// },
|
||||
|
||||
{
|
||||
// OAuth2 APP页面路由
|
||||
path: '/oauth2-app',
|
||||
component: BlankLayout,
|
||||
redirect: '/oauth2-app/login',
|
||||
children: [
|
||||
{
|
||||
// OAuth2 登录路由
|
||||
path: 'login',
|
||||
name: 'login',
|
||||
component: () => import(/* webpackChunkName: "oauth2-app.login" */ '@/views/user/oauth2/OAuth2Login')
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
path: '/test',
|
||||
component: BlankLayout,
|
||||
|
||||
@ -47,7 +47,9 @@ import '@/assets/less/JAreaLinkage.less'
|
||||
import VueAreaLinkage from 'vue-area-linkage'
|
||||
import '@/components/jeecg/JVxeTable/install'
|
||||
import '@/components/JVxeCells/install'
|
||||
|
||||
//表单验证
|
||||
import { rules } from '@/utils/rules'
|
||||
Vue.prototype.rules = rules
|
||||
Vue.config.productionTip = false
|
||||
Vue.use(Storage, config.storageOptions)
|
||||
Vue.use(Antd)
|
||||
|
||||
@ -53,6 +53,7 @@ export const JEditableTableMixin = {
|
||||
this.tableReset();
|
||||
resolve();
|
||||
}).then(() => {
|
||||
if (typeof this.addBefore === 'function') this.addBefore()
|
||||
// 默认新增空数据
|
||||
let rowNum = this.addDefaultRowNum
|
||||
if (typeof rowNum !== 'number') {
|
||||
|
||||
191
ant-design-vue-jeecg/src/mixins/JEditableTableModelMixin.js
Normal file
191
ant-design-vue-jeecg/src/mixins/JEditableTableModelMixin.js
Normal file
@ -0,0 +1,191 @@
|
||||
import JEditableTable from '@/components/jeecg/JEditableTable'
|
||||
import { VALIDATE_NO_PASSED, getRefPromise,validateFormModelAndTables} from '@/utils/JEditableTableUtil'
|
||||
import { httpAction, getAction } from '@/api/manage'
|
||||
|
||||
export const JEditableTableModelMixin = {
|
||||
components: {
|
||||
JEditableTable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
title: '操作',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
model:{},
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 6 }
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 }
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 获取所有的editableTable实例 */
|
||||
getAllTable() {
|
||||
if (!(this.refKeys instanceof Array)) {
|
||||
throw this.throwNotArray('refKeys')
|
||||
}
|
||||
let values = this.refKeys.map(key => getRefPromise(this, key))
|
||||
return Promise.all(values)
|
||||
},
|
||||
|
||||
/** 遍历所有的JEditableTable实例 */
|
||||
eachAllTable(callback) {
|
||||
// 开始遍历
|
||||
this.getAllTable().then(tables => {
|
||||
tables.forEach((item, index) => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(item, index)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
/** 当点击新增按钮时调用此方法 */
|
||||
add() {
|
||||
//update-begin-author:lvdandan date:20201113 for:LOWCOD-1049 JEditaTable,子表默认添加一条数据,addDefaultRowNum设置无效 #1930
|
||||
return new Promise((resolve) => {
|
||||
this.tableReset();
|
||||
resolve();
|
||||
}).then(() => {
|
||||
if (typeof this.addBefore === 'function') this.addBefore()
|
||||
// 默认新增空数据
|
||||
let rowNum = this.addDefaultRowNum
|
||||
if (typeof rowNum !== 'number') {
|
||||
rowNum = 1
|
||||
console.warn('由于你没有在 data 中定义 addDefaultRowNum 或 addDefaultRowNum 不是数字,所以默认添加一条空数据,如果不想默认添加空数据,请将定义 addDefaultRowNum 为 0')
|
||||
}
|
||||
this.eachAllTable((item) => {
|
||||
item.add(rowNum)
|
||||
})
|
||||
if (typeof this.addAfter === 'function') this.addAfter(this.model)
|
||||
this.edit(this.model)
|
||||
})
|
||||
//update-end-author:lvdandan date:20201113 for:LOWCOD-1049 JEditaTable,子表默认添加一条数据,addDefaultRowNum设置无效 #1930
|
||||
},
|
||||
/** 当点击了编辑(修改)按钮时调用此方法 */
|
||||
edit(record) {
|
||||
if(record && '{}'!=JSON.stringify(record)&&record.id){
|
||||
this.tableReset();
|
||||
}
|
||||
if (typeof this.editBefore === 'function') this.editBefore(record)
|
||||
this.visible = true
|
||||
this.activeKey = this.refKeys[0]
|
||||
this.$refs.form.resetFields()
|
||||
this.model = Object.assign({}, record)
|
||||
if (typeof this.editAfter === 'function') this.editAfter(this.model)
|
||||
},
|
||||
/** 关闭弹窗,并将所有JEditableTable实例回归到初始状态 */
|
||||
close() {
|
||||
this.visible = false
|
||||
this.$emit('close')
|
||||
},
|
||||
//清空子表table的数据
|
||||
tableReset(){
|
||||
this.eachAllTable((item) => {
|
||||
item.clearRow()
|
||||
})
|
||||
},
|
||||
/** 查询某个tab的数据 */
|
||||
requestSubTableData(url, params, tab, success) {
|
||||
tab.loading = true
|
||||
getAction(url, params).then(res => {
|
||||
let { result } = res
|
||||
let dataSource = []
|
||||
if (result) {
|
||||
if (Array.isArray(result)) {
|
||||
dataSource = result
|
||||
} else if (Array.isArray(result.records)) {
|
||||
dataSource = result.records
|
||||
}
|
||||
}
|
||||
tab.dataSource = dataSource
|
||||
typeof success === 'function' ? success(res) : ''
|
||||
}).finally(() => {
|
||||
tab.loading = false
|
||||
})
|
||||
},
|
||||
/** 发起请求,自动判断是执行新增还是修改操作 */
|
||||
request(formData) {
|
||||
let url = this.url.add, method = 'post'
|
||||
if (this.model.id) {
|
||||
url = this.url.edit
|
||||
method = 'put'
|
||||
}
|
||||
this.confirmLoading = true
|
||||
httpAction(url, formData, method).then((res) => {
|
||||
if (res.success) {
|
||||
this.$message.success(res.message)
|
||||
this.$emit('ok')
|
||||
this.close()
|
||||
} else {
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/* --- handle 事件 --- */
|
||||
|
||||
/** ATab 选项卡切换事件 */
|
||||
handleChangeTabs(key) {
|
||||
// 自动重置scrollTop状态,防止出现白屏
|
||||
getRefPromise(this, key).then(editableTable => {
|
||||
editableTable.resetScrollTop()
|
||||
})
|
||||
},
|
||||
/** 关闭按钮点击事件 */
|
||||
handleCancel() {
|
||||
this.close()
|
||||
},
|
||||
/** 确定按钮点击事件 */
|
||||
handleOk() {
|
||||
/** 触发表单验证 */
|
||||
this.getAllTable().then(tables => {
|
||||
/** 一次性验证主表和所有的次表 */
|
||||
return validateFormModelAndTables(this.$refs.form,this.model, tables)
|
||||
}).then(allValues => {
|
||||
/** 一次性验证一对一的所有子表 */
|
||||
return this.validateSubForm(allValues)
|
||||
}).then(allValues => {
|
||||
if (typeof this.classifyIntoFormData !== 'function') {
|
||||
throw this.throwNotFunction('classifyIntoFormData')
|
||||
}
|
||||
let formData = this.classifyIntoFormData(allValues)
|
||||
// 发起请求
|
||||
return this.request(formData)
|
||||
}).catch(e => {
|
||||
if (e.error === VALIDATE_NO_PASSED) {
|
||||
// 如果有未通过表单验证的子表,就自动跳转到它所在的tab
|
||||
//update--begin--autor:liusq-----date:20210316------for:未通过表单验证跳转tab问题------
|
||||
this.activeKey = e.index == null ? this.activeKey : (e.paneKey?e.paneKey:this.refKeys[e.index])
|
||||
//update--end--autor:liusq-----date:20210316------for:未通过表单验证跳转tab问题------
|
||||
} else {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
//校验所有子表表单
|
||||
validateSubForm(allValues){
|
||||
return new Promise((resolve) => {
|
||||
resolve(allValues)
|
||||
})
|
||||
},
|
||||
/* --- throw --- */
|
||||
|
||||
/** not a function */
|
||||
throwNotFunction(name) {
|
||||
return `${name} 未定义或不是一个函数`
|
||||
},
|
||||
|
||||
/** not a array */
|
||||
throwNotArray(name) {
|
||||
return `${name} 未定义或不是一个数组`
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -52,10 +52,13 @@ export const JVxeTableMixin = {
|
||||
rowNum = 1
|
||||
console.warn('由于你没有在 data 中定义 addDefaultRowNum 或 addDefaultRowNum 不是数字,所以默认添加一条空数据,如果不想默认添加空数据,请将定义 addDefaultRowNum 为 0')
|
||||
}
|
||||
//update-begin-author:taoyan date:20210315 for: 一对多jvex 默认几行不好使了 LOWCOD-1349
|
||||
this.eachAllTable((item) => {
|
||||
item.addRows()
|
||||
//item.add(rowNum)
|
||||
setTimeout(()=>{
|
||||
item.addRows()
|
||||
}, 30)
|
||||
})
|
||||
//update-end-author:taoyan date:20210315 for: 一对多jvex 默认几行不好使了 LOWCOD-1349
|
||||
if (typeof this.addAfter === 'function') this.addAfter(this.model)
|
||||
this.edit({})
|
||||
},
|
||||
|
||||
181
ant-design-vue-jeecg/src/mixins/JVxeTableModelMixin.js
Normal file
181
ant-design-vue-jeecg/src/mixins/JVxeTableModelMixin.js
Normal file
@ -0,0 +1,181 @@
|
||||
import { VALIDATE_FAILED, getRefPromise, validateFormAndTables,validateFormModelAndTables} from '@/components/jeecg/JVxeTable/utils/vxeUtils.js'
|
||||
import { httpAction, getAction } from '@/api/manage'
|
||||
|
||||
export const JVxeTableModelMixin = {
|
||||
data() {
|
||||
return {
|
||||
title: '操作',
|
||||
visible: false,
|
||||
confirmLoading: false,
|
||||
scrolling: true,
|
||||
model: {},
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 6 }
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 18 }
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 获取所有的JVxeTable实例 */
|
||||
getAllTable() {
|
||||
if (!(this.refKeys instanceof Array)) {
|
||||
throw this.throwNotArray('refKeys')
|
||||
}
|
||||
let values = this.refKeys.map(key => getRefPromise(this, key))
|
||||
return Promise.all(values)
|
||||
},
|
||||
|
||||
/** 遍历所有的JVxeTable实例 */
|
||||
eachAllTable(callback) {
|
||||
// 开始遍历
|
||||
this.getAllTable().then(tables => {
|
||||
console.log("tables",tables)
|
||||
tables.forEach((item, index) => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(item, index)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
/** 当点击新增按钮时调用此方法 */
|
||||
add() {
|
||||
if (typeof this.addBefore === 'function') this.addBefore()
|
||||
// 默认新增空数据
|
||||
let rowNum = this.addDefaultRowNum
|
||||
if (typeof rowNum !== 'number') {
|
||||
rowNum = 1
|
||||
console.warn('由于你没有在 data 中定义 addDefaultRowNum 或 addDefaultRowNum 不是数字,所以默认添加一条空数据,如果不想默认添加空数据,请将定义 addDefaultRowNum 为 0')
|
||||
}
|
||||
this.eachAllTable((item) => {
|
||||
//update-begin-author:taoyan date:20210315 for: 一对多jvex 默认几行不好使了 LOWCOD-1349
|
||||
setTimeout(()=>{
|
||||
item.addRows()
|
||||
}, 30)
|
||||
//update-end-author:taoyan date:20210315 for: 一对多jvex 默认几行不好使了 LOWCOD-1349
|
||||
})
|
||||
if (typeof this.addAfter === 'function') this.addAfter(this.model)
|
||||
this.edit(this.model)
|
||||
},
|
||||
/** 当点击了编辑(修改)按钮时调用此方法 */
|
||||
edit(record) {
|
||||
if (typeof this.editBefore === 'function') this.editBefore(record)
|
||||
this.visible = true
|
||||
this.activeKey = this.refKeys[0]
|
||||
this.$refs.form.resetFields()
|
||||
this.model = Object.assign({}, record)
|
||||
if (typeof this.editAfter === 'function') this.editAfter(this.model)
|
||||
},
|
||||
/** 关闭弹窗,并将所有JVxeTable实例回归到初始状态 */
|
||||
close() {
|
||||
this.visible = false
|
||||
this.eachAllTable((item) => {
|
||||
item._remove()
|
||||
})
|
||||
this.$emit('close')
|
||||
},
|
||||
|
||||
/** 查询某个tab的数据 */
|
||||
requestSubTableData(url, params, tab, success) {
|
||||
tab.loading = true
|
||||
getAction(url, params).then(res => {
|
||||
let { result } = res
|
||||
let dataSource = []
|
||||
if (result) {
|
||||
if (Array.isArray(result)) {
|
||||
dataSource = result
|
||||
} else if (Array.isArray(result.records)) {
|
||||
dataSource = result.records
|
||||
}
|
||||
}
|
||||
tab.dataSource = dataSource
|
||||
typeof success === 'function' ? success(res) : ''
|
||||
}).finally(() => {
|
||||
tab.loading = false
|
||||
})
|
||||
},
|
||||
/** 发起请求,自动判断是执行新增还是修改操作 */
|
||||
request(formData) {
|
||||
let url = this.url.add, method = 'post'
|
||||
if (this.model.id) {
|
||||
url = this.url.edit
|
||||
method = 'put'
|
||||
}
|
||||
this.confirmLoading = true
|
||||
console.log("formData===>",formData);
|
||||
httpAction(url, formData, method).then((res) => {
|
||||
if (res.success) {
|
||||
this.$message.success(res.message)
|
||||
this.$emit('ok')
|
||||
this.close()
|
||||
} else {
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.confirmLoading = false
|
||||
})
|
||||
},
|
||||
|
||||
/* --- handle 事件 --- */
|
||||
|
||||
/** ATab 选项卡切换事件 */
|
||||
handleChangeTabs(key) {
|
||||
// 自动重置scrollTop状态,防止出现白屏
|
||||
getRefPromise(this, key).then(vxeTable => {
|
||||
vxeTable.resetScrollTop()
|
||||
})
|
||||
},
|
||||
/** 关闭按钮点击事件 */
|
||||
handleCancel() {
|
||||
this.close()
|
||||
},
|
||||
/** 确定按钮点击事件 */
|
||||
handleOk() {
|
||||
/** 触发表单验证 */
|
||||
this.getAllTable().then(tables => {
|
||||
/** 一次性验证主表和所有的次表 */
|
||||
return validateFormModelAndTables(this.$refs.form,this.model, tables)
|
||||
}).then(allValues => {
|
||||
/** 一次性验证一对一的所有子表 */
|
||||
return this.validateSubForm(allValues)
|
||||
}).then(allValues => {
|
||||
if (typeof this.classifyIntoFormData !== 'function') {
|
||||
throw this.throwNotFunction('classifyIntoFormData')
|
||||
}
|
||||
let formData = this.classifyIntoFormData(allValues)
|
||||
// 发起请求
|
||||
return this.request(formData)
|
||||
}).catch(e => {
|
||||
if (e.error === VALIDATE_FAILED) {
|
||||
// 如果有未通过表单验证的子表,就自动跳转到它所在的tab
|
||||
this.activeKey = e.index == null ? this.activeKey : this.refKeys[e.index]
|
||||
} else {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
//校验所有子表表单
|
||||
validateSubForm(allValues){
|
||||
return new Promise((resolve) => {
|
||||
resolve(allValues)
|
||||
})
|
||||
},
|
||||
/* --- throw --- */
|
||||
|
||||
/** not a function */
|
||||
throwNotFunction(name) {
|
||||
return `${name} 未定义或不是一个函数`
|
||||
},
|
||||
|
||||
/** not a array */
|
||||
throwNotArray(name) {
|
||||
return `${name} 未定义或不是一个数组`
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ import { deleteAction, getAction,downFile,getFileAccessHttpUrl } from '@/api/man
|
||||
import Vue from 'vue'
|
||||
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
|
||||
import store from '@/store'
|
||||
import {Modal} from 'ant-design-vue'
|
||||
|
||||
export const JeecgListMixin = {
|
||||
data(){
|
||||
@ -66,7 +65,7 @@ export const JeecgListMixin = {
|
||||
let head = {'X-Access-Token': Vue.ls.get(ACCESS_TOKEN)}
|
||||
let tenantid = Vue.ls.get(TENANT_ID)
|
||||
if(tenantid){
|
||||
head['tenant_id'] = tenantid
|
||||
head['tenant-id'] = tenantid
|
||||
}
|
||||
return head;
|
||||
}
|
||||
@ -94,11 +93,11 @@ export const JeecgListMixin = {
|
||||
this.ipagination.total = 0;
|
||||
}
|
||||
//update-end---author:zhangyafei Date:20201118 for:适配不分页的数据列表------------
|
||||
}
|
||||
if(res.code===510){
|
||||
}else{
|
||||
this.$message.warning(res.message)
|
||||
}
|
||||
this.loading = false;
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
initDictConfig(){
|
||||
@ -177,6 +176,8 @@ export const JeecgListMixin = {
|
||||
that.loading = true;
|
||||
deleteAction(that.url.deleteBatch, {ids: ids}).then((res) => {
|
||||
if (res.success) {
|
||||
//重新计算分页问题
|
||||
that.reCalculatePage(that.selectedRowKeys.length)
|
||||
that.$message.success(res.message);
|
||||
that.loadData();
|
||||
that.onClearSelected();
|
||||
@ -198,6 +199,8 @@ export const JeecgListMixin = {
|
||||
var that = this;
|
||||
deleteAction(that.url.delete, {id: id}).then((res) => {
|
||||
if (res.success) {
|
||||
//重新计算分页问题
|
||||
that.reCalculatePage(1)
|
||||
that.$message.success(res.message);
|
||||
that.loadData();
|
||||
} else {
|
||||
@ -205,6 +208,17 @@ export const JeecgListMixin = {
|
||||
}
|
||||
});
|
||||
},
|
||||
reCalculatePage(count){
|
||||
//总数量-count
|
||||
let total=this.ipagination.total-count;
|
||||
//获取删除后的分页数
|
||||
let currentIndex=Math.ceil(total/this.ipagination.pageSize);
|
||||
//删除后的分页数<所在当前页
|
||||
if(currentIndex<this.ipagination.current){
|
||||
this.ipagination.current=currentIndex;
|
||||
}
|
||||
console.log('currentIndex',currentIndex)
|
||||
},
|
||||
handleEdit: function (record) {
|
||||
this.$refs.modalForm.edit(record);
|
||||
this.$refs.modalForm.title = "编辑";
|
||||
@ -218,6 +232,7 @@ export const JeecgListMixin = {
|
||||
handleTableChange(pagination, filters, sorter) {
|
||||
//分页、排序、筛选变化时触发
|
||||
//TODO 筛选
|
||||
console.log(pagination)
|
||||
if (Object.keys(sorter).length > 0) {
|
||||
this.isorter.column = sorter.field;
|
||||
this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
|
||||
@ -280,10 +295,12 @@ export const JeecgListMixin = {
|
||||
},
|
||||
/* 导入 */
|
||||
handleImportExcel(info){
|
||||
this.loading = true;
|
||||
if (info.file.status !== 'uploading') {
|
||||
console.log(info.file, info.fileList);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
this.loading = false;
|
||||
if (info.file.response.success) {
|
||||
// this.$message.success(`${info.file.name} 文件上传成功`);
|
||||
if (info.file.response.code === 201) {
|
||||
@ -305,11 +322,12 @@ export const JeecgListMixin = {
|
||||
this.$message.error(`${info.file.name} ${info.file.response.message}.`);
|
||||
}
|
||||
} else if (info.file.status === 'error') {
|
||||
this.loading = false;
|
||||
if (info.file.response.status === 500) {
|
||||
let data = info.file.response
|
||||
const token = Vue.ls.get(ACCESS_TOKEN)
|
||||
if (token && data.message.includes("Token失效")) {
|
||||
Modal.error({
|
||||
this.$error({
|
||||
title: '登录已过期',
|
||||
content: '很抱歉,登录已过期,请重新登录',
|
||||
okText: '重新登录',
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import { formatDate } from '@/utils/util'
|
||||
import Area from '@/components/_util/Area'
|
||||
import { postAction } from '@/api/manage'
|
||||
|
||||
const onlUtil = {
|
||||
data(){
|
||||
return {
|
||||
mixin_pca:''
|
||||
mixin_pca:'',
|
||||
flowCodePre: 'onl_'
|
||||
}
|
||||
},
|
||||
created(){
|
||||
|
||||
@ -4,19 +4,20 @@ import store from './store'
|
||||
import NProgress from 'nprogress' // progress bar
|
||||
import 'nprogress/nprogress.css' // progress bar style
|
||||
import notification from 'ant-design-vue/es/notification'
|
||||
import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH } from '@/store/mutation-types'
|
||||
import { generateIndexRouter } from "@/utils/util"
|
||||
import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH, OAUTH2_LOGIN_PAGE_PATH } from '@/store/mutation-types'
|
||||
import { generateIndexRouter, isOAuth2AppEnv } from '@/utils/util'
|
||||
|
||||
NProgress.configure({ showSpinner: false }) // NProgress Configuration
|
||||
|
||||
const whiteList = ['/user/login', '/user/register', '/user/register-result','/user/alteration'] // no redirect whitelist
|
||||
whiteList.push(OAUTH2_LOGIN_PAGE_PATH)
|
||||
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start() // start progress bar
|
||||
|
||||
if (Vue.ls.get(ACCESS_TOKEN)) {
|
||||
/* has token */
|
||||
if (to.path === '/user/login') {
|
||||
if (to.path === '/user/login' || to.path === OAUTH2_LOGIN_PAGE_PATH) {
|
||||
next({ path: INDEX_MAIN_PAGE_PATH })
|
||||
NProgress.done()
|
||||
} else {
|
||||
@ -59,10 +60,18 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
} else {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
// 在免登录白名单,如果进入的页面是login页面并且当前是OAuth2app环境,就进入OAuth2登录页面
|
||||
if (to.path === '/user/login' && isOAuth2AppEnv()) {
|
||||
next({path: OAUTH2_LOGIN_PAGE_PATH})
|
||||
} else {
|
||||
// 在免登录白名单,直接进入
|
||||
next()
|
||||
}
|
||||
NProgress.done()
|
||||
} else {
|
||||
next({ path: '/user/login', query: { redirect: to.fullPath } })
|
||||
// 如果当前是在OAuth2APP环境,就跳转到OAuth2登录页面
|
||||
let path = isOAuth2AppEnv() ? OAUTH2_LOGIN_PAGE_PATH : '/user/login'
|
||||
next({ path: path, query: { redirect: to.fullPath } })
|
||||
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,7 +16,8 @@ const getters = {
|
||||
enhanceJs:(state) => (code) => {
|
||||
state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
|
||||
return state.enhance.enhanceJs[code]
|
||||
}
|
||||
},
|
||||
sysSafeMode: state => state.user.sysSafeMode,
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -3,9 +3,9 @@ import Vuex from 'vuex'
|
||||
|
||||
import app from './modules/app'
|
||||
import user from './modules/user'
|
||||
import permission from './modules/permission'
|
||||
import enhance from './modules/enhance'
|
||||
import online from './modules/online'
|
||||
import permission from './modules/permission'
|
||||
import getters from './getters'
|
||||
|
||||
Vue.use(Vuex)
|
||||
@ -16,7 +16,7 @@ export default new Vuex.Store({
|
||||
user,
|
||||
permission,
|
||||
enhance,
|
||||
online
|
||||
online,
|
||||
},
|
||||
state: {
|
||||
|
||||
|
||||
@ -14,7 +14,9 @@ const user = {
|
||||
welcome: '',
|
||||
avatar: '',
|
||||
permissionList: [],
|
||||
info: {}
|
||||
info: {},
|
||||
// 系统安全模式
|
||||
sysSafeMode: null,
|
||||
},
|
||||
|
||||
mutations: {
|
||||
@ -38,6 +40,13 @@ const user = {
|
||||
SET_TENANT: (state, id) => {
|
||||
state.tenantid = id
|
||||
},
|
||||
SET_SYS_SAFE_MODE: (state, sysSafeMode) => {
|
||||
if (typeof sysSafeMode === 'boolean') {
|
||||
state.sysSafeMode = sysSafeMode
|
||||
} else {
|
||||
state.sysSafeMode = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
@ -124,20 +133,22 @@ const user = {
|
||||
sessionStorage.setItem(USER_AUTH,JSON.stringify(authData));
|
||||
sessionStorage.setItem(SYS_BUTTON_AUTH,JSON.stringify(allAuthData));
|
||||
if (menuData && menuData.length > 0) {
|
||||
//update--begin--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||
menuData.forEach((item, index) => {
|
||||
if (item["children"]) {
|
||||
let hasChildrenMenu = item["children"].filter((i) => {
|
||||
return !i.hidden || i.hidden == false
|
||||
})
|
||||
if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
|
||||
item["hidden"] = true
|
||||
}
|
||||
}
|
||||
})
|
||||
//console.log(" menu show json ", menuData)
|
||||
//update--end--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||
// //update--begin--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||
// menuData.forEach((item, index) => {
|
||||
// if (item["children"]) {
|
||||
// let hasChildrenMenu = item["children"].filter((i) => {
|
||||
// return !i.hidden || i.hidden == false
|
||||
// })
|
||||
// if (hasChildrenMenu == null || hasChildrenMenu.length == 0) {
|
||||
// item["hidden"] = true
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// //console.log(" menu show json ", menuData)
|
||||
// //update--end--autor:qinfeng-----date:20200109------for:JEECG-63 一级菜单的子菜单全部是隐藏路由,则一级菜单不显示------
|
||||
commit('SET_PERMISSIONLIST', menuData)
|
||||
// 设置系统安全模式
|
||||
commit('SET_SYS_SAFE_MODE', response.result.sysSafeMode)
|
||||
} else {
|
||||
reject('getPermissionList: permissions must be a non-null array !')
|
||||
}
|
||||
@ -159,6 +170,7 @@ const user = {
|
||||
Vue.ls.remove(USER_NAME)
|
||||
Vue.ls.remove(UI_CACHE_DB_DICT_DATA)
|
||||
Vue.ls.remove(CACHE_INCLUDED_ROUTES)
|
||||
Vue.ls.remove(TENANT_ID)
|
||||
//console.log('logoutToken: '+ logoutToken)
|
||||
logout(logoutToken).then(() => {
|
||||
if (process.env.VUE_APP_SSO == 'true') {
|
||||
|
||||
@ -17,6 +17,7 @@ export const ENCRYPTED_STRING = 'ENCRYPTED_STRING'
|
||||
export const ENHANCE_PRE = 'enhance_'
|
||||
export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA'
|
||||
export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis'
|
||||
export const OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login'
|
||||
export const TENANT_ID = 'TENANT_ID'
|
||||
export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS'
|
||||
//路由缓存问题,关闭了tab页时再打开就不刷新 #842
|
||||
|
||||
@ -8,12 +8,14 @@ const FormTypes = {
|
||||
select: 'select',
|
||||
date: 'date',
|
||||
datetime: 'datetime',
|
||||
time: 'time',
|
||||
upload: 'upload',
|
||||
file: 'file',
|
||||
image: 'image',
|
||||
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',
|
||||
@ -76,6 +78,36 @@ export function validateFormAndTables(form, cases) {
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
}
|
||||
/**
|
||||
* 一次性验证主表单和所有的次表单(新版本)
|
||||
* @param form 主表单 form 对象
|
||||
* @param cases 接收一个数组,每项都是一个JEditableTable实例
|
||||
* @returns {Promise<any>}
|
||||
* @author sunjianlei
|
||||
*/
|
||||
export function validateFormModelAndTables(form,values, cases) {
|
||||
|
||||
if (!(form && typeof form.validate === 'function')) {
|
||||
throw `form 参数需要的是一个form对象,而传入的却是${typeof form}`
|
||||
}
|
||||
let options = {}
|
||||
return new Promise((resolve, reject) => {
|
||||
// 验证主表表单
|
||||
form.validate((valid,obj) => {
|
||||
valid ?resolve(values):reject({ error: VALIDATE_NO_PASSED })
|
||||
})
|
||||
}).then(values => {
|
||||
Object.assign(options, { formValue: values })
|
||||
// 验证所有子表的表单
|
||||
return validateTables(cases)
|
||||
}).then(all => {
|
||||
Object.assign(options, { tablesValue: all })
|
||||
return Promise.resolve(options)
|
||||
}).catch(error => {
|
||||
return Promise.reject(error)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -9,10 +9,10 @@ export function disabledAuthFilter(code,formData) {
|
||||
}
|
||||
|
||||
function nodeDisabledAuth(code,formData){
|
||||
console.log("页面权限禁用--NODE--开始");
|
||||
//console.log("页面权限禁用--NODE--开始");
|
||||
let permissionList = [];
|
||||
try {
|
||||
console.log("页面权限禁用--NODE--开始",formData);
|
||||
//console.log("页面权限禁用--NODE--开始",formData);
|
||||
if (formData) {
|
||||
let bpmList = formData.permissionList;
|
||||
permissionList = bpmList.filter(item=>item.type=='2')
|
||||
@ -53,7 +53,7 @@ function nodeDisabledAuth(code,formData){
|
||||
}
|
||||
|
||||
function globalDisabledAuth(code){
|
||||
console.log("全局页面禁用权限--Global--开始");
|
||||
//console.log("全局页面禁用权限--Global--开始");
|
||||
|
||||
let permissionList = [];
|
||||
let allPermissionList = [];
|
||||
@ -106,7 +106,7 @@ function globalDisabledAuth(code){
|
||||
}else{
|
||||
for (let item2 of permissionList) {
|
||||
if(code === item2.action){
|
||||
console.log("全局页面权限解除禁用--Global--生效");
|
||||
//console.log("全局页面权限解除禁用--Global--生效");
|
||||
gFlag = false;
|
||||
}
|
||||
}
|
||||
|
||||
129
ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js
Normal file
129
ant-design-vue-jeecg/src/utils/encryption/signMd5Utils.js
Normal file
@ -0,0 +1,129 @@
|
||||
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
|
||||
//【这边条件没有encode】带条件参数例子:/sys/dict/getDictItems/sys_user,realname,id,username!='admin'%20order%20by%20create_time
|
||||
let lastpathVariable = url.substring(url.lastIndexOf('/') + 1);
|
||||
if(lastpathVariable.includes(",")){
|
||||
if(lastpathVariable.includes("?")){
|
||||
lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?"));
|
||||
}
|
||||
//解决Sign 签名校验失败 #2728
|
||||
result["x-path-variable"] = decodeURIComponent(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);
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user