Compare commits

...

84 Commits

Author SHA1 Message Date
cb7ae9ca6f 低代码能力 2020-07-01 11:35:43 +08:00
e4b4898e27 JEECG BOOT 低代码开发平台 2020-07-01 11:34:17 +08:00
5066ef526d JEECG BOOT 低代码开发平台 2020-07-01 11:16:12 +08:00
088ff85dcc Jeecg-Boot 低代码开发平台 2020-07-01 09:22:25 +08:00
dadf6a78a1 fastjson 新安全漏洞修复解决;
第三方登录bug处理;
IE兼容js本地化提高前端加载速度
2020-06-04 14:22:40 +08:00
8a8e92cda3 功能蓝图 2020-05-31 16:09:06 +08:00
4566e50dd2 Jeecg Boot 功能蓝图 2020-05-31 16:04:04 +08:00
01ce2e8dab mysql数据库表名和字段默认小写、启动国际化警告问题解决 2020-05-19 00:24:50 +08:00
dc804d9372 jeecgboot 2.2新功能说明 2020-05-15 11:15:45 +08:00
9c64b9a8ed jeecgboot 2.2新功能说明 2020-05-15 11:11:40 +08:00
d52979621f OnLine表单-二次新增数据报错【严重】 2020-05-13 17:52:51 +08:00
5ffc3e4d13 www.jeecg.com官网地址更新 2020-05-08 18:24:50 +08:00
ea7ac65814 2.2新版——10分钟快速入门(每日更新,请大家关注B站) 2020-05-08 10:16:22 +08:00
cdfa6f8ac6 降低前端搭建难度,暂时删除tui-editor markdown编辑器 2020-05-07 10:03:00 +08:00
6ce3a892b6 Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-05 21:13:08 +08:00
28e9a0ddfb Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-03 22:43:59 +08:00
1f27948814 Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-03 17:49:49 +08:00
3bff16a446 Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-03 15:43:49 +08:00
d5bfccdeb0 Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-03 14:12:46 +08:00
9e046a07d4 Jeecg-Boot 2.2.0 版本发布 | 重磅升级 2020-05-03 12:43:53 +08:00
046831e700 浏览器支持说明 2020-03-31 11:20:24 +08:00
e7ad502390 提供oracle11g、sqlserver2017脚步 2020-03-12 18:47:42 +08:00
9f1a2b89b7 oracle11g 脚步提供 2020-03-12 18:37:51 +08:00
5878fcb12b 删除不需要的代码 2020-03-11 21:19:21 +08:00
27e280bd97 build后online表单无法添加/编辑问题处理 2020-03-09 10:54:08 +08:00
91594ddb1a 1.项目样式 scss 全部转换为 less,且移除了项目 node-sass 依赖;
2.online开发ButtonExpHandler is not defined #957;
3.关于打包后JS文件过大,导致加载缓慢的建议 #969
2020-03-04 22:37:17 +08:00
4c0c0945a1 修改新版上传组件,保存路径问题 2020-02-25 20:45:53 +08:00
2b3e157d88 Jeecg-Boot 2.1.4 版本发布 (版本问题修复) 2020-02-25 17:14:10 +08:00
e2d4bb29db Jeecg-Boot 2.1.4 版本发布 (升级ant-design-vue版本) 2020-02-25 16:37:20 +08:00
865ace6db0 Jeecg-Boot 2.1.4 版本发布 (新版问题修复) 2020-02-25 14:52:29 +08:00
7bc3931e1a Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 18:10:58 +08:00
94a945291d Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 17:55:42 +08:00
49a303d5e8 解决新版2.1.4 online表单插入行,添加报错问题 2020-02-24 17:48:23 +08:00
1cabc94cea Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 11:58:21 +08:00
760f8e9f46 Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 11:47:24 +08:00
38e6586c84 Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 03:24:28 +08:00
d7ca307fa2 Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 03:15:14 +08:00
188cec1646 Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 02:48:58 +08:00
4a4f236772 Jeecg-Boot 2.1.4 版本发布 | 重构较大,较多新功能 2020-02-24 02:44:53 +08:00
06847cd801 论坛变更域名:bbs.jeecg.com 2020-01-19 09:28:12 +08:00
365810d4e6 字典排序问题合并 2020-01-13 12:03:30 +08:00
ed83135062 Merge pull request #844 from yyri/master
<a-form-item> should be nested under <a-col>
2020-01-13 11:47:00 +08:00
beabf9b6dd README.md 2020-01-13 11:37:30 +08:00
238b4898c7 20191227 取消系统功能的权限注解 2019-12-27 15:00:22 +08:00
4372773924 <a-form-item> should be nested under <a-col> 2019-12-26 11:11:12 +08:00
d399ec904a Jeecg-Boot 2.1.3 大屏版本发布 2019-12-25 15:50:15 +08:00
8ecc971940 Jeecg-Boot 2.1.3 大屏版本发布 2019-12-25 15:14:54 +08:00
66b3b290fb Jeecg-Boot 2.1.3 大屏版本发布 2019-12-25 13:36:36 +08:00
a9c7f3e547 Jeecg-Boot 2.1.3 大屏版本发布 2019-12-25 13:34:06 +08:00
b54dada91e 2.1.2 大屏版本发布 2019-12-25 13:28:04 +08:00
3ead7c3ae1 Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot.git 2019-12-25 13:26:13 +08:00
9c44ffaa8e 2.1.3 大屏版本发布 2019-12-25 13:25:10 +08:00
ea5ef384f2 2.1.3 大屏版本发布 2019-12-25 13:14:08 +08:00
73f7acfd5a Merge pull request #787 from a63149300/patch-1
Update UserList.vue
2019-12-15 16:38:43 +08:00
728d62f851 Jeecg-Boot 2.1.2版本发布 2019-12-09 14:46:40 +08:00
cd471735d7 Update UserList.vue
修改状态文字
2019-12-06 09:13:44 +08:00
30b35af000 提供oracle11g\sqlserver2017脚步 2019-11-27 15:56:17 +08:00
3372c88607 统计 2019-11-25 15:04:34 +08:00
ce95008fdd 统计下载量 2019-11-25 14:58:00 +08:00
bb54b20734 统计下载 2019-11-25 14:56:27 +08:00
bd790ee24b 统计下载 2019-11-25 14:54:46 +08:00
11018ab29f 统计下载 2019-11-25 14:53:26 +08:00
67c4f7b3d9 Jeecg-Boot 2.1.2 版本发布 2019-11-22 09:45:28 +08:00
9bd67f9905 Jeecg-Boot 2.1.2版本发布 2019-11-21 18:20:40 +08:00
283be0480e Jeecg-Boot 2.1.2版本发布 2019-11-21 18:17:25 +08:00
f0d372d008 Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot.git 2019-11-21 12:26:02 +08:00
65180733e1 Signed-off-by: zhangdaihao <zhangdaiscott@163.com> 2019-11-21 12:25:58 +08:00
c1dab6276a Merge pull request #647 from klniu/master
返回前端数据去除密码与盐
2019-11-19 16:35:41 +08:00
a6b0dccded 投票 2019-11-18 20:02:28 +08:00
bb24448ea2 QQ群支持 2019-10-29 12:04:17 +08:00
07694cf42b 数据库版本描述 2019-10-29 12:04:02 +08:00
2b544b74f7 返回前端数据去除密码与盐 2019-10-21 16:50:25 +08:00
ac891d81ab 返回前端数据去除密码与盐 2019-10-21 14:49:17 +08:00
0640f0b421 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-21 12:10:39 +08:00
ee5fa7ec22 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-21 12:08:30 +08:00
8b41076a27 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-21 11:30:26 +08:00
492ce922e9 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-21 11:27:42 +08:00
fce50e5209 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-21 11:26:02 +08:00
107e901853 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-20 22:49:11 +08:00
c69884c84f JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-20 22:11:27 +08:00
5e2eba86e7 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-19 16:03:45 +08:00
9c6f68fd4a JeecgBoot 2.1.1 版本发布 2019-10-19 15:44:19 +08:00
fd57f233e5 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-18 19:38:34 +08:00
04c0ea55f2 JeecgBoot 2.1.1 代码生成器AI版本发布 2019-10-18 18:37:41 +08:00
737 changed files with 105771 additions and 33632 deletions

10
.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
## ide
**/.idea
*.iml
## backend
**/target
**/logs
## front
**/*.lock

View File

@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (c) 2019 <a href="http://www.jeecg.org">Jeecg Boot</a> All rights reserved.
Copyright (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

128
README.md
View File

@ -1,16 +1,18 @@
![JEECG](https://static.oschina.net/uploads/img/201905/24164523_XDhg.png "JeecgBoot快速开发平台")
![JEECG](https://static.oschina.net/uploads/img/201905/24164523_XDhg.png "JeecgBoot低代码开发平台")
Jeecg-Boot 快速开发平台(前后端分离版本)
JEECG BOOT 低代码开发平台(前后端分离版本)
===============
当前最新版本: 2.1.0发布日期20190826
当前最新版本: 2.2.0发布日期2020-05-06
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-JEECG团队-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/version-2.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-2.2.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -19,17 +21,18 @@ Jeecg-Boot 快速开发平台(前后端分离版本)
项目介绍:
-----------------------------------
<h3 align="center">Java RAD framework for enterprise web applications</h3>
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
Jeecg-Boot 是一款基于SpringBoot+代码生成器的快速开发平台!前后端分离架构SpringBoot,Ant Design Vue,Mybatis,Shiro,JWT。强大的代码生成器让前端和后台代码一键生成,不需要写任何代码保持jeecg一贯的强大绝对是全栈开发福音 JeecgBoot在提高UI能力的同时降低了前后分离的开发成本JeecgBoot还独创在线开发模式No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线图表设计、在线设计流程等等
JeecgBoot 是一款基于代码生成器的`低代码`开发平台,零代码开发!采用前后端分离架构SpringBoot2.xAnt Design&VueMybatis-plusShiroJWT。强大的代码生成器让前端代码一键生成,无需写任何代码! JeecgBoot引领新的开发模式(Online Coding模式-> 代码生成器模式-> 手工MERGE智能开发) 帮助解决Java项目70%的重复工作,让开发更多关注业务逻辑。既能快速提高开发效率,帮助公司节省成本,同时又不失灵活性!JeecgBoot还独创在线开发模式No代码概念在线表单配置表单设计器)、移动配置能力、工作流配置(在线设计流程)、报表配置能力、在线图表配置、插件能力(可插拔)等等
JEECG宗旨是: 简单功能由Online Coding配置实现在线配置表单、在线配置报表、在线图表设计、在线设计流程、在线设计表单复杂功能由代码生成器生成进行手工Merge既保证了智能又兼顾了灵活;
`JEECG宗旨是:` 简单功能由Online Coding配置实现既`零代码开发`在线配置表单、在线配置报表、在线图表设计、在线设计流程、在线设计表单复杂功能由代码生成器生成进行手工Merge既保证了`智能`又兼顾了`灵活`;
业务流程采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
适用项目
-----------------------------------
Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中尤其适合企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
@ -40,63 +43,69 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
- 技术官网: [http://www.jeecg.com](http://www.jeecg.com)
- 开发文档: [http://doc.jeecg.com/1273753](http://doc.jeecg.com/1273753)
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com/1273753)
- 视频教程 [JeecgBoot入门系列视频](https://space.bilibili.com/454617261/channel/detail?cid=84186)
- 视频教程 [JeecgBoot入门视频教程](http://www.jeecg.com/doc/video)
- 常见问题: [入门常见问题大全](http://www.jeecg.org/forum.php?mod=viewthread&tid=7816&extra=page%3D1)
- 常见问题: [入门常见问题大全](http://bbs.jeecg.com/forum.php?mod=viewthread&tid=7816&extra=page%3D1)
- 更新日志: [版本日志](http://www.jeecg.com/#/doc/changelog)
- 更新日志: [版本日志](http://www.jeecg.com/doc/log)
交流互动
-----------------------------------
- QQ交流群 ①284271917、②769925425
- QQ交流群 ②769925425、①284271917
- 反馈问题: [反馈问题请按格式发Issues](https://github.com/zhangdaiscott/jeecg-boot/issues/new)
- 参与开源: [欢迎加入JEECG开源团队共同进步](http://www.jeecg.com/#/doc/canyu-os)
- 参与开源: [欢迎加入JEECG开源团队共同进步](http://www.jeecg.com/doc/join)
- Online一分钟 [1分钟快速学习](https://my.oschina.net/jeecg/blog/3083313)
为什么选择JEECG-BOOT?
-----------------------------------
* 1.采用最新主流前后分离框架Springboot+Mybatis+antd容易上手; 代码生成器依赖性低,灵活的扩展能力,可灵活实现二次开发;
* 2.开发效率高,采用代码生成器,单表数据模型和一对多(父子表)数据模型,增删改查功能自动生成,菜单配置直接使用;
* 3.代码生成器提供强大模板机制,支持自定义模板风格。目前提供四套风格模板(单表两套、一对多套)
* 4.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
* 5.常用共通封装,各种工具类(定时任务,短信接口,邮件发送,Excel导入导出等),基本满足80%项目需求
* 6.简易Excel导入导出支持单表导出和一对多表模式导出生成的代码自带导入导出功能
* 7.集成简易报表工具图像报表和数据导出非常方便可极其方便的生成图形报表、pdf、excel、word等报表
* 8.采用前后分离技术页面UI风格精美针对常用组件做了封装时间、行表格控件、截取显示控件、报表组件编辑器等等
* 9.查询过滤器查询功能自动生成后台动态拼SQL追加查询条件支持多种匹配方式全匹配/模糊查询/包含查询/不匹配查询);
* 10.数据权限(精细化数据权限控制,控制到行级,列表级,表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段
* 11.在线配置报表(无需编码,通过在线配置方式,实现曲线图,柱状图,数据等报表
* 12.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等);
* 13.集成工作流activiti并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 14.在线流程设计采用开源Activiti流程引擎实现在线画流程,自定义表单,表单挂靠,业务流转
* 15.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;
* 16.提供单点登录CAS集成方案项目中已经提供完善的对接代码
* 17.表单设计器支持用户自定义表单布局支持单表一对多表单、支持select、radio、checkbox、textarea、date、popup、列表、宏等控件
* 18.专业接口对接机制统一采用restful接口方式集成swagger-ui在线接口文档Jwt token安全验证方便客户端对接
* 19.接口安全机制,可细化控制接口授权,非常简便实现不同客户端只看自己数据等控制
* 20.高级组合查询功能,在线配置支持主子表关联查询,可保存查询历史
* 21.提供各种系统监控,实时跟踪系统运行情况(监控 Redis、Tomcat、jvm、服务器信息、请求追踪、SQL监控
* 22.消息中心(支持短信、邮件、微信推送等等)
* 21.集成Websocket消息通知机制
* 22.提供APP发布方案国际化
* 23.支持多语言,提供国际化方案;
* 24.数据变更记录日志,可记录数据每次变更内容,通过版本对比功能查看历史变化
* 25.平台UI强大实现了移动自适应
* 26.平台首页风格,提供多种组合模式,支持自定义风格
* 27.提供简单易用的打印插件支持谷歌、IE浏览器等各种浏览器
* 28.示例代码丰富,提供很多学习案例参考
* 29.采用maven分模块开发方式
* 30.支持菜单动态路由
* 31.权限控制采用 RBACRole-Based Access Control基于角色的访问控制
* 1.采用最新主流前后分离框架Springboot+Mybatis+antd容易上手; 代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发;
* 2.开发效率高,采用代码生成器,单表、树列表、一对多、一对一等数据模型,增删改查功能一键生成,菜单配置直接使用;
* 3.代码生成器提供强大模板机制,支持自定义模板目前提供四套风格模板(单表两套、树模型一套、一对多套)
* 4.代码生成器非常智能在线业务建模、在线配置、所见即所得支持23种类控件一键生成前后端代码大幅度提升开发效率不再为重复工作发愁。
* 5.低代码能力Online在线表单无需编码通过在线配置表单实现表单的增删改查支持单表、树、一对多、一对一等模型实现人人皆可编码
* 5-1.低代码能力Online在线报表无需编码通过在线配置方式实现数据报表可以快速抽取数据减轻开发压力实现人人皆可编码
* 5-2.低代码能力Online在线图表无需编码通过在线配置方式实现曲线图柱状图数据报表等支持自定义排版布局实现人人皆可编码
* 6.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
* 7.常用共通封装,各种工具类(定时任务,短信接口,邮件发送,Excel导入导出等),基本满足80%项目需求
* 8.简易Excel导入导出支持单表导出和一对多表模式导出生成的代码自带导入导出功能
* 9.集成简易报表工具图像报表和数据导出非常方便可极其方便的生成图形报表、pdf、excel、word等报表
* 10.采用前后分离技术页面UI风格精美针对常用组件做了封装时间、行表格控件、截取显示控件、报表组件编辑器等等
* 11.查询过滤器查询功能自动生成后台动态拼SQL追加查询条件支持多种匹配方式全匹配/模糊查询/包含查询/不匹配查询)
* 12.数据权限(精细化数据权限控制,控制到行级,列表级,表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段
* 13.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等);
* 14.支持SAAS服务模式提供SaaS多租户架构方案。
* 15.分布式文件服务集成minio、阿里OSS等优秀的第三方提供便捷的文件上传与管理同时也支持本地存储。
* 16.主流数据库兼容一套代码完全兼容Mysql、Postgresql、Oracle三大主流数据库。
* 17.集成工作流activiti并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 18.低代码能力在线流程设计采用开源Activiti流程引擎实现在线画流程,自定义表单,表单挂靠,业务流转
* 19.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;
* 20.提供单点登录CAS集成方案项目中已经提供完善的对接代码
* 21.低代码能力表单设计器支持用户自定义表单布局支持单表一对多表单、支持select、radio、checkbox、textarea、date、popup、列表、宏等控件
* 22.专业接口对接机制统一采用restful接口方式集成swagger-ui在线接口文档Jwt token安全验证方便客户端对接
* 23.接口安全机制,可细化控制接口授权,非常简便实现不同客户端只看自己数据等控制
* 24.高级组合查询功能,在线配置支持主子表关联查询,可保存查询历史
* 25.提供各种系统监控,实时跟踪系统运行情况(监控 Redis、Tomcat、jvm、服务器信息、请求追踪、SQL监控
* 26.消息中心(支持短信、邮件、微信推送等等)
* 27.集成Websocket消息通知机制
* 28.移动自适应效果优秀提供APP发布方案
* 29.支持多语言,提供国际化方案;
* 30.数据变更记录日志,可记录数据每次变更内容,通过版本对比功能查看历史变化
* 31.平台UI强大实现了移动自适应
* 32.平台首页风格,提供多种组合模式,支持自定义风格
* 33.提供简单易用的打印插件支持谷歌、火狐、IE11+ 等各种浏览器
* 34.示例代码丰富,提供很多学习案例参考
* 35.采用maven分模块开发方式
* 36.支持菜单动态路由
* 37.权限控制采用 RBACRole-Based Access Control基于角色的访问控制
@ -162,6 +171,8 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
│ └─树分类字典
│ └─系统公告
│ └─我的组织机构
│ └─职务管理
│ └─通讯录
├─消息中心
│ ├─消息管理
│ ├─模板管理
@ -199,7 +210,12 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
│ └─进度条
│ └─排名列表
│ └─等等
│─大屏模板
│ ├─作战指挥中心大屏
│ └─物流服务中心大屏
│─常用示例
│ ├─自定义组件
│ ├─对象存储(对接阿里云)
│ ├─单表模型例子
│ └─一对多模型例子
│ └─打印例子
@ -208,7 +224,10 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
│ └─常用选择组件
│ └─异步树table
│ └─接口模拟测试
│ └─表格合计示例
│ └─异步树列表示例
│ └─一对多JEditable
│ └─JEditable组件示例
│ └─图片拖拽排序
│ └─图片翻页
│ └─图片预览
@ -240,15 +259,16 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
│ └─异常页面
│ └─个人页面
├─高级功能
│ ├─系统编码规则
│ ├─提供单点登录CAS集成方案
│ ├─提供APP发布方案
│ ├─集成Websocket消息通知机制
├─Online在线开发(暂未开源)
├─Online在线低代码开发(暂未开源)
│ ├─Online在线表单 - 功能已开放
│ ├─在线代码生成器 - 功能已开放
│ ├─Online在线报表 - 功能已开放
│ ├─Online在线图表
│ ├─Online图表模板配置
│ ├─Online在线报表
│ ├─高级表单设计器
│─流程模块功能 (暂不开源)
│ ├─流程设计器
@ -269,6 +289,9 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
```
### Jeecg Boot 产品功能蓝图
![功能蓝图](https://static.jeecg.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")
后台开发环境和依赖
----
@ -277,7 +300,7 @@ Jeecg-Boot快速开发平台可以应用在任何J2EE项目的开发中
- jdk8
- mysql
- redis
- 数据库脚jeecg-boot\docs\jeecg-boot-mysql.sql
- 数据库脚jeecg-boot\docs\jeecg-boot-mysql.sql
- 默认登录账号: admin/123456
@ -327,10 +350,13 @@ yarn run lint
系统效果
----
##### 大屏模板
![输入图片说明](https://static.oschina.net/uploads/img/201912/25133248_Ag1C.jpg "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201912/25133301_k9Kc.jpg "在这里输入图片标题")
##### PC端
![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题")
![输入图片说明](https://oscimg.oschina.net/oscnet/ba807921197596ba56f495d4b22ee3280ca.jpg "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160657_cHwb.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题")

View File

@ -1,7 +1,7 @@
Ant Design Jeecg Vue
====
当前最新版本: 2.1.0发布日期20190826
当前最新版本: 2.2.0发布日期2020-05-06
Overview
----

View File

@ -1,5 +1,6 @@
module.exports = {
presets: [
'@vue/app'
['@vue/app',
{ useBuiltIns: 'entry' }]
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,19 +1,19 @@
{
"name": "vue-antd-jeecg",
"version": "2.1.0",
"private": false,
"version": "2.2.0",
"private": true,
"scripts": {
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
"pre": "yarn --registry https://registry.npm.taobao.org || cnpm install || npm install --registry https://registry.npm.taobao.org ",
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@antv/data-set": "^0.10.2",
"@jeecg/antd-onine": "^1.0.1",
"@antv/data-set": "^0.11.2",
"@jeecg/antd-online-beta220": "^1.0.1",
"@tinymce/tinymce-vue": "^2.0.0",
"ant-design-vue": "^1.3.9",
"apexcharts": "^3.6.5",
"ant-design-vue": "1.5.2",
"area-data": "^5.0.6",
"axios": "^0.18.0",
"clipboard": "^2.0.4",
"codemirror": "^5.46.0",
@ -24,23 +24,20 @@
"lodash.pick": "^4.4.0",
"md5": "^2.2.1",
"nprogress": "^0.2.0",
"tinymce": "^5.0.2",
"tinymce": "^5.1.4",
"viser-vue": "^2.4.4",
"vue": "^2.6.10",
"vue-apexcharts": "^1.3.2",
"vue-class-component": "^6.0.0",
"vue-area-linkage": "^5.1.0",
"vue-cropper": "^0.4.8",
"vue-i18n": "^8.7.0",
"vue-loader": "^15.7.0",
"vue-ls": "^3.2.0",
"vue-photo-preview": "^1.1.3",
"vue-print-nb-jeecg": "^1.0.8",
"vue-property-decorator": "^7.3.0",
"vue-print-nb-jeecg": "^1.0.9",
"vue-router": "^3.0.1",
"vue-splitpane": "^1.0.4",
"vuedraggable": "^2.20.0",
"vuex": "^3.0.1",
"vuex-class": "^0.3.1"
"vuex": "^3.1.0"
},
"devDependencies": {
"@babel/polyfill": "^7.2.5",
@ -49,12 +46,12 @@
"@vue/cli-service": "^3.3.0",
"@vue/eslint-config-standard": "^4.0.0",
"babel-eslint": "^10.0.1",
"compression-webpack-plugin": "^3.1.0",
"eslint": "^5.16.0",
"eslint-plugin-vue": "^5.1.0",
"html-webpack-plugin": "^4.2.0",
"less": "^3.9.0",
"less-loader": "^4.1.0",
"node-sass": "^4.11.0",
"sass-loader": "^7.0.1",
"vue-template-compiler": "^2.6.10"
},
"eslintConfig": {
@ -64,7 +61,7 @@
},
"extends": [
"plugin:vue/strongly-recommended",
"eslint:recommended"
"@vue/standard"
],
"parserOptions": {
"parser": "babel-eslint"
@ -92,7 +89,9 @@
"vue/no-use-v-if-with-v-for": 0,
"vue/html-closing-bracket-newline": 0,
"vue/no-parsing-error": 0,
"no-console": 0
"no-console": 0,
"no-tabs": 0,
"indent": [1, 4]
}
},
"postcss": {
@ -103,6 +102,6 @@
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
"not ie <= 10"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -7679,3 +7679,23 @@ font.medium {
font.weak {
color: #f5222d;
}
// begin -------- JAreaLinkage 三级联动样式 --------------
.cascader-menu-list .cascader-menu-option.hover,
.cascader-menu-list .cascader-menu-option:hover {
background-color: color(~`colorPalette("@{primary-color}", 1)`);
}
.area-selectable-list .area-select-option.hover {
background-color: color(~`colorPalette("@{primary-color}", 1)`);
}
.area-select:hover {
border-color: @primary-color;
}
.area-select:active {
box-shadow: 0 0 0 2px color(~`colorPalette("@{primary-color}", 1)`);
}
// end -------- JAreaLinkage 三级联动样式 --------------

View File

@ -5,9 +5,9 @@
<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>Jeecg-Boot 快速开发平台</title>
<title>Jeecg-Boot 企业级快速开发平台</title>
<link rel="icon" href="<%= BASE_URL %>logo.png">
<script src="https://cdn.bootcss.com/babel-polyfill/7.6.0/polyfill.js"></script>
<script src="/cdn/babel-polyfill/polyfill_7_2_5.js"></script>
<style>
html,
body,
@ -244,7 +244,8 @@
window._CONFIG = {};
window._CONFIG['domianURL'] = 'http://127.0.0.1:8080/jeecg-boot';
window._CONFIG['casPrefixUrl'] = 'http://cas.example.org:8443/cas';
window._CONFIG['imgDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/view';
window._CONFIG['onlinePreviewDomainURL'] = 'http://fileview.jeecg.com/onlinePreview'
window._CONFIG['staticDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/static';
window._CONFIG['pdfDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/pdf/pdfPreviewIframe';
</script>
</head>

View File

@ -1,9 +1,9 @@
<template>
<a-locale-provider :locale="locale">
<a-config-provider :locale="locale">
<div id="app">
<router-view/>
</div>
</a-locale-provider>
</a-config-provider>
</template>
<script>
import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'

View File

@ -0,0 +1,30 @@
import Vue from 'vue'
/**
* 将一个请求分组
*
* @param getPromise 传入一个可以获取到Promise对象的方法
* @param groupId 分组ID如果不传或者为空则不分组
* @param expire 过期时间,默认 半分钟
*/
export function httpGroupRequest(getPromise, groupId, expire = 1000 * 30) {
if (groupId == null || groupId === '') {
console.log("--------popup----------getFrom DB-------with---no--groupId ")
return getPromise()
}
if (Vue.ls.get(groupId)) {
console.log("---------popup--------getFrom Cache--------groupId = " + groupId)
return Promise.resolve(Vue.ls.get(groupId));
} else {
console.log("--------popup----------getFrom DB---------groupId = " + groupId)
}
// 还没有发出请求,就发出第一次的请求
return getPromise().then(res => {
Vue.ls.set(groupId, res, expire);
return Promise.resolve(res);
})
}

View File

@ -1,16 +1,11 @@
import { getAction,deleteAction,putAction,postAction} from '@/api/manage'
import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
import Vue from 'vue'
import {UI_CACHE_DB_DICT_DATA } from "@/store/mutation-types"
////根路径
// const doMian = "/jeecg-boot/";
////图片预览请求地址
// const imgView = "http://localhost:8080/jeecg-boot/sys/common/view/";
//角色管理
const addRole = (params)=>postAction("/sys/role/add",params);
const editRole = (params)=>putAction("/sys/role/edit",params);
// const getRoleList = (params)=>getAction("/sys/role/list",params);
// const deleteRole = (params)=>deleteAction("/sys/role/delete",params);
// const deleteRoleList = (params)=>deleteAction("/sys/role/deleteBatch",params);
const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
const queryall = (params)=>getAction("/sys/role/queryall",params);
@ -19,26 +14,25 @@ const addUser = (params)=>postAction("/sys/user/add",params);
const editUser = (params)=>putAction("/sys/user/edit",params);
const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
const getUserList = (params)=>getAction("/sys/user/list",params);
// const deleteUser = (params)=>deleteAction("/sys/user/delete",params);
// const deleteUserList = (params)=>deleteAction("/sys/user/deleteBatch",params);
const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
//验证用户是否存在
const checkOnlyUser = (params)=>getAction("/sys/user/checkOnlyUser",params);
//改变密码
const changPassword = (params)=>putAction("/sys/user/changPassword",params);
const changePassword = (params)=>putAction("/sys/user/changePassword",params);
//权限管理
const addPermission= (params)=>postAction("/sys/permission/add",params);
const editPermission= (params)=>putAction("/sys/permission/edit",params);
const getPermissionList = (params)=>getAction("/sys/permission/list",params);
// const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
// const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",params);
const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
//const queryPermissionsByUser = (params)=>getAction("/sys/permission/queryByUser",params);
const queryPermissionsByUser = (params)=>getAction("/sys/permission/getUserPermissionByToken",params);
const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
@ -51,25 +45,35 @@ const queryParentName = (params)=>getAction("/sys/sysDepart/queryParentName",p
const searchByKeywords = (params)=>getAction("/sys/sysDepart/searchBy",params);
const deleteByDepartId = (params)=>deleteAction("/sys/sysDepart/delete",params);
//二级部门管理
const queryDepartPermission = (params)=>getAction("/sys/permission/queryDepartPermission",params);
const saveDepartPermission = (params)=>postAction("/sys/permission/saveDepartPermission",params);
const queryTreeListForDeptRole = (params)=>getAction("/sys/sysDepartPermission/queryTreeListForDeptRole",params);
const queryDeptRolePermission = (params)=>getAction("/sys/sysDepartPermission/queryDeptRolePermission",params);
const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/saveDeptRolePermission",params);
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);
//数据字典
const addDict = (params)=>postAction("/sys/dict/add",params);
const editDict = (params)=>putAction("/sys/dict/edit",params);
//const getDictList = (params)=>getAction("/sys/dict/list",params);
const treeList = (params)=>getAction("/sys/dict/treeList",params);
// const delDict = (params)=>deleteAction("/sys/dict/delete",params);
//const getDictItemList = (params)=>getAction("/sys/dictItem/list",params);
const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
//const delDictItem = (params)=>deleteAction("/sys/dictItem/delete",params);
//const delDictItemList = (params)=>deleteAction("/sys/dictItem/deleteBatch",params);
//字典标签专用通过code获取字典数组
export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
//从缓存中获取字典配置
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)
return dictItems;
}
}
//系统通告
const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",params);
@ -77,20 +81,18 @@ const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",param
//获取系统访问量
const getLoginfo = (params)=>getAction("/sys/loginfo",params);
const getVisitInfo = (params)=>getAction("/sys/visitInfo",params);
//数据日志访问
// const getDataLogList = (params)=>getAction("/sys/dataLog/list",params);
// 根据部门主键查询用户信息
const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
// 查询用户角色表里的所有信息
const queryUserRoleMap = (params)=>getAction("/sys/user/queryUserRoleMap",params);
// 重复校验
const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
// 加载分类字典
const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
const checkRuleByCode = (params) => getAction('/sys/checkRule/checkByCode', params)
//我的通告
const getUserNoticeInfo= (params)=>getAction("/sys/sysAnnouncementSend/getMyAnnouncementSend",params);
export {
// imgView,
// doMian,
addRole,
editRole,
checkRoleCode,
@ -101,7 +103,7 @@ export {
queryall,
frozenBatch,
checkOnlyUser,
changPassword,
changePassword,
getPermissionList,
addPermission,
editPermission,
@ -130,9 +132,21 @@ export {
getLoginfo,
getVisitInfo,
queryUserByDepId,
queryUserRoleMap,
duplicateCheck,
queryTreeListForRole,
getSystemMenuList,
getSystemSubmenu,
getSystemSubmenuBatch,
loadCategoryData,
checkRuleByCode,
queryDepartPermission,
saveDepartPermission,
queryTreeListForDeptRole,
queryDeptRolePermission,
saveDeptRolePermission,
queryMyDepartTreeList,
getUserNoticeInfo,
getDictItemsFromCache
}

View File

@ -55,4 +55,19 @@ export function logout(logoutToken) {
'X-Access-Token': logoutToken
}
})
}
/**
* 第三方登录
* @param token
* @returns {*}
*/
export function thirdLogin(token) {
return axios({
url: `/thirdLogin/getLoginUser/${token}`,
method: 'get',
headers: {
'Content-Type': 'application/json;charset=UTF-8'
}
})
}

View File

@ -1,3 +1,4 @@
import Vue from 'vue'
import { axios } from '@/utils/request'
const api = {
@ -112,3 +113,65 @@ export function downFile(url,parameter){
})
}
/**
* 下载文件
* @param url 文件路径
* @param fileName 文件名
* @param parameter
* @returns {*}
*/
export function downloadFile(url, fileName, parameter) {
return downFile(url, parameter).then((data) => {
if (!data || data.size === 0) {
Vue.prototype['$message'].warning('文件下载失败')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName)
} else {
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) //下载完成移除元素
window.URL.revokeObjectURL(url) //释放掉blob对象
}
})
}
/**
* 文件上传 用于富文本上传图片
* @param url
* @param parameter
* @returns {*}
*/
export function uploadAction(url,parameter){
return axios({
url: url,
data: parameter,
method:'post' ,
headers: {
'Content-Type': 'multipart/form-data', // 文件上传
},
})
}
/**
* 获取文件服务访问路径
* @param avatar
* @param subStr
* @returns {*}
*/
export function getFileAccessHttpUrl(avatar,subStr) {
if(!subStr) subStr = 'http'
if(avatar && avatar.startsWith(subStr)){
return avatar;
}else{
if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
return window._CONFIG['staticDomainURL'] + "/" + avatar;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,259 @@
.area-zoom-in-top-enter-active,
.area-zoom-in-top-leave-active {
opacity: 1;
transform: scaleY(1);
}
.area-zoom-in-top-enter,
.area-zoom-in-top-leave-active {
opacity: 0;
transform: scaleY(0);
}
.area-select {
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
list-style: none;
font-feature-settings: 'tnum';
position: relative;
outline: 0;
display: block;
background-color: #fff;
border: 1px solid #d9d9d9;
border-top-width: 1.02px;
border-radius: 4px;
outline: none;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
-webkit-user-select: none;
-ms-user-select: none;
user-select: none;
}
.area-select-wrap .area-select {
display: inline-block;
}
.area-select * {
box-sizing: border-box;
}
.area-select:hover {
border-color: #40a9ff;
border-right-width: 1px !important;
outline: 0;
}
.area-select:active {
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
}
.area-select.small {
width: 126px;
}
.area-select.medium {
width: 160px;
}
.area-select.large {
width: 194px;
}
.area-select.is-disabled {
background: #eceff5;
cursor: not-allowed;
}
.area-select.is-disabled:hover {
border-color: #e1e2e6;
}
.area-select.is-disabled .area-selected-trigger {
cursor: not-allowed;
}
.area-select .area-selected-trigger {
position: relative;
display: block;
font-size: 14px;
cursor: pointer;
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
height: 100%;
padding: 8px 20px 7px 12px;
}
.area-select .area-select-icon {
position: absolute;
top: 50%;
margin-top: -2px;
right: 6px;
content: "";
width: 0;
height: 0;
border: 6px solid transparent;
border-top-color: rgba(0, 0, 0, 0.25);
transition: all .3s linear;
transform-origin: center;
}
.area-select .area-select-icon.active {
margin-top: -8px;
transform: rotate(180deg);
}
.area-selectable-list-wrap {
position: absolute;
width: 100%;
max-height: 275px;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow-x: auto;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.area-selectable-list {
position: relative;
margin: 0;
padding: 6px 0;
width: 100%;
font-size: 14px;
color: #565656;
list-style: none;
}
.area-selectable-list .area-select-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.area-selectable-list .area-select-option.hover {
background-color: #e6f7ff;
}
.area-selectable-list .area-select-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list-wrap {
position: absolute;
white-space: nowrap;
z-index: 15000;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
font-size: 0;
margin: 2px 0;
border-radius: 4px;
outline: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
transition: opacity 0.15s, transform 0.3s !important;
transform-origin: center top !important;
}
.cascader-menu-list {
position: relative;
margin: 0;
font-size: 14px;
color: #565656;
padding: 6px 0;
list-style: none;
display: inline-block;
height: 204px;
overflow-x: hidden;
overflow-y: auto;
min-width: 160px;
vertical-align: top;
background-color: #fff;
border-right: 1px solid #e4e7ed;
}
.cascader-menu-list:last-child {
border-right: none;
}
.cascader-menu-list .cascader-menu-option {
position: relative;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
padding: 0 15px 0 10px;
height: 32px;
line-height: 32px;
}
.cascader-menu-list .cascader-menu-option.hover,
.cascader-menu-list .cascader-menu-option:hover {
background-color: #e6f7ff;
}
.cascader-menu-list .cascader-menu-option.selected {
color: rgba(0, 0, 0, 0.65);
font-weight: 600;
background-color: #efefef;
}
.cascader-menu-list .cascader-menu-option.cascader-menu-extensible:after {
position: absolute;
top: 50%;
margin-top: -4px;
right: 5px;
content: "";
width: 0;
height: 0;
border: 4px solid transparent;
border-left-color: #a1a4ad;
}
.cascader-menu-list::-webkit-scrollbar,
.area-selectable-list-wrap::-webkit-scrollbar {
width: 8px;
background: transparent;
}
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:decremen,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:end:decrement,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:increment,
.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:start:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:decremen,
.cascader-menu-list::-webkit-scrollbar-button:vertical:end:decrement,
.cascader-menu-list::-webkit-scrollbar-button:vertical:increment,
.cascader-menu-list::-webkit-scrollbar-button:vertical:start:increment {
display: none;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical {
background-color: #b8b8b8;
border-radius: 4px;
}
.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
background-color: #777;
}

View File

@ -0,0 +1,15 @@
/** [表格主题样式一] 表格强制列不换行 */
.j-table-force-nowrap {
td, th {
white-space: nowrap;
}
.ant-table-selection-column {
padding: 12px 22px !important;
}
/** 列自适应,弊端会导致列宽失效 */
&.ant-table-wrapper .ant-table-content {
overflow-x: auto;
}
}

View File

@ -1,11 +1,18 @@
/*列表上方操作按钮区域*/
.ant-card-body .table-operator {
margin-bottom: 18px;
margin-bottom: 8px;
}
/** Button按钮间距 */
.table-operator .ant-btn {
margin-right: 6px
margin: 0 8px 8px 0;
}
.table-operator .ant-btn-group .ant-btn {
margin: 0;
}
.table-operator .ant-btn-group .ant-btn:last-child {
margin: 0 8px 8px 0;
}
/*列表td的padding设置 可以控制列表大小*/
.ant-table-tbody .ant-table-row td {
@ -40,3 +47,12 @@
/*列表中范围查询样式*/
.query-group-cust{width: calc(50% - 10px)}
.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}
/*erp风格子表外框padding设置*/
.ant-card-wider-padding.cust-erp-sub-tab>.ant-card-body{padding:5px 12px}
/* 内嵌子表背景颜色 */
.j-inner-table-wrapper /deep/ .ant-table-expanded-row .ant-table-wrapper .ant-table-tbody .ant-table-row {
background-color: #FFFFFF;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -8,14 +8,14 @@ const init = (callback) => {
console.log("-------单点登录开始-------");
let token = Vue.ls.get(ACCESS_TOKEN);
let st = getUrlParam("ticket");
var sevice = "http://"+window.location.host+"/";
let sevice = "http://"+window.location.host+"/";
if(token){
loginSuccess(callback);
}else{
if(st){
validateSt(st,sevice,callback);
}else{
var serviceUrl = encodeURIComponent(sevice);
let serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}
@ -26,14 +26,14 @@ const SSO = {
};
function getUrlParam(paraName) {
var url = document.location.toString();
var arrObj = url.split("?");
let url = document.location.toString();
let arrObj = url.split("?");
if (arrObj.length > 1) {
var arrPara = arrObj[1].split("&");
var arr;
let arrPara = arrObj[1].split("&");
let arr;
for (var i = 0; i < arrPara.length; i++) {
for (let i = 0; i < arrPara.length; i++) {
arr = arrPara[i].split("=");
if (arr != null && arr[0] == paraName) {
@ -57,8 +57,8 @@ function validateSt(ticket,service,callback){
if(res.success){
loginSuccess(callback);
}else{
var sevice = "http://"+window.location.host+"/";
var serviceUrl = encodeURIComponent(sevice);
let sevice = "http://"+window.location.host+"/";
let serviceUrl = encodeURIComponent(sevice);
window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
}
}).catch((err) => {

View File

@ -42,7 +42,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.chart-card-header {
position: relative;
overflow: hidden;

View File

@ -1,31 +1,20 @@
<script>
import Tooltip from 'ant-design-vue/es/tooltip'
import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'
/*
const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined;
const TooltipOverlayStyle = {
overflowWrap: 'break-word',
wordWrap: 'break-word',
};
*/
export default {
name: 'Ellipsis',
components: {
Tooltip
},
props: {
prefixCls: {
type: String,
default: 'ant-pro-ellipsis'
},
tooltip: {
type: Boolean
type: Boolean,
default: true,
},
length: {
type: Number,
required: true
default: 25,
},
lines: {
type: Number,
@ -36,28 +25,25 @@
default: false
}
},
methods: {
getStrDom (str) {
return (
<span>{ cutStrByFullLength(str, this.length) + '...' }</span>
)
},
getTooltip ( fullStr) {
return (
<Tooltip>
<template slot="title">{ fullStr }</template>
{ this.getStrDom(fullStr) }
</Tooltip>
)
}
},
render () {
methods: {},
render() {
const { tooltip, length } = this.$props
let str = this.$slots.default.map(vNode => vNode.text).join("")
const strDom = tooltip && getStrFullLength(str) > length ? this.getTooltip(str) : this.getStrDom(str);
return (
strDom
)
let text = ''
// 处理没有default插槽时的特殊情况
if (this.$slots.default) {
text = this.$slots.default.map(vNode => vNode.text).join('')
}
// 判断是否显示 tooltip
if (tooltip && getStrFullLength(text) > length) {
return (
<a-tooltip>
<template slot="title">{text}</template>
<span>{cutStrByFullLength(text, this.length) + '…'}</span>
</a-tooltip>
)
} else {
return (<span>{text}</span>)
}
}
}
</script>

View File

@ -0,0 +1,79 @@
import { pcaa } from 'area-data'
/**
* 省市区
*/
export default class Area {
/**
* 构造器
* @param express
*/
constructor() {
let arr = []
const province = pcaa['86']
Object.keys(province).map(key=>{
arr.push({id:key, text:province[key], pid:'86'});
const city = pcaa[key];
Object.keys(city).map(key2=>{
arr.push({id:key2, text:city[key2], pid:key});
const qu = pcaa[key2];
Object.keys(qu).map(key3=>{
arr.push({id:key3, text:qu[key3], pid:key2});
})
})
})
this.all = arr;
}
get pca(){
return this.all;
}
getCode(text){
if(!text || text.length==0){
return ''
}
for(let item of this.all){
if(item.text === text){
return item.id;
}
}
}
getText(code){
if(!code || code.length==0){
return ''
}
let arr = []
this.getAreaBycode(code,arr);
return arr.join('/')
}
getRealCode(code){
let arr = []
this.getPcode(code, arr)
return arr;
}
getPcode(id, arr){
for(let item of this.all){
if(item.id === id){
arr.unshift(id)
if(item.pid != '86'){
this.getPcode(item.pid,arr)
}
}
}
}
getAreaBycode(code,arr){
//console.log("this.all.length",this.all)
for(let item of this.all){
if(item.id === code){
arr.unshift(item.text);
this.getAreaBycode(item.pid,arr)
}
}
}
}

View File

@ -83,6 +83,6 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
@import "chart";
</style>

View File

@ -1,7 +1,7 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<div :style="{ padding: '0 50px 32px 0' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :padding=" padding" :onClick="handleClick">
<v-tooltip/>
<v-legend/>
<v-axis/>
@ -12,9 +12,11 @@
</template>
<script>
import { ChartEventMixins } from './mixins/ChartMixins'
export default {
name: 'BarMultid',
name: 'BarAndLine',
mixins: [ChartEventMixins],
props: {
title: {
type: String,
@ -23,13 +25,13 @@
dataSource: {
type: Array,
default: () => [
{ type: '10:10', bar: 2, line: 2 },
{ type: '10:15', bar: 6, line: 3 },
{ type: '10:20', bar: 2, line: 5 },
{ type: '10:25', bar: 9, line: 1 },
{ type: '10:30', bar: 2, line: 3 },
{ type: '10:35', bar: 2, line: 1 },
{ type: '10:40', bar: 1, line: 2 }
{ type: '10:10', bar: 200, line: 1000 },
{ type: '10:15', bar: 600, line: 1000},
{ type: '10:20', bar: 200, line: 1000},
{ type: '10:25', bar: 900, line: 1000},
{ type: '10:30', bar: 200, line: 1000},
{ type: '10:35', bar: 200, line: 1000},
{ type: '10:40', bar: 100, line: 1000}
]
},
height: {
@ -39,6 +41,7 @@
},
data() {
return {
padding: { top:50, right:50, bottom:100, left:50 },
scale: [{
dataKey: 'bar',
min: 0

View File

@ -1,36 +1,43 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :forceFit="true" :height="height" :data="data">
<v-tooltip />
<v-axis />
<v-legend />
<v-bar position="x*y" color="type" :adjust="adjust" />
<v-chart :data="data" :height="height" :force-fit="true" :onClick="handleClick">
<v-tooltip/>
<v-axis/>
<v-legend/>
<v-bar position="x*y" color="type" :adjust="adjust"/>
</v-chart>
</div>
</template>
<script>
import { DataSet } from '@antv/data-set'
import { ChartEventMixins } from './mixins/ChartMixins'
export default {
name: 'BarMultid',
mixins: [ChartEventMixins],
props: {
title: {
type: String,
default: ''
},
dataSource:{
dataSource: {
type: Array,
default: () => [
{ type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
{ type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
]
},
fields:{
fields: {
type: Array,
default: () => ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.']
},
// 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
aliases: {
type: Array,
default: () => []
},
height: {
type: Number,
default: 254
@ -55,11 +62,22 @@
})
// bar 使用不了 - 和 / 所以替换下
return dv.rows.map(row => {
row.x = row.x.replace(/[-/]/g, '_')
let rows = dv.rows.map(row => {
if (typeof row.x === 'string') {
row.x = row.x.replace(/[-/]/g, '_')
}
return row
})
// 替换别名
rows.forEach(row => {
for (let item of this.aliases) {
if (item.field === row.type) {
row.type = item.alias
break
}
}
})
return rows
}
}
}

View File

@ -1,6 +1,6 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<v-chart :forceFit="true" :height="350" :data="chartData" :scale="scale">
<v-chart :forceFit="true" :height="300" :data="chartData" :scale="scale">
<v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
<v-axis
dataKey="value"
@ -32,7 +32,7 @@
type="arc"
:zIndex="1"
:start="arcGuide2Start"
:end="getArcGuide2End()"
:end="getArcGuide2End"
:vStyle="arcGuide2Style"
></v-guide>
<v-guide
@ -88,7 +88,7 @@
}];
const data = [
{ value: 0},
{ value: 7.0 },
];
export default {
@ -96,7 +96,7 @@
props:{
datasource:{
type: Number,
default:0
default:7
},
title: {
type: String,

View File

@ -0,0 +1,61 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart
height="254"
:data="datasource"
:forceFit="true"
:padding="['auto', 'auto', '40', '50']">
<v-tooltip />
<v-axis />
<v-bar position="x*y"/>
</v-chart>
</div>
</template>
<script>
const data = []
for (let i = 0; i < 12; i += 1) {
data.push({
x: `${i + 1}`,
y: Math.floor(Math.random() * 1000) + 200
})
}
const tooltip = [
'x*y',
(x, y) => ({
name: x,
value: y
})
]
const scale = [{
dataKey: 'x',
min: 2
}, {
dataKey: 'y',
title: '时间',
min: 1,
max: 22
}]
export default {
name: "Bar",
props: {
title: {
type: String,
default: ''
}
},
mounted(){
this.datasource = data
},
data () {
return {
datasource:[],
scale,
tooltip
}
}
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<div :style="{ padding: '0 0 32px 32px' }">
<h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale">
<v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
<v-tooltip/>
<v-axis/>
<v-legend/>
@ -13,9 +13,11 @@
<script>
import { DataSet } from '@antv/data-set'
import { ChartEventMixins } from './mixins/ChartMixins'
export default {
name: 'LineChartMultid',
mixins: [ChartEventMixins],
props: {
title: {
type: String,
@ -42,6 +44,11 @@
type: Array,
default: () => ['jeecg', 'jeebt']
},
// 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
aliases:{
type: Array,
default: () => []
},
height: {
type: Number,
default: 254
@ -66,7 +73,17 @@
key: 'x',
value: 'y'
})
return dv.rows
let rows = dv.rows
// 替换别名
rows.forEach(row => {
for (let item of this.aliases) {
if (item.field === row.x) {
row.x = item.alias
break
}
}
})
return rows
}
}
}

View File

@ -64,6 +64,6 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
@import "chart";
</style>

View File

@ -71,6 +71,6 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
@import "chart";
</style>

View File

@ -34,7 +34,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.chart-mini-progress {
padding: 5px 0;
position: relative;

View File

@ -1,5 +1,5 @@
<template>
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale">
<v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
<v-tooltip :showTitle="false" dataKey="item*percent"/>
<v-axis/>
<v-legend dataKey="item"/>
@ -10,8 +10,11 @@
<script>
const DataSet = require('@antv/data-set')
import { ChartEventMixins } from './mixins/ChartMixins'
export default {
name: 'Pie',
mixins: [ChartEventMixins],
props: {
title: {
type: String,

View File

@ -32,7 +32,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.rank {
padding: 0 32px 32px 72px;

View File

@ -0,0 +1,54 @@
<template>
<div>
<v-chart :forceFit="true" :height="height" :data="data">
<v-coord type="rect" direction="LB" />
<v-tooltip />
<v-legend />
<v-axis dataKey="State" :label="label" />
<v-stack-bar position="State*流程数量" color="流程状态" />
</v-chart>
</div>
</template>
<script>
const DataSet = require('@antv/data-set');
export default {
name: 'StackBar',
props: {
dataSource: {
type: Array,
required: true,
default: () => [
{ 'State': '请假', '流转中': 25, '已归档': 18 },
{ 'State': '出差', '流转中': 30, '已归档': 20 },
{ 'State': '加班', '流转中': 38, '已归档': 42},
{ 'State': '用车', '流转中': 51, '已归档': 67}
]
},
height: {
type: Number,
default: 254
}
},
data() {
return {
label: { offset: 12 }
}
},
computed: {
data() {
const dv = new DataSet.View().source(this.dataSource);
dv.transform({
type: 'fold',
fields: ['流转中', '已归档'],
key: '流程状态',
value: '流程数量',
retains: ['State'],
});
return dv.rows;
}
}
}
</script>

View File

@ -52,7 +52,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.chart-trend {
display: inline-block;
font-size: 14px;

View File

@ -0,0 +1,10 @@
export const ChartEventMixins = {
methods: {
handleClick(event, chart) {
this.handleEvent('click', event, chart)
},
handleEvent(eventName, event, chart) {
this.$emit(eventName, event, chart)
},
}
}

View File

@ -1,10 +1,14 @@
<template>
<a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="value" :disabled="disabled">
<a-radio-group v-if="tagType=='radio'" @change="handleInput" :value="getValueSting" :disabled="disabled">
<a-radio v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio>
</a-radio-group>
<a-select v-else-if="tagType=='select'" :placeholder="placeholder" :disabled="disabled" :value="value" @change="handleInput">
<a-select-option value="">请选择</a-select-option>
<a-radio-group v-else-if="tagType=='radioButton'" buttonStyle="solid" @change="handleInput" :value="getValueSting" :disabled="disabled">
<a-radio-button v-for="(item, key) in dictOptions" :key="key" :value="item.value">{{ item.text }}</a-radio-button>
</a-radio-group>
<a-select v-else-if="tagType=='select'" :getPopupContainer = "(target) => target.parentNode" :placeholder="placeholder" :disabled="disabled" :value="getValueSting" @change="handleInput">
<a-select-option :value="undefined">请选择</a-select-option>
<a-select-option v-for="(item, key) in dictOptions" :key="key" :value="item.value">
<span style="display: inline-block;width: 100%" :title=" item.text || item.label ">
{{ item.text || item.label }}
@ -14,7 +18,7 @@
</template>
<script>
import {ajaxGetDictItems} from '@/api/api'
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
export default {
name: "JDictSelectTag",
@ -23,7 +27,7 @@
placeholder: String,
triggerChange: Boolean,
disabled: Boolean,
value: String,
value: [String, Number],
type: String
},
data() {
@ -32,18 +36,37 @@
tagType:""
}
},
watch:{
dictCode:{
immediate:true,
handler() {
this.initDictData()
},
}
},
created() {
console.log(this.dictCode);
// console.log(this.dictCode);
if(!this.type || this.type==="list"){
this.tagType = "select"
}else{
this.tagType = this.type
}
//获取字典数据
this.initDictData();
// this.initDictData();
},
computed: {
getValueSting(){
return this.value != null ? this.value.toString() : null;
},
},
methods: {
initDictData() {
//优先从缓存中读取字典配置
if(getDictItemsFromCache(this.dictCode)){
this.dictOptions = getDictItemsFromCache(this.dictCode);
return
}
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dictCode, null).then((res) => {
if (res.success) {

View File

@ -4,7 +4,7 @@
* date: 20190109
*/
import {ajaxGetDictItems} from '@/api/api'
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
import {getAction} from '@/api/manage'
/**
@ -16,6 +16,13 @@ export async function initDictOptions(dictCode) {
if (!dictCode) {
return '字典Code不能为空!';
}
//优先从缓存中读取字典配置
if(getDictItemsFromCache(dictCode)){
let res = {}
res.result = getDictItemsFromCache(dictCode);
res.success = true;
return res;
}
//获取字典数组
let res = await ajaxGetDictItems(dictCode);
return res;
@ -28,13 +35,25 @@ export async function initDictOptions(dictCode) {
* @return String
*/
export function filterDictText(dictOptions, text) {
let re = "";
dictOptions.forEach(function (option) {
if (text === option.value) {
re = option.text;
// --update-begin----author:sunjianlei---date:20200323------for: 字典翻译 text 允许逗号分隔 ---
if (text != null && dictOptions instanceof Array) {
let result = []
// 允许多个逗号分隔
let splitText = text.toString().trim().split(',')
for (let txt of splitText) {
let dictText = txt
for (let dictItem of dictOptions) {
if (txt === dictItem.value.toString()) {
dictText = dictItem.text
break
}
}
result.push(dictText)
}
});
return re;
return result.join(',')
}
return text
// --update-end----author:sunjianlei---date:20200323------for: 字典翻译 text 允许逗号分隔 ---
}
/**
@ -44,21 +63,35 @@ export function filterDictText(dictOptions, text) {
* @return String
*/
export function filterMultiDictText(dictOptions, text) {
if(!text){
//js “!text” 认为0为空所以做提前处理
if(text === 0 || text === '0'){
if(dictOptions){
for (let dictItem of dictOptions) {
if (text == dictItem.value) {
return dictItem.text
}
}
}
}
if(!text || text=='null' || !dictOptions || dictOptions.length==0){
return ""
}
let re = "";
text = text.toString()
let arr = text.split(",")
dictOptions.forEach(function (option) {
for(let i=0;i<arr.length;i++){
if (arr[i] === option.value) {
re += option.text+",";
break;
if(option){
for(let i=0;i<arr.length;i++){
if (arr[i] === option.value) {
re += option.text+",";
break;
}
}
}
});
if(re==""){
return "";
return text;
}
return re.substring(0,re.length-1);
}
@ -68,20 +101,42 @@ export function filterMultiDictText(dictOptions, text) {
* @param children
* @returns string
*/
export async function ajaxFilterDictText(dictCode, key) {
export function filterDictTextByCache(dictCode, key) {
if(key==null ||key.length==0){
return;
}
if (!dictCode) {
return '字典Code不能为空!';
}
//console.log(`key : ${key}`);
if (!key) {
return '';
}
//通过请求读取字典文本
let res = await getAction(`/sys/dict/getDictText/${dictCode}/${key}`);
if (res.success) {
// console.log('restult: '+ res.result);
return res.result;
} else {
return '';
//优先从缓存中读取字典配置
if(getDictItemsFromCache(dictCode)){
let item = getDictItemsFromCache(dictCode).filter(t => t["value"] == key)
if(item && item.length>0){
return item[0]["text"]
}
}
}
/** 通过code获取字典数组 */
export async function getDictItems(dictCode, params) {
//优先从缓存中读取字典配置
if(getDictItemsFromCache(dictCode)){
let desformDictItems = getDictItemsFromCache(dictCode).map(item => ({...item, label: item.text}))
return desformDictItems;
}
//缓存中没有,就请求后台
return await ajaxGetDictItems(dictCode, params).then(({success, result}) => {
if (success) {
let res = result.map(item => ({...item, label: item.text}))
console.log('------- 从DB中获取到了字典-------dictCode : ', dictCode, res)
return Promise.resolve(res)
} else {
console.error('getDictItems error: : ', res)
return Promise.resolve([])
}
}).catch((res) => {
console.error('getDictItems error: ', res)
return Promise.resolve([])
})
}

View File

@ -9,7 +9,9 @@
@change="onChange"
:disabled="disabled"
mode="multiple"
:placeholder="placeholder">
:placeholder="placeholder"
:getPopupContainer="(node) => node.parentNode"
allowClear>
<a-select-option
v-for="(item,index) in dictOptions"
:key="index"
@ -23,7 +25,7 @@
</template>
<script>
import {ajaxGetDictItems} from '@/api/api'
import {ajaxGetDictItems,getDictItemsFromCache} from '@/api/api'
export default {
name: 'JMultiSelectTag',
props: {
@ -48,12 +50,18 @@
this.tagType = this.type
}
//获取字典数据
this.initDictData();
//this.initDictData();
},
watch:{
options: function(val){
this.setCurrentDictOptions(val);
},
dictCode:{
immediate:true,
handler() {
this.initDictData()
},
},
value (val) {
if(!val){
this.arrayValue = []
@ -67,6 +75,11 @@
if(this.options && this.options.length>0){
this.dictOptions = [...this.options]
}else{
//优先从缓存中读取字典配置
if(getDictItemsFromCache(this.dictCode)){
this.dictOptions = getDictItemsFromCache(this.dictCode);
return
}
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dictCode, null).then((res) => {
if (res.success) {

View File

@ -4,12 +4,15 @@
v-if="async"
showSearch
labelInValue
:disabled="disabled"
:getPopupContainer="(node) => node.parentNode"
@search="loadData"
:placeholder="placeholder"
v-model="selectedAsyncValue"
style="width: 100%"
:filterOption="false"
@change="handleAsyncChange"
allowClear
:notFoundContent="loading ? undefined : null"
>
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
@ -18,13 +21,16 @@
<a-select
v-else
:getPopupContainer="(node) => node.parentNode"
showSearch
:disabled="disabled"
:placeholder="placeholder"
optionFilterProp="children"
style="width: 100%"
@change="handleChange"
:filterOption="filterOption"
v-model="selectedValue"
allowClear
:notFoundContent="loading ? undefined : null">
<a-spin v-if="loading" slot="notFoundContent" size="small"/>
<a-select-option v-for="d in options" :key="d.value" :value="d.value">{{ d.text }}</a-select-option>
@ -33,7 +39,7 @@
</template>
<script>
import { ajaxGetDictItems } from '@/api/api'
import { ajaxGetDictItems,getDictItemsFromCache } from '@/api/api'
import debounce from 'lodash/debounce';
import { getAction } from '../../api/manage'
@ -41,7 +47,7 @@
name: 'JSearchSelectTag',
props:{
disabled: Boolean,
value: String,
value: [String, Number],
dict: String,
dictOptions: Array,
async: Boolean,
@ -69,8 +75,12 @@
immediate:true,
handler(val){
if(!val){
this.selectedValue=[]
this.selectedAsyncValue=[]
if(val==0){
this.initSelectValue()
}else{
this.selectedValue=[]
this.selectedAsyncValue=[]
}
}else{
this.initSelectValue()
}
@ -98,7 +108,7 @@
})
}
}else{
this.selectedValue = this.value
this.selectedValue = this.value.toString()
}
},
loadData(value){
@ -130,11 +140,28 @@
this.options = [...this.dictOptions]
}else{
//根据字典Code, 初始化字典数组
ajaxGetDictItems(this.dict, null).then((res) => {
if (res.success) {
this.options = res.result;
}
})
let dictStr = ''
if(this.dict){
let arr = this.dict.split(',')
if(arr[0].indexOf('where')>0){
let tbInfo = arr[0].split('where')
dictStr = tbInfo[0].trim()+','+arr[1]+','+arr[2]+','+encodeURIComponent(tbInfo[1])
}else{
dictStr = this.dict
}
if (this.dict.indexOf(",") == -1) {
//优先从缓存中读取字典配置
if (getDictItemsFromCache(this.dictCode)) {
this.options = getDictItemsFromCache(this.dictCode);
return
}
}
ajaxGetDictItems(dictStr, null).then((res) => {
if (res.success) {
this.options = res.result;
}
})
}
}
}
},

View File

@ -0,0 +1,156 @@
<template>
<div v-if="!reloading" class="j-area-linkage">
<area-cascader
v-if="_type === enums.type[0]"
:value="innerValue"
:data="pcaa"
:level="1"
:style="{width}"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
/>
<area-select
v-else-if="_type === enums.type[1]"
:value="innerValue"
:data="pcaa"
:level="2"
v-bind="$attrs"
v-on="_listeners"
@change="handleChange"
/>
<div v-else>
<span style="color:red;"> Bad type value: {{_type}}</span>
</div>
</div>
</template>
<script>
import { pcaa } from 'area-data'
import Area from '@/components/_util/Area'
export default {
name: 'JAreaLinkage',
props: {
value: {
type: String,
required:false
},
// 组件的类型,可选值:
// select 下拉样式
// cascader 级联样式(默认)
type: {
type: String,
default: 'cascader'
},
width: {
type: String,
default: '100%'
}
},
data() {
return {
pcaa,
innerValue: [],
usedListeners: ['change'],
enums: {
type: ['cascader', 'select']
},
reloading: false,
areaData:''
}
},
computed: {
_listeners() {
let listeners = { ...this.$listeners }
// 去掉已使用的事件,防止冲突
this.usedListeners.forEach(key => {
delete listeners[key]
})
return listeners
},
_type() {
if (this.enums.type.includes(this.type)) {
return this.type
} else {
console.error(`JAreaLinkage的type属性只能接收指定的值${this.enums.type.join('|')}`)
return this.enums.type[0]
}
},
},
watch: {
value: {
immediate: true,
handler() {
this.loadDataByValue(this.value)
}
},
},
created() {
this.initAreaData();
},
methods: {
/** 通过 value 反推 options */
loadDataByValue(value) {
if(!value || value.length==0){
this.innerValue = []
this.reloading = true;
setTimeout(()=>{
this.reloading = false
},100)
}else{
this.initAreaData();
let arr = this.areaData.getRealCode(value);
this.innerValue = arr
}
},
/** 通过地区code获取子级 */
loadDataByCode(value) {
let options = []
let data = pcaa[value]
if (data) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
options.push({ value: key, label: data[key], })
}
}
return options
} else {
return []
}
},
/** 判断是否有子节点 */
hasChildren(options) {
options.forEach(option => {
let data = this.loadDataByCode(option.value)
option.isLeaf = data.length === 0
})
},
handleChange(values) {
let value = values[values.length - 1]
this.$emit('change', value)
},
initAreaData(){
if(!this.areaData){
this.areaData = new Area();
}
},
},
model: { prop: 'value', event: 'change' },
}
</script>
<style lang="less" scoped>
.j-area-linkage {
height:40px;
/deep/ .area-cascader-wrap .area-select {
width: 100%;
}
/deep/ .area-select .area-selected-trigger {
line-height: 1.15;
}
}
</style>

View File

@ -0,0 +1,238 @@
<template>
<a-tree-select
allowClear
labelInValue
style="width: 100%"
:disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
:placeholder="placeholder"
:loadData="asyncLoadTreeData"
:value="treeValue"
:treeData="treeData"
:multiple="multiple"
@change="onChange">
</a-tree-select>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'JCategorySelect',
props: {
value:{
type: String,
required: false
},
placeholder:{
type: String,
default: '请选择',
required: false
},
disabled:{
type:Boolean,
default:false,
required:false
},
condition:{
type:String,
default:'',
required:false
},
// 是否支持多选
multiple: {
type: Boolean,
default: false,
},
loadTriggleChange:{
type: Boolean,
default: false,
required:false
},
pid:{
type:String,
default:'',
required:false
},
pcode:{
type:String,
default:'',
required:false
},
back:{
type:String,
default:'',
required:false
}
},
data () {
return {
treeValue:"",
treeData:[],
url:"/sys/category/loadTreeData",
view:'/sys/category/loadDictItem/',
tableName:"",
text:"",
code:"",
}
},
watch: {
value () {
this.loadItemByCode()
},
pcode(){
this.loadRoot();
}
},
created(){
this.validateProp().then(()=>{
this.loadRoot()
this.loadItemByCode()
})
},
methods: {
/**加载一级节点 */
loadRoot(){
let param = {
pid:this.pid,
pcode:!this.pcode?'0':this.pcode,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success && res.result){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.treeData = [...res.result]
}else{
console.log("树一级节点查询结果-else",res)
}
})
},
/** 数据回显*/
loadItemByCode(){
if(!this.value || this.value=="0"){
this.treeValue = []
}else{
getAction(this.view,{ids:this.value}).then(res=>{
if(res.success){
let values = this.value.split(',')
this.treeValue = res.result.map((item, index) => ({
key: values[index],
value: values[index],
label: item
}))
this.onLoadTriggleChange(res.result[0]);
}
})
}
},
onLoadTriggleChange(text){
//只有单选才会触发
if(!this.multiple && this.loadTriggleChange){
this.backValue(this.value,text)
}
},
backValue(value,label){
let obj = {}
if(this.back){
obj[this.back] = label
}
this.$emit('change', value, obj)
},
asyncLoadTreeData (treeNode) {
return new Promise((resolve) => {
if (treeNode.$vnode.children) {
resolve()
return
}
let pid = treeNode.$vnode.key
let param = {
pid:pid,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success){
for(let i of res.result){
i.value = i.key
if(i.leaf==false){
i.isLeaf=false
}else if(i.leaf==true){
i.isLeaf=true
}
}
this.addChildren(pid,res.result,this.treeData)
this.treeData = [...this.treeData]
}
resolve()
})
})
},
addChildren(pid,children,treeArray){
if(treeArray && treeArray.length>0){
for(let item of treeArray){
if(item.key == pid){
if(!children || children.length==0){
item.isLeaf=true
}else{
item.children = children
}
break
}else{
this.addChildren(pid,children,item.children)
}
}
}
},
onChange(value){
if(!value){
this.$emit('change', '');
this.treeValue = ''
} else if (value instanceof Array) {
//this.$emit('change', value.map(item => item.value).join(','))
//this.treeValue = value
} else {
this.backValue(value.value,value.label)
this.treeValue = value
}
},
getCurrTreeData(){
return this.treeData
},
validateProp(){
let mycondition = this.condition
return new Promise((resolve,reject)=>{
if(!mycondition){
resolve();
}else{
try {
let test=JSON.parse(mycondition);
if(typeof test == 'object' && test){
resolve()
}else{
this.$message.error("组件JTreeSelect-condition传值有误需要一个json字符串!")
reject()
}
} catch(e) {
this.$message.error("组件JTreeSelect-condition传值有误需要一个json字符串!")
reject()
}
}
})
}
},
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
model: {
prop: 'value',
event: 'change'
}
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<a-checkbox-group :options="options" :value="checkboxArray" @change="onChange" />
<a-checkbox-group :options="options" :value="checkboxArray" v-bind="$attrs" @change="onChange" />
</template>
<script>

View File

@ -1,6 +1,6 @@
<template>
<div v-bind="fullScreenParentProps">
<a-icon v-if="fullScreen" class="full-screen-icon" type="fullscreen" @click="()=>fullCoder=!fullCoder"/>
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
<div class="code-editor-cust full-screen-child">
<textarea ref="textarea"></textarea>
@ -91,6 +91,7 @@
return {
// 内部真实的内容
code: '',
iconType: 'fullscreen',
hasCode:false,
// 默认的语法类型
mode: 'javascript',
@ -155,6 +156,15 @@
}
},
watch: {
fullCoder:{
handler(value) {
if(value){
this.iconType="fullscreen-exit"
}else{
this.iconType="fullscreen"
}
}
},
// value: {
// immediate: false,
// handler(value) {
@ -228,10 +238,11 @@
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
this.coder = CodeMirror.fromTextArea(this.$refs.textarea, this.coderOptions)
// 编辑器赋值
this.coder.setValue(this.value || this.code)
if(this.value||this.code){
this.hasCode=true
this.coder.setValue(this.value || this.code)
}else{
this.coder.setValue('')
this.hasCode=false
}
// 支持双向绑定
@ -266,7 +277,13 @@
return this.code
},
setCodeContent(val){
this.coder.setValue(val)
setTimeout(()=>{
if(!val){
this.coder.setValue('')
}else{
this.coder.setValue(val)
}
},300)
},
// 获取当前语法类型
_getLanguage (language) {
@ -401,9 +418,12 @@
.full-screen-child {
min-height: 120px;
max-height: 320px;
overflow:hidden;
}
}
.CodeMirror-cursor{
height:18.4px !important;
}
</style>

View File

@ -18,9 +18,6 @@
value: {
required: false,
type: String,
default:()=>{
return '* * * * * ? *'
}
}
},
data(){

View File

@ -51,7 +51,7 @@
},
getCalendarContainer: {
type: Function,
default: () => document.body
default: (node) => node.parentNode
}
},
data () {

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
import Editor from '@tinymce/tinymce-vue'
import 'tinymce/themes/silver/theme'
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/media'
import 'tinymce/plugins/table'
import 'tinymce/plugins/lists'
@ -22,6 +23,7 @@
import 'tinymce/plugins/colorpicker'
import 'tinymce/plugins/textcolor'
import 'tinymce/plugins/fullscreen'
import { uploadAction,getFileAccessHttpUrl } from '@/api/manage'
export default {
components: {
Editor
@ -42,11 +44,12 @@
},
plugins: {
type: [String, Array],
default: 'lists image media table textcolor wordcount contextmenu fullscreen'
default: 'lists image link media table textcolor wordcount contextmenu fullscreen'
},
toolbar: {
type: [String, Array],
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists image media table | removeformat | fullscreen'
default: 'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | lists link unlink image media table | removeformat | fullscreen',
branding:false
}
},
data() {
@ -61,9 +64,23 @@
toolbar: this.toolbar,
branding: false,
menubar: false,
toolbar_drawer: false,
images_upload_handler: (blobInfo, success) => {
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
let formData = new FormData()
formData.append('file', blobInfo.blob(), blobInfo.filename());
formData.append('biz', "jeditor");
formData.append("jeditor","1");
uploadAction(window._CONFIG['domianURL']+"/sys/common/upload", formData).then((res) => {
if (res.success) {
if(res.message == 'local'){
const img = 'data:image/jpeg;base64,' + blobInfo.base64()
success(img)
}else{
let img = getFileAccessHttpUrl(res.message)
success(img)
}
}
})
}
},
myValue: this.value
@ -87,12 +104,9 @@
this.myValue = (newValue == null ? '' : newValue)
},
myValue(newValue) {
console.log(newValue)
if(this.triggerChange){
console.log(1)
this.$emit('change', newValue)
}else{
console.log(2)
this.$emit('input', newValue)
}
}

View File

@ -1,12 +1,13 @@
<template>
<div v-if="disabled" class="jeecg-form-container-disabled">
<div :class="disabled?'jeecg-form-container-disabled':''">
<fieldset :disabled="disabled">
<slot name="detail"></slot>
</fieldset>
<slot name="edit"></slot>
<fieldset disabled>
<slot></slot>
</fieldset>
</div>
<div v-else>
<slot></slot>
</div>
</template>
<script>
@ -45,4 +46,16 @@
-ms-pointer-events: none;
pointer-events: none;
}
.jeecg-form-container-disabled .ant-upload-select{display:none}
.jeecg-form-container-disabled .ant-upload-list{cursor:grabbing}
.jeecg-form-container-disabled fieldset[disabled] .ant-upload-list{
-ms-pointer-events: auto !important;
pointer-events: auto !important;
}
.jeecg-form-container-disabled .ant-upload-list-item-actions .anticon-delete,
.jeecg-form-container-disabled .ant-upload-list-item .anticon-close{
display: none;
}
</style>

View File

@ -1,163 +0,0 @@
<template>
<div class="gc-canvas" @click="reloadPic">
<canvas id="gc-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</div>
</template>
<script>
export default {
name: 'JGraphicCode',
props: {
length:{
type: Number,
default: 4
},
fontSizeMin: {
type: Number,
default: 20
},
fontSizeMax: {
type: Number,
default: 45
},
backgroundColorMin: {
type: Number,
default: 180
},
backgroundColorMax: {
type: Number,
default: 240
},
colorMin: {
type: Number,
default: 50
},
colorMax: {
type: Number,
default: 160
},
lineColorMin: {
type: Number,
default: 40
},
lineColorMax: {
type: Number,
default: 180
},
dotColorMin: {
type: Number,
default: 0
},
dotColorMax: {
type: Number,
default: 255
},
contentWidth: {
type: Number,
default:136
},
contentHeight: {
type: Number,
default: 38
}
},
methods: {
// 生成一个随机数
randomNum (min, max) {
return Math.floor(Math.random() * (max - min) + min)
},
// 生成一个随机的颜色
randomColor (min, max) {
let r = this.randomNum(min, max)
let g = this.randomNum(min, max)
let b = this.randomNum(min, max)
return 'rgb(' + r + ',' + g + ',' + b + ')'
},
drawPic () {
this.randomCode()
let canvas = document.getElementById('gc-canvas')
let ctx = canvas.getContext('2d')
ctx.textBaseline = 'bottom'
// 绘制背景
ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
// 绘制文字
for (let i = 0; i < this.code.length; i++) {
this.drawText(ctx, this.code[i], i)
}
this.drawLine(ctx)
this.drawDot(ctx)
this.$emit("success",this.code)
},
drawText (ctx, txt, i) {
ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
let fontSize = this.randomNum(this.fontSizeMin, this.fontSizeMax)
ctx.font = fontSize + 'px SimHei'
let padding = 10;
let offset = (this.contentWidth-40)/(this.code.length-1)
let x=padding;
if(i>0){
x = padding+(i*offset)
}
//let x = (i + 1) * (this.contentWidth / (this.code.length + 1))
let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
if(fontSize>40){
y=40
}
var deg = this.randomNum(-10,10)
// 修改坐标原点和旋转角度
ctx.translate(x, y)
ctx.rotate(deg * Math.PI / 180)
ctx.fillText(txt, 0, 0)
// 恢复坐标原点和旋转角度
ctx.rotate(-deg * Math.PI / 180)
ctx.translate(-x, -y)
},
drawLine (ctx) {
// 绘制干扰线
for (let i = 0; i <1; i++) {
ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
ctx.beginPath()
ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
ctx.stroke()
}
},
drawDot (ctx) {
// 绘制干扰点
for (let i = 0; i < 100; i++) {
ctx.fillStyle = this.randomColor(0, 255)
ctx.beginPath()
ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
ctx.fill()
}
},
reloadPic(){
this.drawPic()
},
randomCode(){
let random = ''
//去掉了I l i o O
let str = "QWERTYUPLKJHGFDSAZXCVBNMqwertyupkjhgfdsazxcvbnm1234567890"
for(let i = 0; i < this.length; i++) {
let index = Math.floor(Math.random()*57);
random += str[index];
}
this.code = random
}
},
mounted () {
this.drawPic()
},
data(){
return {
code:""
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,201 @@
<template>
<a-upload
name="file"
listType="picture-card"
:multiple="isMultiple"
:action="uploadAction"
:headers="headers"
:data="{biz:bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
:disabled="disabled"
:isMultiple="isMultiple"
:showUploadList="isMultiple"
@change="handleChange"
@preview="handlePreview">
<img v-if="!isMultiple && picUrl" :src="getAvatarView()" style="height:104px;max-width:300px"/>
<div v-else >
<a-icon :type="uploadLoading ? 'loading' : 'plus'" />
<div class="ant-upload-text">{{ text }}</div>
</div>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel()">
<img alt="example" style="width: 100%" :src="previewImage"/>
</a-modal>
</a-upload>
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import { getFileAccessHttpUrl } from '@/api/manage'
const uidGenerator=()=>{
return '-'+parseInt(Math.random()*10000+1,10);
}
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JImageUpload',
data(){
return {
uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
uploadLoading:false,
picUrl:false,
headers:{},
fileList: [],
previewImage:"",
previewVisible: false,
}
},
props:{
text:{
type:String,
required:false,
default:"上传"
},
/*这个属性用于控制文件上传的业务路径*/
bizPath:{
type:String,
required:false,
default:"temp"
},
value:{
type:[String,Array],
required:false
},
disabled:{
type:Boolean,
required:false,
default: false
},
isMultiple:{
type:Boolean,
required:false,
default: false
}
},
watch:{
value(val){
if (val instanceof Array) {
this.initFileList(val.join(','))
} else {
this.initFileList(val)
}
}
},
created(){
const token = Vue.ls.get(ACCESS_TOKEN);
this.headers = {"X-Access-Token":token}
},
methods:{
initFileList(paths){
if(!paths || paths.length==0){
this.fileList = [];
return;
}
this.picUrl = true;
let fileList = [];
let arr = paths.split(",")
for(var a=0;a<arr.length;a++){
let url = getFileAccessHttpUrl(arr[a]);
fileList.push({
uid: uidGenerator(),
name: getFileName(arr[a]),
status: 'done',
url: url,
response:{
status:"history",
message:arr[a]
}
})
}
this.fileList = fileList
},
beforeUpload: function(file){
var fileType = file.type;
if(fileType.indexOf('image')<0){
this.$message.warning('请上传图片');
return false;
}
},
handleChange(info) {
this.picUrl = false;
let fileList = info.fileList
if(info.file.status==='done'){
if(info.file.response.success){
this.picUrl = true;
fileList = fileList.map((file) => {
if (file.response) {
file.url = file.response.message;
}
return file;
});
}
//this.$message.success(`${info.file.name} 上传成功!`);
}else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`);
}else if(info.file.status === 'removed'){
this.handleDelete(info.file)
}
this.fileList = fileList
if(info.file.status==='done' || info.file.status === 'removed'){
this.handlePathChange()
}
},
// 预览
handlePreview (file) {
this.previewImage = file.url || file.thumbUrl
this.previewVisible = true
},
getAvatarView(){
if(this.fileList.length>0){
let url = this.fileList[0].url
return getFileAccessHttpUrl(url)
}
},
handlePathChange(){
let uploadFiles = this.fileList
let path = ''
if(!uploadFiles || uploadFiles.length==0){
path = ''
}
let arr = [];
if(!this.isMultiple){
arr.push(uploadFiles[uploadFiles.length-1].response.message)
}else{
for(var a=0;a<uploadFiles.length;a++){
arr.push(uploadFiles[a].response.message)
}
}
if(arr.length>0){
path = arr.join(",")
}
this.$emit('change', path);
},
handleDelete(file){
//如有需要新增 删除逻辑
console.log(file)
},
handleCancel() {
this.close();
this.previewVisible = false;
},
close () {
},
},
model: {
prop: 'value',
event: 'change'
}
}
</script>
<style scoped>
</style>

View File

@ -42,6 +42,11 @@
type: String,
default: '',
required: false
},
biz:{
type: String,
default: '',
required: false
}
},
data(){
@ -49,7 +54,8 @@
visible:false,
uploading:false,
fileList:[],
uploadAction:''
uploadAction:'',
foreignKeys:''
}
},
watch: {
@ -67,10 +73,11 @@
handleClose(){
this.visible=false
},
show(){
show(arg){
this.fileList = []
this.uploading = false
this.visible = true
this.foreignKeys = arg;
},
handleRemove(file) {
const index = this.fileList.indexOf(file);
@ -85,6 +92,12 @@
handleImport() {
const { fileList } = this;
const formData = new FormData();
if(this.biz){
formData.append('isSingleTableImport',this.biz);
}
if(this.foreignKeys && this.foreignKeys.length>0){
formData.append('foreignKeys',this.foreignKeys);
}
fileList.forEach((file) => {
formData.append('files[]', file);
});

View File

@ -0,0 +1,100 @@
<template>
<a-input :placeholder="placeholder" :value="inputVal" @input="backValue"></a-input>
</template>
<script>
const JINPUT_QUERY_LIKE = 'like';
const JINPUT_QUERY_NE = 'ne';
const JINPUT_QUERY_GE = 'ge'; //大于等于
const JINPUT_QUERY_LE = 'le'; //小于等于
export default {
name: 'JInput',
props:{
value:{
type:String,
required:false
},
type:{
type:String,
required:false,
default:JINPUT_QUERY_LIKE
},
placeholder:{
type:String,
required:false,
default:''
}
},
watch:{
value:{
immediate:true,
handler:function(){
this.initVal();
}
},
// update-begin author:sunjianlei date:20200225 for:当 type 变化的时候重新计算值 ------
type() {
this.backValue({ target: { value: this.inputVal } })
},
// update-end author:sunjianlei date:20200225 for:当 type 变化的时候重新计算值 ------
},
model: {
prop: 'value',
event: 'change'
},
data(){
return {
inputVal:''
}
},
methods:{
initVal(){
if(!this.value){
this.inputVal = ''
}else{
let text = this.value
switch (this.type) {
case JINPUT_QUERY_LIKE:
text = text.substring(1,text.length-1);
break;
case JINPUT_QUERY_NE:
text = text.substring(1);
break;
case JINPUT_QUERY_GE:
text = text.substring(2);
break;
case JINPUT_QUERY_LE:
text = text.substring(2);
break;
default:
}
this.inputVal = text
}
},
backValue(e){
let text = e.target.value
switch (this.type) {
case JINPUT_QUERY_LIKE:
text = "*"+text+"*";
break;
case JINPUT_QUERY_NE:
text = "!"+text;
break;
case JINPUT_QUERY_GE:
text = ">="+text;
break;
case JINPUT_QUERY_LE:
text = "<="+text;
break;
default:
}
this.$emit("change",text)
}
}
}
</script>
<style scoped>
</style>

View File

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

View File

@ -59,8 +59,13 @@
this.mouseMoveStata = false;
var width = e.clientX - this.beginClientX;
if(width<this.maxwidth){
document.getElementsByClassName('handler')[0].style.left = 0 + 'px';
document.getElementsByClassName('drag_bg')[0].style.width = 0 + 'px';
// ---- update-begin- author:sunjianlei --- date:20191009 --- for: 修复获取不到 handler 的时候报错 ----
let handler = document.getElementsByClassName('handler')[0]
if (handler) {
handler.style.left = 0 + 'px'
document.getElementsByClassName('drag_bg')[0].style.width = 0 + 'px'
}
// ---- update-end- author:sunjianlei --- date:20191009 --- for: 修复获取不到 handler 的时候报错 ----
}
} //mouseup事件
},

View File

@ -1,152 +1,640 @@
<template>
<a-modal
<div class="j-super-query-box">
<slot name="button" :isActive="superQueryFlag" :isMobile="izMobile" :open="handleOpen" :reset="handleReset">
<a-tooltip v-if="superQueryFlag" v-bind="tooltipProps" :mouseLeaveDelay="0.2">
<!-- begin 不知道为什么不加上这段代码就无法生效 -->
<span v-show="false">{{tooltipProps}}</span>
<!-- end 不知道为什么不加上这段代码就无法生效 -->
<template slot="title">
<span>已有高级查询条件生效</span>
<a-divider type="vertical"/>
<a @click="handleReset">清空</a>
</template>
<a-button-group>
<a-button type="primary" @click="handleOpen">
<a-icon type="appstore" theme="twoTone" spin/>
<span>高级查询</span>
</a-button>
<a-button v-if="izMobile" type="primary" icon="delete" @click="handleReset"/>
</a-button-group>
</a-tooltip>
<a-button v-else type="primary" icon="filter" @click="handleOpen">高级查询</a-button>
</slot>
<j-modal
title="高级查询构造器"
:width="800"
:width="1000"
:visible="visible"
:confirmLoading="confirmLoading"
@cancel="handleCancel"
:mask="false"
wrapClassName="ant-modal-cust-warp"
style="top:5%;max-height: 95%;">
:fullscreen="izMobile"
class="j-super-query-modal"
style="top:5%;max-height: 95%;"
>
<template slot="footer">
<a-button @click="handleCancel"> </a-button>
<a-button @click="handleReset" style="float: left"> </a-button>
<a-button type="primary" @click="handleOk"> </a-button>
<div style="float: left">
<a-button :loading="loading" @click="handleReset">重置</a-button>
<a-button :loading="loading" @click="handleSave">保存查询条件</a-button>
</div>
<a-button :loading="loading" @click="handleCancel">关闭</a-button>
<a-button :loading="loading" type="primary" @click="handleOk">查询</a-button>
</template>
<a-spin :spinning="confirmLoading">
<a-form>
<div>
<a-row type="flex" style="margin-bottom:10px" :gutter="16" v-for="(item, index) in queryParamsModel" :key="index">
<a-spin :spinning="loading">
<a-row>
<a-col :sm="24" :md="24-5">
<a-col :span="6">
<a-select placeholder="选择查询字段" v-model="item.field" @select="(val,option)=>handleSelected(option,item)">
<a-select-option v-for="(f,fIndex) in fieldList" :key=" 'field'+fIndex" :value="f.value" :data-type="f.type">{{ f.text }}</a-select-option>
</a-select>
</a-col>
<a-empty v-if="queryParamsModel.length === 0" style="margin-bottom: 12px;">
<div slot="description">
<span>没有任何查询条件</span>
<a-divider type="vertical"/>
<a @click="handleAdd">点击新增</a>
</div>
</a-empty>
<a-col :span="6">
<a-select placeholder="选择匹配规则" v-model="item.rule">
<a-select-option value="eq">等于</a-select-option>
<a-select-option value="ne">不等于</a-select-option>
<a-select-option value="gt">大于</a-select-option>
<a-select-option value="ge">大于等于</a-select-option>
<a-select-option value="lt">小于</a-select-option>
<a-select-option value="le">小于等于</a-select-option>
<a-select-option value="right_like">..开始</a-select-option>
<a-select-option value="left_like">..结尾</a-select-option>
<a-select-option value="like">包含</a-select-option>
<a-select-option value="in">...</a-select-option>
</a-select>
</a-col>
<a-form v-else layout="inline">
<a-col :span="6">
<j-date v-if=" item.type=='date' " v-model="item.val" placeholder="请选择日期"></j-date>
<j-date v-else-if=" item.type=='datetime' " v-model="item.val" placeholder="请选择时间" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss"></j-date>
<a-input-number v-else-if=" item.type=='int'||item.type=='number' " style="width: 100%" placeholder="请输入数值" v-model="item.val"/>
<a-input v-else v-model="item.val" placeholder="请输入值" />
</a-col>
<a-row style="margin-bottom: 12px;">
<a-col :md="12" :xs="24">
<a-form-item label="过滤条件匹配" :labelCol="{md: 6,xs:24}" :wrapperCol="{md: 18,xs:24}" style="width: 100%;">
<a-select v-model="matchType" :getPopupContainer="node=>node.parentNode" style="width: 100%;">
<a-select-option value="and">AND所有条件都要求匹配</a-select-option>
<a-select-option value="or">OR条件中的任意一个匹配</a-select-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row type="flex" style="margin-bottom:10px" :gutter="16" v-for="(item, index) in queryParamsModel" :key="index">
<a-col :md="8" :xs="24" style="margin-bottom: 12px;">
<a-tree-select
showSearch
v-model="item.field"
:treeData="fieldTreeData"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="选择查询字段"
allowClear
treeDefaultExpandAll
:getPopupContainer="node=>node.parentNode"
style="width: 100%"
@select="(val,option)=>handleSelected(option,item)"
>
</a-tree-select>
</a-col>
<a-col :md="4" :xs="24" style="margin-bottom: 12px;">
<a-select placeholder="匹配规则" :value="item.rule" :getPopupContainer="node=>node.parentNode" @change="handleRuleChange(item,$event)">
<a-select-option value="eq">等于</a-select-option>
<a-select-option value="like">包含</a-select-option>
<a-select-option value="right_like">以..开始</a-select-option>
<a-select-option value="left_like">以..结尾</a-select-option>
<a-select-option value="in">在...中</a-select-option>
<a-select-option value="ne">不等于</a-select-option>
<a-select-option value="gt">大于</a-select-option>
<a-select-option value="ge">大于等于</a-select-option>
<a-select-option value="lt">小于</a-select-option>
<a-select-option value="le">小于等于</a-select-option>
</a-select>
</a-col>
<a-col :md="8" :xs="24" style="margin-bottom: 12px;">
<template v-if="item.dictCode">
<template v-if="item.type === 'table-dict'">
<j-popup
v-model="item.val"
:code="item.dictTable"
:field="item.dictCode"
:orgFields="item.dictCode"
:destFields="item.dictCode"
></j-popup>
</template>
<template v-else>
<j-multi-select-tag v-show="allowMultiple(item)" v-model="item.val" :dictCode="item.dictCode" placeholder="请选择"/>
<j-dict-select-tag v-show="!allowMultiple(item)" v-model="item.val" :dictCode="item.dictCode" placeholder="请选择"/>
</template>
</template>
<j-popup v-else-if="item.type === 'popup'" :value="item.val" v-bind="item.popup" group-id="superQuery" @input="(e,v)=>handleChangeJPopup(item,e,v)"/>
<j-select-multi-user
v-else-if="item.type === 'select-user' || item.type === 'sel_user'"
v-model="item.val"
:buttons="false"
:multiple="false"
placeholder="请选择用户"
:returnKeys="['id', item.customReturnField || 'username']"
/>
<j-select-depart
v-else-if="item.type === 'select-depart' || item.type === 'sel_depart'"
v-model="item.val"
:multi="false"
placeholder="请选择部门"
:customReturnField="item.customReturnField || 'id'"
/>
<a-select
v-else-if="item.options instanceof Array"
v-model="item.val"
:options="item.options"
allowClear
placeholder="请选择"
:mode="allowMultiple(item)?'multiple':''"
/>
<j-area-linkage v-model="item.val" v-else-if="item.type==='area-linkage' || item.type==='pca'" style="width: 100%"/>
<j-date v-else-if=" item.type=='date' " v-model="item.val" placeholder="请选择日期" style="width: 100%"></j-date>
<j-date v-else-if=" item.type=='datetime' " v-model="item.val" placeholder="请选择时间" :show-time="true" date-format="YYYY-MM-DD HH:mm:ss" style="width: 100%"></j-date>
<a-time-picker v-else-if="item.type==='time'" :value="item.val ? moment(item.val,'HH:mm:ss') : null" format="HH:mm:ss" style="width: 100%" @change="(time,value)=>item.val=value"/>
<a-input-number v-else-if=" item.type=='int'||item.type=='number' " style="width: 100%" placeholder="请输入数值" v-model="item.val"/>
<a-input v-else v-model="item.val" placeholder="请输入值"/>
</a-col>
<a-col :md="4" :xs="0" style="margin-bottom: 12px;">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
<a-col :md="0" :xs="24" style="margin-bottom: 12px;text-align: right;">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
</a-row>
</a-form>
</a-col>
<a-col :sm="24" :md="5">
<!-- 查询记录 -->
<a-card class="j-super-query-history-card" :bordered="true">
<div slot="title">
保存的查询
</div>
<a-empty v-if="saveTreeData.length === 0" class="j-super-query-history-empty" description="没有保存任何查询"/>
<a-tree
v-else
class="j-super-query-history-tree"
showIcon
:treeData="saveTreeData"
:selectedKeys="[]"
@select="handleTreeSelect"
>
</a-tree>
</a-card>
</a-col>
</a-row>
<a-col :span="6">
<a-button @click="handleAdd" icon="plus"></a-button>&nbsp;
<a-button @click="handleDel( index )" icon="minus"></a-button>
</a-col>
</a-row>
</div>
</a-form>
</a-spin>
</a-modal>
<a-modal title="请输入保存的名称" :visible="prompt.visible" @cancel="prompt.visible=false" @ok="handlePromptOk">
<a-input v-model="prompt.value"></a-input>
</a-modal>
</j-modal>
</div>
</template>
<script>
import ACol from 'ant-design-vue/es/grid/Col'
import JDate from '@/components/jeecg/JDate.vue';
import moment from 'moment'
import * as utils from '@/utils/util'
import { mixinDevice } from '@/utils/mixin'
import JDate from '@/components/jeecg/JDate.vue'
import JSelectDepart from '@/components/jeecgbiz/JSelectDepart'
import JSelectMultiUser from '@/components/jeecgbiz/JSelectMultiUser'
import JMultiSelectTag from '@/components/dict/JMultiSelectTag'
import JAreaLinkage from '@comp/jeecg/JAreaLinkage'
export default {
name: 'JSuperQuery',
components: {
ACol,
JDate
},
data(){
return {
visible:false,
confirmLoading:false,
queryParamsModel:[{}]
}
},
props:{
/* fieldList:[{value:'',text:'',type:''}]
* type:date datetime int number string
mixins: [mixinDevice],
components: { JAreaLinkage, JMultiSelectTag, JDate, JSelectDepart, JSelectMultiUser },
props: {
/*
fieldList: [{
value:'',
text:'',
type:'',
dictCode:'' // 只要 dictCode 有值,无论 type 是什么,都显示为字典下拉框
}]
type:date datetime int number string
* */
fieldList:{
type:Array,
required:true
fieldList: {
type: Array,
required: true
},
/*
* 这个回调函数接收一个数组参数 即查询条件
* */
callback:{
type:String,
required:false,
default:'handleSuperQuery'
callback: {
type: String,
required: false,
default: 'handleSuperQuery'
},
// 当前是否在加载中
loading: {
type: Boolean,
default: false
},
// 保存查询条件的唯一 code通过该 code 区分
// 默认为 null代表以当前路由全路径为区分Code
saveCode: {
type: String,
default: null
}
},
data() {
return {
moment,
fieldTreeData: [],
prompt: {
visible: false,
value: ''
},
visible: false,
queryParamsModel: [],
treeIcon: <a-icon type="file-text"/>,
// 保存查询条件的treeData
saveTreeData: [],
// 保存查询条件的前缀名
saveCodeBefore: 'JSuperQuerySaved_',
// 查询类型过滤条件匹配and、or
matchType: 'and',
superQueryFlag: false,
}
},
methods:{
show(){
if(!this.queryParamsModel ||this.queryParamsModel.length==0){
this.queryParamsModel = [{}]
}
this.visible = true;
computed: {
izMobile() {
return this.device === 'mobile'
},
handleOk(){
console.log("---高级查询参数--->",this.queryParamsModel)
if(!this.isNullArray()){
this.$emit(this.callback, this.queryParamsModel)
}else{
this.$emit(this.callback)
tooltipProps() {
return this.izMobile ? { visible: false } : {}
},
fullSaveCode() {
let saveCode = this.saveCode
if (saveCode == null || saveCode === '') {
saveCode = this.$route.fullPath
}
return this.saveCodeBefore + saveCode
},
},
watch: {
// 当 saveCode 变化时,重新查询已保存的条件
fullSaveCode: {
immediate: true,
handler() {
let list = this.$ls.get(this.fullSaveCode)
if (list instanceof Array) {
this.saveTreeData = list.map(i => this.renderSaveTreeData(i))
}
}
},
handleCancel(){
fieldList: {
deep: true,
immediate: true,
handler(val) {
let mainData = [], subData = []
val.forEach(item => {
let data = { ...item }
data.label = data.label || data.text
let hasChildren = (data.children instanceof Array)
data.disabled = hasChildren
data.selectable = !hasChildren
if (hasChildren) {
data.children = data.children.map(item2 => {
let child = { ...item2 }
child.label = child.label || child.text
child.label = data.label + '-' + child.label
child.value = data.value + ',' + child.value
child.val = ''
return child
})
data.val = ''
subData.push(data)
} else {
mainData.push(data)
}
})
this.fieldTreeData = mainData.concat(subData)
}
}
},
methods: {
show() {
if (!this.queryParamsModel || this.queryParamsModel.length === 0) {
this.resetLine()
}
this.visible = true
},
handleOk() {
if (!this.isNullArray(this.queryParamsModel)) {
let event = {
matchType: this.matchType,
params: this.removeEmptyObject(this.queryParamsModel)
}
// 移动端模式下关闭弹窗
if (this.izMobile) {
this.visible = false
}
this.emitCallback(event)
} else {
this.$message.warn("不能查询空条件")
}
},
emitCallback(event = {}) {
let { params = [], matchType = this.matchType } = event
this.superQueryFlag = (params && params.length > 0)
for (let param of params) {
if (Array.isArray(param.val)) {
param.val = param.val.join(',')
}
}
console.debug('---高级查询参数--->', { params, matchType })
this.$emit(this.callback, params, matchType)
},
handleCancel() {
this.close()
},
close () {
this.$emit('close');
this.visible = false;
close() {
this.$emit('close')
this.visible = false
},
handleAdd () {
this.queryParamsModel.push({});
handleAdd() {
this.addNewLine()
},
handleDel (index) {
addNewLine() {
this.queryParamsModel.push({ rule: 'eq' })
},
resetLine() {
this.superQueryFlag = false
this.queryParamsModel = []
this.addNewLine()
},
handleDel(index) {
this.queryParamsModel.splice(index, 1)
},
handleSelected(node, item) {
let { type, options, dictCode, dictTable, customReturnField, popup } = node.dataRef
item['type'] = type
item['options'] = options
item['dictCode'] = dictCode
item['dictTable'] = dictTable
item['customReturnField'] = customReturnField
if (popup) {
item['popup'] = popup
}
this.$set(item, 'val', undefined)
},
handleOpen() {
this.show()
},
handleReset() {
this.resetLine()
this.emitCallback()
},
handleSave() {
let queryParams = this.removeEmptyObject(this.queryParamsModel)
if (this.isNullArray(queryParams)) {
this.$message.warning('空条件不能保存')
} else {
this.prompt.value = ''
this.prompt.visible = true
}
},
handlePromptOk() {
let { value } = this.prompt
if(!value){
this.$message.warning('保存名称不能为空')
return
}
// 取出查询条件
let records = this.removeEmptyObject(this.queryParamsModel)
// 判断有没有重名的
let filterList = this.saveTreeData.filter(i => i.originTitle === value)
if (filterList.length > 0) {
this.$confirm({
content: `${value} 已存在,是否覆盖?`,
onOk: () => {
this.prompt.visible = false
filterList[0].records = records
this.saveToLocalStore()
this.$message.success('保存成功')
}
})
} else {
// 没有重名的,直接添加
this.prompt.visible = false
// 添加到树列表中
this.saveTreeData.push(this.renderSaveTreeData({
title: value,
matchType: this.matchType,
records: records
}))
// 保存到 LocalStore
this.saveToLocalStore()
this.$message.success('保存成功')
}
},
handleTreeSelect(idx, event) {
if (event.selectedNodes[0]) {
let { matchType, records } = event.selectedNodes[0].data.props
// 将保存的matchType取出兼容旧数据如果没有保存就还是使用原来的
this.matchType = matchType || this.matchType
this.queryParamsModel = utils.cloneObject(records)
}
},
handleRemoveSaveTreeItem(event, vNode) {
// 阻止事件冒泡
event.stopPropagation()
this.queryParamsModel.splice(index,1);
this.$message.warning("请关闭后重新打开")
this.$confirm({
content: '是否删除当前查询?',
onOk: () => {
let { eventKey } = vNode
this.saveTreeData.splice(Number.parseInt(eventKey.substring(2)), 1)
this.saveToLocalStore()
},
})
},
handleSelected(option,item){
item['type'] = option.data.attrs['data-type']
// 将查询保存到 LocalStore 里
saveToLocalStore() {
let saveValue = this.saveTreeData.map(({ originTitle, matchType, records }) => ({ title: originTitle, matchType, records }))
this.$ls.set(this.fullSaveCode, saveValue)
},
handleReset(){
this.queryParamsModel=[{}]
this.$emit(this.callback)
},
isNullArray(){
isNullArray(array) {
//判断是不是空数组对象
if(!this.queryParamsModel || this.queryParamsModel.length==0){
if (!array || array.length === 0) {
return true
}
if(this.queryParamsModel.length==1){
let obj = this.queryParamsModel[0]
if(!obj.field || !obj.val || !obj.rule){
if (array.length === 1) {
let obj = array[0]
if (!obj.field || (obj.val == null || obj.val === '') || !obj.rule) {
return true
}
}
return false;
}
return false
},
// 去掉数组中的空对象
removeEmptyObject(arr) {
let array = utils.cloneObject(arr)
for (let i = 0; i < array.length; i++) {
let item = array[i]
if (item == null || Object.keys(item).length <= 0) {
array.splice(i--, 1)
} else {
if (Array.isArray(item.options)) {
// 如果有字典属性,就不需要保存 options 了
if (item.dictCode) {
// 去掉特殊属性
delete item.options
}
}
}
}
return array
},
/** 渲染保存查询条件的 title加个删除按钮 */
renderSaveTreeData(item) {
item.icon = this.treeIcon
item.originTitle = item['title']
item.title = (fn, vNode) => {
let { originTitle } = vNode.dataRef
return (
<div class="j-history-tree-title">
<span>{originTitle}</span>
<div class="j-history-tree-title-closer" onClick={e => this.handleRemoveSaveTreeItem(e, vNode)}>
<a-icon type="close-circle"/>
</div>
</div>
)
}
return item
},
/** 判断是否允许多选 */
allowMultiple(item) {
return item.rule === 'in'
},
handleRuleChange(item, newValue) {
let oldValue = item.rule
this.$set(item, 'rule', newValue)
// 上一个规则是否是 in且type是字典或下拉
if (oldValue === 'in') {
if (item.dictCode || item.options instanceof Array) {
let value = item.val
if (typeof item.val === 'string') {
value = item.val.split(',')[0]
} else if (Array.isArray(item.val)) {
value = item.val[0]
}
this.$set(item, 'val', value)
}
}
},
handleChangeJPopup(item, e, values) {
item.val = values[item.popup['destFields']]
},
}
}
</script>
<style >
<style lang="less" scoped>
.j-super-query-box {
display: inline-block;
}
.j-super-query-modal {
.j-super-query-history-card {
/deep/ .ant-card-body,
/deep/ .ant-card-head-title {
padding: 0;
}
/deep/ .ant-card-head {
padding: 4px 8px;
min-height: initial;
}
}
.j-super-query-history-empty {
/deep/ .ant-empty-image {
height: 80px;
line-height: 80px;
margin-bottom: 0;
}
/deep/ img {
width: 80px;
height: 65px;
}
/deep/ .ant-empty-description {
color: #afafaf;
margin: 8px 0;
}
}
.j-super-query-history-tree {
.j-history-tree-title {
width: calc(100% - 24px);
position: relative;
display: inline-block;
&-closer {
color: #999999;
position: absolute;
top: 0;
right: 0;
width: 24px;
height: 24px;
text-align: center;
opacity: 0;
transition: opacity 0.3s, color 0.3s;
&:hover {
color: #666666;
}
&:active {
color: #333333;
}
}
&:hover {
.j-history-tree-title-closer {
opacity: 1;
}
}
}
/deep/ .ant-tree-switcher {
display: none;
}
/deep/ .ant-tree-node-content-wrapper {
width: 100%;
}
}
}
</style>

View File

@ -0,0 +1,57 @@
<template>
<a-switch v-model="checkStatus" :disabled="disabled" @change="handleChange"/>
</template>
<script>
export default {
name: 'JSwitch',
props: {
value:{
type: String,
required: false
},
disabled:{
type: Boolean,
required: false,
default: false
},
options:{
type:Array,
required:false,
default:()=>['Y','N']
}
},
data () {
return {
checkStatus: false
}
},
watch: {
value:{
immediate: true,
handler(val){
if(!val){
this.checkStatus = false
this.$emit('change', this.options[1]);
}else{
if(this.options[0]==val){
this.checkStatus = true
}else{
this.checkStatus = false
}
}
}
}
},
methods: {
handleChange(checked){
let flag = checked===false?this.options[1]:this.options[0];
this.$emit('change', flag);
}
},
model: {
prop: 'value',
event: 'change'
}
}
</script>

View File

@ -22,7 +22,7 @@
data(){
return {
treeData:[],
treeValue:"",
treeValue: null,
url_root:"/sys/category/loadTreeRoot",
url_children:"/sys/category/loadTreeChildren",
url_view:'/sys/category/loadOne',
@ -97,7 +97,7 @@
methods:{
loadViewInfo(){
if(!this.value || this.value=="0"){
this.treeValue = ""
this.treeValue = null
}else{
let param = {
field:this.field,
@ -180,7 +180,11 @@
},
onChange(value){
console.log(value)
this.$emit('change', value.value);
if(!value){
this.$emit('change', '');
}else{
this.$emit('change', value.value);
}
this.treeValue = value
},
onSearch(value){

View File

@ -2,6 +2,7 @@
<a-tree-select
allowClear
labelInValue
:getPopupContainer="(node) => node.parentNode"
style="width: 100%"
:disabled="disabled"
:dropdownStyle="{ maxHeight: '400px', overflow: 'auto' }"
@ -9,6 +10,7 @@
:loadData="asyncLoadTreeData"
:value="treeValue"
:treeData="treeData"
:multiple="multiple"
@change="onChange"
@search="onSearch">
</a-tree-select>
@ -45,7 +47,7 @@
},
pidValue:{
type: String,
default: '0',
default: '',
required: false
},
disabled:{
@ -57,11 +59,26 @@
type: String,
default: '',
required: false
},
condition:{
type:String,
default:'',
required:false
},
// 是否支持多选
multiple: {
type: Boolean,
default: false,
},
loadTriggleChange:{
type: Boolean,
default: false,
required:false
}
},
data () {
return {
treeValue:"",
treeValue: null,
treeData:[],
url:"/sys/dict/loadTreeData",
view:'/sys/dict/loadDictItem/',
@ -81,26 +98,36 @@
}
},
created(){
this.initDictInfo()
this.loadRoot()
this.loadItemByCode()
this.validateProp().then(()=>{
this.initDictInfo()
this.loadRoot()
this.loadItemByCode()
})
},
methods: {
loadItemByCode(){
if(!this.value || this.value=="0"){
this.treeValue = ""
this.treeValue = null
}else{
getAction(`${this.view}${this.dict}`,{key:this.value}).then(res=>{
if(res.success){
this.treeValue = {
key:this.value,
value:this.value,
label:res.result
}
let values = this.value.split(',')
this.treeValue = res.result.map((item, index) => ({
key: values[index],
value: values[index],
label: item
}))
this.onLoadTriggleChange(res.result[0]);
}
})
}
},
onLoadTriggleChange(text){
//只有单选才会触发
if(!this.multiple && this.loadTriggleChange){
this.$emit('change', this.value,text)
}
},
initDictInfo(){
let arr = this.dict.split(",")
this.tableName = arr[0]
@ -120,7 +147,8 @@
text:this.text,
code:this.code,
pidField:this.pidField,
hasChildField:this.hasChildField
hasChildField:this.hasChildField,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success){
@ -162,7 +190,8 @@
text:this.text,
code:this.code,
pidField:this.pidField,
hasChildField:this.hasChildField
hasChildField:this.hasChildField,
condition:this.condition
}
getAction(this.url,param).then(res=>{
if(res.success && res.result){
@ -183,9 +212,12 @@
onChange(value){
if(!value){
this.$emit('change', '');
this.treeValue = ''
}else{
this.$emit('change', value.value);
this.treeValue = null
} else if (value instanceof Array) {
this.$emit('change', value.map(item => item.value).join(','))
this.treeValue = value
} else {
this.$emit('change', value.value,value.label)
this.treeValue = value
}
@ -195,6 +227,28 @@
},
getCurrTreeData(){
return this.treeData
},
validateProp(){
let mycondition = this.condition
return new Promise((resolve,reject)=>{
if(!mycondition){
resolve();
}else{
try {
let test=JSON.parse(mycondition);
console.log("aaaaasdsdd",typeof test)
if(typeof test == 'object' && test){
resolve()
}else{
this.$message.error("组件JTreeSelect-condition传值有误需要一个json字符串!")
reject()
}
} catch(e) {
this.$message.error("组件JTreeSelect-condition传值有误需要一个json字符串!")
reject()
}
}
})
}
},
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼

View File

@ -3,8 +3,11 @@
:rowKey="rowKey"
:columns="columns"
:dataSource="dataSource"
v-bind="tableProps"
@expand="handleExpand">
:expandedRowKeys="expandedRowKeys"
v-bind="tableAttrs"
v-on="$listeners"
@expand="handleExpand"
@expandedRowsChange="expandedRowKeys=$event">
<template v-for="(slotItem) of slots" :slot="slotItem" slot-scope="text, record, index">
<slot :name="slotItem" v-bind="{text,record,index}"></slot>
@ -30,8 +33,7 @@
},
queryParams: {
type: Object,
default: () => {
}
default: () => ({})
},
// 查询顶级时的值如果顶级为0则传0
topValue: {
@ -52,13 +54,23 @@
},
tableProps: {
type: Object,
default: () => {
}
default: () => ({})
},
/** 是否在创建组件的时候就查询数据 */
immediateRequest: {
type: Boolean,
default: true
},
condition:{
type:String,
default:'',
required:false
}
},
data() {
return {
dataSource: []
dataSource: [],
expandedRowKeys: []
}
},
computed: {
@ -77,6 +89,9 @@
}
}
return slots
},
tableAttrs() {
return Object.assign(this.$attrs, this.tableProps)
}
},
watch: {
@ -88,20 +103,44 @@
}
},
created() {
this.loadData()
if (this.immediateRequest) this.loadData()
},
methods: {
/** 加载数据*/
loadData(id = this.topValue, first = true, url = this.url) {
this.$emit('requestBefore', { first })
if (first) {
this.expandedRowKeys = []
}
let params = Object.assign({}, this.queryParams || {})
params[this.queryKey] = id
if(this.condition && this.condition.length>0){
params['condition'] = this.condition
}
return getAction(url, params).then(res => {
let dataSource = res.result.map(item => {
let list = []
if (res.result instanceof Array) {
list = res.result
} else if (res.result.records instanceof Array) {
list = res.result.records
} else {
throw '返回数据类型不识别'
}
let dataSource = list.map(item => {
// 判断是否标记了带有子级
if (item.hasChildren === true) {
// 查找第一个带有dataIndex的值的列
let firstColumn
for (let column of this.columns) {
firstColumn = column.dataIndex
if (firstColumn) break
}
// 定义默认展开时显示的loading子级实际子级数据只在展开时加载
let loadChild = { id: `${item.id}_loadChild`, name: 'loading...', isLoading: true }
let loadChild = { id: `${item.id}_loadChild`, [firstColumn]: 'loading...', isLoading: true }
item.children = [loadChild]
}
return item
@ -109,8 +148,9 @@
if (first) {
this.dataSource = dataSource
}
this.$emit('requestSuccess', { first, dataSource, res })
return Promise.resolve(dataSource)
})
}).finally(() => this.$emit('requestFinally', { first }))
},
/** 点击展开图标时触发 */

View File

@ -1,23 +1,50 @@
<template>
<a-upload
name="file"
:multiple="true"
:action="uploadAction"
:headers="headers"
:data="{'isup':1,'bizPath':bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
@change="handleChange">
<a-button>
<a-icon type="upload" />{{ text }}
</a-button>
</a-upload>
<div :id="containerId" style="position: relative">
<!-- ---------------------------- begin 图片左右换位置 ------------------------------------- -->
<div class="movety-container" :style="{top:top+'px',left:left+'px',display:moveDisplay}" style="padding:0 8px;position: absolute;z-index: 91;height: 32px;width: 104px;text-align: center;">
<div :id="containerId+'-mover'" :class="showMoverTask?'uploadty-mover-mask':'movety-opt'" style="margin-top: 12px">
<a @click="moveLast" style="margin: 0 5px;"><a-icon type="arrow-left" style="color: #fff;font-size: 16px"/></a>
<a @click="moveNext" style="margin: 0 5px;"><a-icon type="arrow-right" style="color: #fff;font-size: 16px"/></a>
</div>
</div>
<!-- ---------------------------- end 图片左右换位置 ------------------------------------- -->
<a-upload
name="file"
:multiple="true"
:action="uploadAction"
:headers="headers"
:data="{'biz':bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
@change="handleChange"
:disabled="disabled"
:returnUrl="returnUrl"
:listType="complistType"
@preview="handlePreview"
:class="{'uploadty-disabled':disabled}">
<template>
<div v-if="isImageComp">
<a-icon type="plus" />
<div class="ant-upload-text">{{ text }}</div>
</div>
<a-button v-else-if="buttonVisible">
<a-icon type="upload" />{{ text }}
</a-button>
</template>
</a-upload>
<a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
<img alt="example" style="width: 100%" :src="previewImage" />
</a-modal>
</div>
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import { getFileAccessHttpUrl } from '@/api/manage';
const FILE_TYPE_ALL = "all"
const FILE_TYPE_IMG = "image"
@ -37,9 +64,21 @@
data(){
return {
uploadAction:window._CONFIG['domianURL']+"/sys/common/upload",
urlDownload:window._CONFIG['domianURL'] + "/sys/common/download/",
headers:{},
fileList: []
fileList: [],
newFileList: [],
uploadGoOn:true,
previewVisible: false,
//---------------------------- begin 图片左右换位置 -------------------------------------
previewImage: '',
containerId:'',
top:'',
left:'',
moveDisplay:'none',
showMoverTask:false,
moverHold:false,
currentImg:''
//---------------------------- end 图片左右换位置 -------------------------------------
}
},
props:{
@ -60,27 +99,98 @@
default:"temp"
},
value:{
type:String,
type:[String,Array],
required:false
},
// update-begin- --- author:wangshuai ------ date:20190929 ---- for:Jupload组件增加是否能够点击
disabled:{
type:Boolean,
required:false,
default: false
},
// update-end- --- author:wangshuai ------ date:20190929 ---- for:Jupload组件增加是否能够点击
//此属性被废弃了
triggerChange:{
type: Boolean,
required: false,
default: false
},
/**
* update -- author:lvdandan -- date:20190219 -- for:Jupload组件增加是否返回url
* true仅返回url
* false返回fileName filePath fileSize
*/
returnUrl:{
type:Boolean,
required:false,
default: true
},
number:{
type:Number,
required:false,
default: 0
},
buttonVisible:{
type:Boolean,
required:false,
default: true
},
},
watch:{
value(val){
this.initFileList(val)
value:{
immediate: true,
handler() {
let val = this.value
if (val instanceof Array) {
if(this.returnUrl){
this.initFileList(val.join(','))
}else{
this.initFileListArr(val);
}
} else {
this.initFileList(val)
}
}
}
},
computed:{
isImageComp(){
return this.fileType === FILE_TYPE_IMG
},
complistType(){
return this.fileType === FILE_TYPE_IMG?'picture-card':'text'
}
},
created(){
const token = Vue.ls.get(ACCESS_TOKEN);
this.headers = {"X-Access-Token":token}
//---------------------------- begin 图片左右换位置 -------------------------------------
this.headers = {"X-Access-Token":token};
this.containerId = 'container-ty-'+new Date().getTime();
//---------------------------- end 图片左右换位置 -------------------------------------
},
methods:{
initFileListArr(val){
if(!val || val.length==0){
this.fileList = [];
return;
}
let fileList = [];
for(var a=0;a<val.length;a++){
let url = getFileAccessHttpUrl(val[a].filePath);
fileList.push({
uid:uidGenerator(),
name:val[a].fileName,
status: 'done',
url: url,
response:{
status:"history",
message:val[a].filePath
}
})
}
this.fileList = fileList
},
initFileList(paths){
if(!paths || paths.length==0){
//return [];
@ -92,11 +202,12 @@
let fileList = [];
let arr = paths.split(",")
for(var a=0;a<arr.length;a++){
let url = getFileAccessHttpUrl(arr[a]);
fileList.push({
uid:uidGenerator(),
name:getFileName(arr[a]),
status: 'done',
url: this.urlDownload+arr[a],
url: url,
response:{
status:"history",
message:arr[a]
@ -122,15 +233,12 @@
this.$emit('change', path);
},
beforeUpload(file){
this.uploadGoOn=true
var fileType = file.type;
if(fileType===FILE_TYPE_IMG){
if(this.fileType===FILE_TYPE_IMG){
if(fileType.indexOf('image')<0){
this.$message.warning('请上传图片');
return false;
}
}else if(fileType===FILE_TYPE_TXT){
if(fileType.indexOf('image')>=0){
this.$message.warning('请上传文件');
this.uploadGoOn=false
return false;
}
}
@ -139,17 +247,24 @@
},
handleChange(info) {
console.log("--文件列表改变--")
if(!info.file.status && this.uploadGoOn === false){
info.fileList.pop();
}
let fileList = info.fileList
if(info.file.status==='done'){
if(this.number>0){
fileList = fileList.slice(-this.number);
}
if(info.file.response.success){
fileList = fileList.map((file) => {
if (file.response) {
file.url = this.urlDownload+file.response.message;
let reUrl = file.response.message;
file.url = getFileAccessHttpUrl(reUrl);
}
return file;
});
}
this.$message.success(`${info.file.name} 上传成功!`);
//this.$message.success(`${info.file.name} 上传成功!`);
}else if (info.file.status === 'error') {
this.$message.error(`${info.file.name} 上传失败.`);
}else if(info.file.status === 'removed'){
@ -157,13 +272,137 @@
}
this.fileList = fileList
if(info.file.status==='done' || info.file.status === 'removed'){
this.handlePathChange()
//returnUrl为true时仅返回文件路径
if(this.returnUrl){
this.handlePathChange()
}else{
//returnUrl为false时返回文件名称、文件路径及文件大小
this.newFileList = [];
for(var a=0;a<fileList.length;a++){
var fileJson = {
fileName:fileList[a].name,
filePath:fileList[a].response.message,
fileSize:fileList[a].size
};
this.newFileList.push(fileJson);
}
this.$emit('change', this.newFileList);
}
}
},
handleDelete(file){
//如有需要新增 删除逻辑
console.log(file)
},
handlePreview(file){
if(this.fileType === FILE_TYPE_IMG){
this.previewImage = file.url || file.thumbUrl;
this.previewVisible = true;
}else{
location.href=file.url
}
},
handleCancel(){
this.previewVisible = false;
},
//---------------------------- begin 图片左右换位置 -------------------------------------
moveLast(){
//console.log(ev)
//console.log(this.fileList)
//console.log(this.currentImg)
let index = this.getIndexByUrl();
if(index==0){
this.$message.warn('未知的操作')
}else{
let curr = this.fileList[index].url;
let last = this.fileList[index-1].url;
let arr =[]
for(let i=0;i<this.fileList.length;i++){
if(i==index-1){
arr.push(curr)
}else if(i==index){
arr.push(last)
}else{
arr.push(this.fileList[i].url)
}
}
this.currentImg = last
this.$emit('change',arr.join(','))
}
},
moveNext(){
let index = this.getIndexByUrl();
if(index==this.fileList.length-1){
this.$message.warn('已到最后~')
}else{
let curr = this.fileList[index].url;
let next = this.fileList[index+1].url;
let arr =[]
for(let i=0;i<this.fileList.length;i++){
if(i==index+1){
arr.push(curr)
}else if(i==index){
arr.push(next)
}else{
arr.push(this.fileList[i].url)
}
}
this.currentImg = next
this.$emit('change',arr.join(','))
}
},
getIndexByUrl(){
for(let i=0;i<this.fileList.length;i++){
if(this.fileList[i].url === this.currentImg || encodeURI(this.fileList[i].url) === this.currentImg){
return i;
}
}
return -1;
}
},
mounted(){
const moverObj = document.getElementById(this.containerId+'-mover');
moverObj.addEventListener('mouseover',()=>{
this.moverHold = true
this.moveDisplay = 'block';
});
moverObj.addEventListener('mouseout',()=>{
this.moverHold = false
this.moveDisplay = 'none';
});
let picList = document.getElementById(this.containerId)?document.getElementById(this.containerId).getElementsByClassName('ant-upload-list-picture-card'):[];
if(picList && picList.length>0){
picList[0].addEventListener('mouseover',(ev)=>{
ev = ev || window.event;
let target = ev.target || ev.srcElement;
if('ant-upload-list-item-info' == target.className){
this.showMoverTask=false
let item = target.parentElement
this.left = item.offsetLeft
this.top=item.offsetTop+item.offsetHeight-50;
this.moveDisplay = 'block';
this.currentImg = target.getElementsByTagName('img')[0].src
}
});
picList[0].addEventListener('mouseout',(ev)=>{
ev = ev || window.event;
let target = ev.target || ev.srcElement;
//console.log('移除',target)
if('ant-upload-list-item-info' == target.className){
this.showMoverTask=true
setTimeout(()=>{
if(this.moverHold === false)
this.moveDisplay = 'none';
},100)
}
if('ant-upload-list-item ant-upload-list-item-done' == target.className || 'ant-upload-list ant-upload-list-picture-card'== target.className){
this.moveDisplay = 'none';
}
})
//---------------------------- end 图片左右换位置 -------------------------------------
}
},
model: {
prop: 'value',
@ -172,6 +411,24 @@
}
</script>
<style scoped>
<style lang="less">
.uploadty-disabled{
.ant-upload-list-item {
.anticon-close{
display: none;
}
.anticon-delete{
display: none;
}
}
}
//---------------------------- begin 图片左右换位置 -------------------------------------
.uploadty-mover-mask{
background-color: rgba(0, 0, 0, 0.5);
opacity: .8;
color: #fff;
height: 28px;
line-height: 28px;
}
//---------------------------- end 图片左右换位置 -------------------------------------
</style>

View File

@ -477,6 +477,7 @@ online用 实际开发请使用components/dict/JMultiSelectTag
| dict |string | ✔| 表名,显示字段名,存储字段名拼接的字符串 |
| pidField |string | ✔| 父ID的字段名 |
| pidValue |string | | 根节点父ID的值 默认'0' 不可以设置为空,如果想使用此组件而数据库根节点父ID为空请修改之 |
| multiple |boolean | |是否支持多选 |
使用示例
----

View File

@ -73,6 +73,7 @@
- `required` 是否必填,可选值为`true`or`false`
- `pattern` 正则表达式验证,只有成功匹配该正则的值才能成功通过验证
- `handler` 自定义函数校验,使用方法请见[示例五](#示例五)
- `message` 当验证未通过时显示的提示文本,可以使用`${...}`变量替换文本(详见`${...} 变量使用方式`
- 配置示例请看[示例二](#示例二)
@ -252,6 +253,19 @@ setValues([
}
])
```
### clearSelection
主动清空选择的行
- `参数:`
- `返回值:`
## 内置插槽
| 插槽名 | 说明 |
|--------------|------------------------------------------------------|
| buttonBefore | 在操作按钮的**前面**插入插槽,不受`actionButton`属性的影响 |
| buttonAfter | 在操作按钮的**后面**插入插槽,不受`actionButton`属性的影响 |
## ${...} 变量使用方式
@ -510,4 +524,54 @@ this.$refs.editableTable.getValues((error, values) => {
}
}
</script>
```
## 示例五
```js
// 该示例是自定义函数校验
columns: [
{
title: '字段名称',
key: 'dbFieldName',
type: FormTypes.input,
defaultValue: '',
validateRules: [
{
// 自定义函数校验 handler
handler(type, value, row, column, callback, target) {
// type 触发校验的类型input、change、blur
// value 当前校验的值
// callback(flag, message) 方法必须执行且只能执行一次
// flag = 是否通过了校验,不填写或者填写 null 代表不进行任何操作
// message = 提示的类型,默认使用配置的 message
// target 行编辑的实例对象
if (type === 'blur') {
if (value === 'abc') {
callback(false, '${title}不能是abc') // false = 未通过,可以跟自定义提示
return
}
let { values } = target.getValuesSync({ validate: false })
let count = 0
for (let val of values) {
if (val['dbFieldName'] === value) {
if (++count >= 2) {
callback(false, '${title}不能重复')
return
}
}
}
callback(true) // true = 通过验证
} else {
callback() // 不填写或者填写 null 代表不进行任何操作
}
},
message: '${title}默认提示'
}
]
},
]
```

View File

@ -1,5 +1,9 @@
import T from './JFormContainer.vue'
let install = function (Vue) {
Vue.component('JFormContainer',T);
}
export default { install };
import JModal from './JModal'
import JFormContainer from './JFormContainer.vue'
export default {
install(Vue) {
Vue.component('JFormContainer', JFormContainer)
Vue.component(JModal.name, JModal)
}
}

View File

@ -0,0 +1,113 @@
<template>
<div>
<a-modal
title="文件上传"
:width="width"
:visible="visible"
@ok="ok"
cancelText="取消"
@cancel="close">
<!--style="top: 20px;"-->
<j-upload :file-type="fileType" :value="filePath" @change="handleChange" :disabled="disabled"></j-upload>
</a-modal>
</div>
</template>
<script>
import JUpload from '@/components/jeecg/JUpload'
import { getFileAccessHttpUrl } from '@/api/manage';
const getFileName=(path)=>{
if(path.lastIndexOf("\\")>=0){
let reg=new RegExp("\\\\","g");
path = path.replace(reg,"/");
}
return path.substring(path.lastIndexOf("/")+1);
}
export default {
name: 'JFilePop',
components: { JUpload },
props:{
title:{
type:String,
default:'',
required:false
},
position:{
type:String,
default:'right',
required:false
},
height:{
type:Number,
default:200,
required:false
},
width:{
type:Number,
default:520,
required:false
},
popContainer:{
type:String,
default:'',
required:false
},
disabled:{
type:Boolean,
default:false,
required:false
}
},
data(){
return {
visible:false,
filePath:'',
id:'',
fileType:'file'
}
},
methods:{
handleChange(value){
this.filePath = value;
},
show(id,value,flag){
this.id = id;
this.filePath = value;
this.visible=true
if(flag === 'img'){
this.fileType = 'image'
}else{
this.fileType = 'file'
}
},
ok(){
if(!this.filePath){
this.$message.error("未上传任何文件")
return false;
}
let arr = this.filePath.split(",")
let obj = {
name:getFileName(arr[0]),
url:getFileAccessHttpUrl(arr[0]),
path:this.filePath,
status: 'done',
id:this.id
}
this.$emit('ok',obj)
this.visible=false
},
close(){
this.visible=false
}
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,98 @@
<template>
<a-popover trigger="contextmenu" v-model="visible" :placement="position">
<!--"(node) => node.parentNode.parentNode"-->
<div slot="title">
<span>{{ title }}</span>
<span style="float: right" title="关闭">
<a-icon type="close" @click="visible=false"/>
</span>
</div>
<a-input :value="inputContent" @change="handleInputChange">
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
</a-input>
<div slot="content">
<textarea :value="inputContent" @input="handleInputChange" :style="{ height: height + 'px', width: width + 'px' }"></textarea>
</div>
</a-popover>
</template>
<script>
export default {
name: 'JInputPop',
props:{
title:{
type:String,
default:'',
required:false
},
position:{
type:String,
default:'right',
required:false
},
height:{
type:Number,
default:200,
required:false
},
width:{
type:Number,
default:150,
required:false
},
value:{
type:String,
required:false
},
popContainer:{
type:String,
default:'',
required:false
}
},
data(){
return {
visible:false,
inputContent:''
}
},
watch:{
value:{
immediate:true,
handler:function(){
if(this.value && this.value.length>0){
this.inputContent = this.value;
}
}
},
},
model: {
prop: 'value',
event: 'change'
},
methods:{
handleInputChange(event){
this.inputContent = event.target.value
this.$emit('change',this.inputContent)
},
pop(){
this.visible=true
},
getPopupContainer(node){
if(!this.popContainer){
return node.parentNode
}else{
return document.getElementById(this.popContainer)
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -17,7 +17,7 @@
</a-row>
<a-row>
<a-radio value="2">每隔
<a-input-number size="small" v-model="result.second.incrementIncrement" :min="1" :max="60"></a-input-number>
<a-input-number size="small" v-model="result.second.incrementIncrement" :min="1" :max="59"></a-input-number>
秒执行 从
<a-input-number size="small" v-model="result.second.incrementStart" :min="0" :max="59"></a-input-number>
秒开始
@ -31,7 +31,7 @@
</a-row>
<a-row>
<a-radio value="4">周期从
<a-input-number size="small" v-model="result.second.rangeStart" :min="1" :max="60"></a-input-number>
<a-input-number size="small" v-model="result.second.rangeStart" :min="1" :max="59"></a-input-number>
<a-input-number size="small" v-model="result.second.rangeEnd" :min="0" :max="59"></a-input-number>
@ -829,7 +829,7 @@
}
</script>
<style lang="scss">
<style lang="less">
.card-container {
background: #fff;
overflow: hidden;
@ -877,7 +877,7 @@
}
}
</style>
<style lang="scss" scoped>
<style lang="less" scoped>
.container-widthEn{
width: 755px;
}

View File

@ -0,0 +1,334 @@
<template>
<a-modal
centered
:title="name + '选择'"
:width="width"
:visible="visible"
@ok="handleOk"
@cancel="close"
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="24">
<a-col :span="14">
<a-form-item :label="(queryParamText||name)">
<a-input v-model="queryParam[queryParamCode||valueKey]" :placeholder="'请输入' + (queryParamText||name)" @pressEnter="searchQuery"/>
</a-form-item>
</a-col>
<a-col :span="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<a-table
size="small"
bordered
:rowKey="rowKey"
:columns="innerColumns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:scroll="{ y: 240 }"
:rowSelection="{selectedRowKeys, onChange: onSelectChange, type: multiple ? 'checkbox':'radio'}"
:customRow="customRowFn"
@change="handleTableChange">
</a-table>
</a-col>
<a-col :span="8">
<a-card :title="'已选' + name" :bordered="false" :head-style="{padding:0}" :body-style="{padding:0}">
<a-table size="small" :rowKey="rowKey" bordered v-bind="selectedTable">
<span slot="action" slot-scope="text, record, index">
<a @click="handleDeleteSelected(record, index)">删除</a>
</span>
</a-table>
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { getAction } from '@/api/manage'
import Ellipsis from '@/components/Ellipsis'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { cloneObject, pushIfNotExist } from '@/utils/util'
export default {
name: 'JSelectBizComponentModal',
mixins: [JeecgListMixin],
components: { Ellipsis },
props: {
value: {
type: Array,
default: () => []
},
visible: {
type: Boolean,
default: false
},
valueKey: {
type: String,
required: true
},
multiple: {
type: Boolean,
default: true
},
width: {
type: Number,
default: 900
},
name: {
type: String,
default: ''
},
listUrl: {
type: String,
required: true,
default: ''
},
// 根据 value 获取显示文本的地址,例如存的是 username可以通过该地址获取到 realname
valueUrl: {
type: String,
default: ''
},
displayKey: {
type: String,
default: null
},
columns: {
type: Array,
required: true,
default: () => []
},
// 查询条件Code
queryParamCode: {
type: String,
default: null
},
// 查询条件文字
queryParamText: {
type: String,
default: null
},
rowKey: {
type: String,
default: 'id'
},
// 过长裁剪长度,设置为 -1 代表不裁剪
ellipsisLength: {
type: Number,
default: 12
},
},
data() {
return {
innerValue: [],
// 已选择列表
selectedTable: {
pagination: false,
scroll: { y: 240 },
columns: [
{
...this.columns[0],
width: this.columns[0].widthRight || this.columns[0].width,
},
{ title: '操作', dataIndex: 'action', align: 'center', width: 60, scopedSlots: { customRender: 'action' }, }
],
dataSource: [],
},
renderEllipsis: (value) => (<ellipsis length={this.ellipsisLength}>{value}</ellipsis>),
url: { list: this.listUrl },
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 5,
pageSizeOptions: ['5', '10', '20', '30'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' 共' + total + '条'
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
options: [],
dataSourceMap: {},
}
},
computed: {
// 表头
innerColumns() {
let columns = cloneObject(this.columns)
columns.forEach(column => {
// 给所有的列加上过长裁剪
if (this.ellipsisLength !== -1) {
column.customRender = (text) => this.renderEllipsis(text)
}
})
return columns
},
},
watch: {
value: {
deep: true,
immediate: true,
handler(val) {
this.innerValue = cloneObject(val)
this.selectedRowKeys = []
this.valueWatchHandler(val)
this.queryOptionsByValue(val)
}
},
dataSource: {
deep: true,
handler(val) {
this.emitOptions(val)
this.valueWatchHandler(this.innerValue)
}
},
selectedRowKeys: {
immediate: true,
deep: true,
handler(val) {
this.selectedTable.dataSource = val.map(key => {
for (let data of this.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
for (let data of this.selectedTable.dataSource) {
if (data[this.rowKey] === key) {
pushIfNotExist(this.innerValue, data[this.valueKey])
return data
}
}
console.warn('未找到选择的行信息key' + key)
return {}
})
},
}
},
methods: {
/** 关闭弹窗 */
close() {
this.$emit('update:visible', false)
},
valueWatchHandler(val) {
val.forEach(item => {
this.dataSource.concat(this.selectedTable.dataSource).forEach(data => {
if (data[this.valueKey] === item) {
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
}
})
})
},
queryOptionsByValue(value) {
if (!value || value.length === 0) {
return
}
// 判断options是否存在value如果已存在数据就不再请求后台了
let notExist = false
for (let val of value) {
let find = false
for (let option of this.options) {
if (val === option.value) {
find = true
break
}
}
if (!find) {
notExist = true
break
}
}
if (!notExist) return
getAction(this.valueUrl || this.listUrl, {
// 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
[this.valueKey]: value.join(',') + ',',
pageNo: 1,
pageSize: value.length
}).then((res) => {
if (res.success) {
let dataSource = res.result
if (!(dataSource instanceof Array)) {
dataSource = res.result.records
}
this.emitOptions(dataSource, (data) => {
pushIfNotExist(this.innerValue, data[this.valueKey])
pushIfNotExist(this.selectedRowKeys, data[this.rowKey])
pushIfNotExist(this.selectedTable.dataSource, data, this.rowKey)
})
}
})
},
emitOptions(dataSource, callback) {
dataSource.forEach(data => {
let key = data[this.valueKey]
this.dataSourceMap[key] = data
pushIfNotExist(this.options, { label: data[this.displayKey || this.valueKey], value: key }, 'value')
typeof callback === 'function' ? callback(data) : ''
})
this.$emit('options', this.options, this.dataSourceMap)
},
/** 完成选择 */
handleOk() {
let value = this.selectedTable.dataSource.map(data => data[this.valueKey])
this.$emit('input', value)
this.close()
},
/** 删除已选择的 */
handleDeleteSelected(record, index) {
this.selectedRowKeys.splice(this.selectedRowKeys.indexOf(record[this.rowKey]), 1)
this.selectedTable.dataSource.splice(index, 1)
},
customRowFn(record) {
return {
on: {
click: () => {
let key = record[this.rowKey]
if (!this.multiple) {
this.selectedRowKeys = [key]
this.selectedTable.dataSource = [record]
} else {
let index = this.selectedRowKeys.indexOf(key)
if (index === -1) {
this.selectedRowKeys.push(key)
this.selectedTable.dataSource.push(record)
} else {
this.handleDeleteSelected(record, index)
}
}
}
}
}
},
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -0,0 +1,36 @@
# JSelectBizComponent
Jeecg 选择组件的公共可复用组件
## 引用方式
```js
import JSelectBizComponent from '@/src/components/jeecgbiz/JSelectBizComponent'
export default {
components: { JSelectBizComponent }
}
```
## 参数
### 配置参数
| 参数名 | 类型 | 必填 | 默认值 | 备注 |
|-----------------------|---------|------|--------------|--------------------------------------------------------------------------------------|
| rowKey | String | | "id" | 唯一标识的字段名 |
| value(v-model) | String | | "" | 默认选择的数据,多个用半角逗号分割 |
| name | String | | "" | 显示名字,例如选择用户就填写"用户" |
| listUrl | String | 是 | | 数据请求地址,必须是封装了分页的地址 |
| valueUrl | String | | "" | 获取显示文本的地址,例如存的是 username可以通过该地址获取到 realname |
| displayKey | String | | null | 显示在标签上的字段 key ,不传则直接显示数据 |
| returnKeys | Array | | ['id', 'id'] | v-model 绑定的 keys是个数组默认使用第二项当配置了 `returnId=true` 就返回第一项 |
| returnId | Boolean | | false | 返回ID设为true后将返回配置的 `returnKeys` 中的第一项 |
| selectButtonText | String | | "选择" | 选择按钮的文字 |
| queryParamText | String | | null | 查询条件显示文字,不传则使用 `name` |
| columns | Array | 是 | | 列配置项与antd的table的配置完全一致。列的第一项会被配置成右侧已选择的列表上 |
| columns[0].widthRight | String | | null | 仅列的第一项可以应用此配置,表示右侧已选择列表的宽度,建议 `70%`,不传则应用`width` |
| placeholder | String | | "请选择" | 占位符 |
| disabled | Boolean | | false | 是否禁用 |
| multiple | Boolean | | false | 是否可多选 |
| buttons | Boolean | | true | 是否显示"选择"按钮,如果不显示,可以直接点击文本框打开选择界面 |

View File

@ -0,0 +1,165 @@
<template>
<a-row class="j-select-biz-component-box" type="flex" :gutter="8">
<a-col class="left" :class="{'full': !buttons}">
<slot name="left">
<a-select
mode="multiple"
:placeholder="placeholder"
v-model="selectValue"
:options="selectOptions"
allowClear
:disabled="disabled"
:open="selectOpen"
style="width: 100%;"
@dropdownVisibleChange="handleDropdownVisibleChange"
@click.native="visible=(buttons?visible:true)"
/>
</slot>
</a-col>
<a-col v-if="buttons" class="right">
<a-button type="primary" icon="search" :disabled="disabled" @click="visible=true">{{selectButtonText}}</a-button>
</a-col>
<j-select-biz-component-modal
v-model="selectValue"
:visible.sync="visible"
v-bind="modalProps"
@options="handleOptions"
/>
</a-row>
</template>
<script>
import JSelectBizComponentModal from './JSelectBizComponentModal'
export default {
name: 'JSelectBizComponent',
components: { JSelectBizComponentModal },
props: {
value: {
type: String,
default: ''
},
/** 是否返回 id默认 false返回 code */
returnId: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
// 是否支持多选,默认 true
multiple: {
type: Boolean,
default: true
},
// 是否显示按钮,默认 true
buttons: {
type: Boolean,
default: true
},
// 显示的 Key
displayKey: {
type: String,
default: null
},
// 返回的 key
returnKeys: {
type: Array,
default: () => ['id', 'id']
},
// 选择按钮文字
selectButtonText: {
type: String,
default: '选择'
},
},
data() {
return {
selectValue: [],
selectOptions: [],
dataSourceMap: {},
visible: false,
selectOpen: false,
}
},
computed: {
valueKey() {
return this.returnId ? this.returnKeys[0] : this.returnKeys[1]
},
modalProps() {
return Object.assign({
valueKey: this.valueKey,
multiple: this.multiple,
returnKeys: this.returnKeys,
displayKey: this.displayKey || this.valueKey
}, this.$attrs)
},
},
watch: {
value: {
immediate: true,
handler(val) {
if (val) {
this.selectValue = val.split(',')
} else {
this.selectValue = []
}
}
},
selectValue: {
deep: true,
handler(val) {
let rows = val.map(key => this.dataSourceMap[key])
this.$emit('select', rows)
let data = val.join(',')
this.$emit('input', data)
this.$emit('change', data)
}
}
},
methods: {
handleOptions(options, dataSourceMap) {
this.selectOptions = options
this.dataSourceMap = dataSourceMap
},
handleDropdownVisibleChange() {
// 解决antdv自己的bug —— open 设置为 false 了,点击后还是添加了 open 样式,导致点击事件失效
this.selectOpen = true
this.$nextTick(() => {
this.selectOpen = false
})
},
}
}
</script>
<style lang="less" scoped>
.j-select-biz-component-box {
@width: 82px;
.left {
width: calc(100% - @width - 8px);
}
.right {
width: @width;
}
.full {
width: 100%;
}
/deep/ .ant-select-search__field {
display: none !important;
}
}
</style>

View File

@ -11,7 +11,7 @@
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="value"
:depart-id="departIds"
@ok="handleOK"
@initComp="initComp"/>
</div>
@ -48,6 +48,11 @@
type: Boolean,
required: false,
default: false
},
// 自定义返回字段,默认返回 id
customReturnField: {
type: String,
default: 'id'
}
},
data(){
@ -63,7 +68,9 @@
},
watch:{
value(val){
this.departIds = val
if (this.customReturnField === 'id') {
this.departIds = val
}
}
},
methods:{
@ -73,21 +80,17 @@
openModal(){
this.$refs.innerDepartSelectModal.show()
},
handleOK(rows,idstr){
console.log("当前选中部门",rows)
console.log("当前选中部门ID",idstr)
if(!rows){
handleOK(rows, idstr) {
let value = ''
if (!rows && rows.length <= 0) {
this.departNames = ''
this.departIds=''
}else{
let temp = ''
for(let item of rows){
temp+=','+item.departName
}
this.departNames = temp.substring(1)
this.departIds=idstr
this.departIds = ''
} else {
value = rows.map(row => row[this.customReturnField]).join(',')
this.departNames = rows.map(row => row['departName']).join(',')
this.departIds = idstr
}
this.$emit("change",this.departIds)
this.$emit("change", value)
},
getDepartNames(){
return this.departNames

View File

@ -1,65 +1,47 @@
<template>
<div style="width: 100%;">
<a-select
mode="multiple"
placeholder="Please select"
:value="nameList"
style="width: calc(100% - 178px);">
</a-select>
<span style="display: inline-block;width:170px;float: right;overflow: hidden;">
<a-button type="primary" @click="handleSelect" icon="search" style="width: 81px">选择</a-button>
<a-button type="primary" @click="selectReset" icon="reload" style="margin-left: 8px;width: 81px">清空</a-button>
</span>
<!-- 选择多个用户支持排序 -->
<j-select-multi-user-modal ref="selectModal" @selectFinished="selectOK"/>
</div>
<!-- 定义在这里的参数都是不可在外部覆盖的防止出现问题 -->
<j-select-biz-component
:value="value"
:ellipsisLength="25"
:listUrl="url.list"
:columns="columns"
v-on="$listeners"
v-bind="attrs"
/>
</template>
<script>
import JSelectMultiUserModal from './modal/JSelectMultiUserModal'
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectMultiUser',
components:{ JSelectMultiUserModal },
props:{
value:{
type:String,
required:false
}
},
data(){
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
selectList: [],
url: { list: '/sys/user/list' },
columns: [
{ title: '姓名', align: 'center', width: '25%', widthRight: '70%', dataIndex: 'realname' },
{ title: '账号', align: 'center', width: '25%', dataIndex: 'username' },
{ title: '电话', align: 'center', width: '20%', dataIndex: 'phone' },
{ title: '出生日期', align: 'center', width: '20%', dataIndex: 'birthday' }
],
// 定义在这里的参数都是可以在外部传递覆盖的,可以更灵活的定制化使用的组件
default: {
name: '用户',
width: 1200,
displayKey: 'realname',
returnKeys: ['id', 'username'],
queryParamText: '账号',
}
}
},
computed: {
nameList: function () {
var names = [];
for (var a = 0; a < this.selectList.length; a++) {
names.push(this.selectList[a].name);
}
let nameStr = ''
if(names.length>0){
nameStr = names.join(",")
}
this.$emit("change",nameStr)
return names;
}
},
model: {
prop: 'value',
event: 'change'
},
methods:{
handleSelect: function () {
this.$refs.selectModal.add();
},
selectReset() {
this.selectList = [];
},
selectOK: function (data) {
this.selectList = data;
attrs() {
return Object.assign(this.default, this.$attrs)
}
}
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,37 @@
<template>
<j-select-biz-component :width="1000" v-bind="configs" v-on="$listeners"/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectPosition',
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
settings: {
name: '职务',
displayKey: 'name',
returnKeys: ['id', 'code'],
listUrl: '/sys/position/list',
queryParamCode: 'name',
queryParamText: '职务名称',
columns: [
{ title: '职务名称', dataIndex: 'name', align: 'center', width: '30%', widthRight: '70%' },
{ title: '职务编码', dataIndex: 'code', align: 'center', width: '35%' },
{ title: '职级', dataIndex: 'rank_dictText', align: 'center', width: '25%' }
]
}
}
},
computed: {
configs() {
return Object.assign({ value: this.value }, this.settings, this.$attrs)
}
}
}
</script>
<style lang="less" scoped></style>

View File

@ -0,0 +1,38 @@
<template>
<j-select-biz-component
:value="value"
name="角色"
displayKey="roleName"
:returnKeys="returnKeys"
:listUrl="url.list"
:columns="columns"
queryParamText="角色编码"
v-on="$listeners"
v-bind="$attrs"
/>
</template>
<script>
import JSelectBizComponent from './JSelectBizComponent'
export default {
name: 'JSelectMultiUser',
components: { JSelectBizComponent },
props: ['value'],
data() {
return {
returnKeys: ['id', 'roleCode'],
url: { list: '/sys/role/list' },
columns: [
{ title: '角色名称', dataIndex: 'roleName', align: 'center', width: 120 },
{ title: '角色编码', dataIndex: 'roleCode', align: 'center', width: 120 }
]
}
}
}
</script>
<style lang="less" scoped></style>

View File

@ -1,51 +1,56 @@
<template>
<div>
<a-input-search
v-model="selectedDepUsers"
v-model="userNames"
placeholder="请先选择用户"
disabled
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"
@ok="onSearchDepUserCallBack" />
<j-select-user-by-dep-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" @ok="selectOK" :user-ids="value" @initComp="initComp"/>
</div>
</template>
<script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
export default {
name: 'JSelectUserByDep',
components: { JSelectUserByDepModal },
props:{
modalWidth:{
type:Number,
default:1250,
required:false
components: {JSelectUserByDepModal},
props: {
modalWidth: {
type: Number,
default: 1250,
required: false
},
value:{
type:String,
required:false
value: {
type: String,
required: false
},
disabled:{
disabled: {
type: Boolean,
required: false,
default: false
}
},
multi: {
type: Boolean,
default: true,
required: false
},
},
data() {
return {
selectedDepUsers:"",
userIds: "",
userNames: ""
}
},
mounted(){
this.selectedDepUsers = this.value
mounted() {
this.userIds = this.value
},
watch:{
value(val){
this.selectedDepUsers = val
watch: {
value(val) {
this.userIds = val
}
},
model: {
@ -53,14 +58,27 @@
event: 'change'
},
methods: {
//通过组织机构筛选选择用户
initComp(userNames) {
this.userNames = userNames
},
onSearchDepUser() {
this.$refs.selectModal.showModal()
this.onSearchDepUserCallBack('')
},
onSearchDepUserCallBack(selectedDepUsers) {
this.selectedDepUsers = selectedDepUsers
this.$emit("change",selectedDepUsers)
selectOK(rows, idstr) {
console.log("当前选中用户", rows)
console.log("当前选中用户ID", idstr)
if (!rows) {
this.userNames = ''
this.userIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.realname
}
this.userNames = temp.substring(1)
this.userIds = idstr
}
this.$emit("change", this.userIds)
}
}
}

View File

@ -63,7 +63,7 @@
handler() {
if (this.departId) {
this.checkedKeys = this.departId.split(",");
console.log('this.departId', this.departId)
// console.log('this.departId', this.departId)
} else {
this.checkedKeys = [];
}
@ -75,7 +75,6 @@
this.visible=true
this.checkedRows=[]
this.checkedKeys=[]
console.log("this.multi",this.multi)
},
loadDepart(){
queryDepartTreeList().then(res=>{
@ -133,39 +132,29 @@
},
onCheck (checkedKeys,info) {
if(!this.multi){
let arr = checkedKeys.checked.filter(item=>{
return this.checkedKeys.indexOf(item)<0
})
let arr = checkedKeys.checked.filter(item => this.checkedKeys.indexOf(item) < 0)
this.checkedKeys = [...arr]
this.checkedRows=[info.node.dataRef]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
}else{
this.checkedKeys = checkedKeys.checked
this.checkedRows.push(info.node.dataRef)
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
//this.$emit("input",this.checkedKeys.join(","))
//console.log(this.checkedKeys.join(","))
},
onSelect (selectedKeys,info) {
console.log(selectedKeys)
onSelect(selectedKeys,info) {
let keys = []
keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length==0 || !this.multi){
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=>{
return item !=currKey
})
this.checkedRows=this.checkedRows.filter(item=>{
return item.key !=currKey
})
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
}else{
this.checkedRows.push(info.node.dataRef)
this.checkedKeys.push(...keys)
}
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
@ -215,6 +204,32 @@
})
},
// 根据 checkedKeys 获取 rows
getCheckedRows(checkedKeys) {
const forChildren = (list, key) => {
for (let item of list) {
if (item.id === key) {
return item
}
if (item.children instanceof Array) {
let value = forChildren(item.children, key)
if (value != null) {
return value
}
}
}
return null
}
let rows = []
for (let key of checkedKeys) {
let row = forChildren(this.treeData, key)
if (row != null) {
rows.push(row)
}
}
return rows
}
}
}

View File

@ -1,274 +0,0 @@
<template>
<a-modal
centered
:title="title"
:width="1000"
:visible="visible"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭">
<a-row :gutter="18">
<a-col :span="16">
<a-card title="选择人员" :bordered="true">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline">
<a-row :gutter="24">
<a-col :span="10">
<a-form-item label="姓名">
<a-input placeholder="请输入姓名" v-model="queryParam.name"></a-input>
</a-form-item>
</a-col>
<a-col :span="8" >
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- table区域-begin -->
<div>
<a-table
size="small"
bordered
rowKey="id"
:columns="columns1"
:dataSource="dataSource1"
:pagination="ipagination"
:loading="loading"
:scroll="{ y: 240 }"
:rowSelection="{selectedRowKeys: selectedRowKeys,onSelectAll:onSelectAll,onSelect:onSelect,onChange: onSelectChange}"
@change="handleTableChange">
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</a-col>
<a-col :span="8">
<a-card title="用户选择" :bordered="true">
<!-- table区域-begin -->
<div>
<a-table
size="small"
bordered
rowKey="id"
:columns="columns2"
:dataSource="dataSource2"
:loading="loading"
:scroll="{ y: 240 }"
>
<span slot="action" slot-scope="text, record">
<a-button type="primary" size="small" @click="handleDelete(record)" icon="delete">删除</a-button>
</span>
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</a-col>
</a-row>
</a-modal>
</template>
<script>
import { filterObj } from '@/utils/util'
import { getAction } from '@/api/manage'
export default {
name: 'JSelectMultiUserModal',
data () {
return {
title: "用户列表",
names: [],
visible: false,
placement: 'right',
description: '人员管理页面',
// 查询条件
queryParam: {},
// 表头
columns1: [
{
title: '#',
dataIndex: '',
key:'rowIndex',
width:50,
align:"center",
customRender:function (t,r,index) {
return parseInt(index)+1;
}
},
{
title: '姓名',
align:"center",
width:113,
dataIndex: 'name'
},
{
title: '年龄',
align:"center",
width:100,
dataIndex: 'age'
},
{
title: '出生日期',
align:"center",
width:100,
dataIndex: 'birthday'
}
],
columns2: [
{
title: '用户账号',
align:"center",
width:100,
dataIndex: 'name'
},
{
title: '操作',
dataIndex: 'action',
align:"center",
width:100,
scopedSlots: { customRender: 'action' },
}
],
//数据集
dataSource1:[],
dataSource2:[],
// 分页参数
ipagination:{
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '20', '30'],
showTotal: (total, range) => {
return range[0] + "-" + range[1] + " " + total + ""
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
isorter:{
column: 'createTime',
order: 'desc',
},
loading:false,
selectedRowKeys: [],
selectedRows: [],
url: {
list: "/test/jeecgDemo/list",
},
}
},
created() {
this.loadData();
},
methods: {
searchQuery(){
this.loadData(1);
},
searchReset(){
this.queryParam={};
this.loadData(1);
},
handleCancel() {
this.visible = false;
},
handleOk() {
this.$emit("selectFinished",this.dataSource2);
this.visible = false;
},
add() {
this.visible = true;
},
loadData (arg){
//加载数据 若传入参数1则加载第一页的内容
if(arg===1){
this.ipagination.current = 1;
}
var params = this.getQueryParams();//查询条件
getAction(this.url.list,params).then((res)=>{
if(res.success){
this.dataSource1 = res.result.records;
this.ipagination.total = res.result.total;
}
})
},
getQueryParams(){
var param = Object.assign({}, this.queryParam,this.isorter);
param.field = this.getQueryField();
param.pageNo = this.ipagination.current;
param.pageSize = this.ipagination.pageSize;
return filterObj(param);
},
getQueryField(){
//TODO 字段权限控制
},
onSelectAll (selected, selectedRows, changeRows) {
if(selected===true){
for(var a = 0;a<changeRows.length;a++){
this.dataSource2.push(changeRows[a]);
}
}else{
for(var b = 0;b<changeRows.length;b++){
this.dataSource2.splice(this.dataSource2.indexOf(changeRows[b]),1);
}
}
// console.log(selected, selectedRows, changeRows);
},
onSelect (record,selected) {
if(selected===true){
this.dataSource2.push(record);
}else{
var index = this.dataSource2.indexOf(record);
//console.log();
if(index >=0 ){
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
}
}
},
onSelectChange (selectedRowKeys,selectedRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectionRows = selectedRows;
},
onClearSelected(){
this.selectedRowKeys = [];
this.selectionRows = [];
},
handleDelete: function(record){
this.dataSource2.splice(this.dataSource2.indexOf(record),1);
},
handleTableChange(pagination, filters, sorter){
//分页、排序、筛选变化时触发
console.log(sorter);
//TODO 筛选
if (Object.keys(sorter).length>0){
this.isorter.column = sorter.field;
this.isorter.order = "ascend"==sorter.order?"asc":"desc"
}
this.ipagination = pagination;
this.loadData();
}
}
}
</script>
<style lang="less" scoped>
.ant-card-body .table-operator{
margin-bottom: 18px;
}
.ant-table-tbody .ant-table-row td{
padding-top:15px;
padding-bottom:15px;
}
.anty-row-operator button{margin: 0 5px}
.ant-btn-danger{background-color: #ffffff}
.ant-modal-cust-warp{height: 100%}
.ant-modal-cust-warp .ant-modal-body{height:calc(100% - 110px) !important;overflow-y: auto}
.ant-modal-cust-warp .ant-modal-content{height:90% !important;overflow-y: hidden}
</style>

View File

@ -15,11 +15,13 @@
<!--组织机构-->
<a-directory-tree
selectable
:selectedKeys="selectedKeys"
:selectedKeys="selectedDepIds"
:checkStrictly="true"
@select="this.onSelect"
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
:expandAction="false"
:expandedKeys.sync="expandedKeys"
@select="onDepSelect"
/>
</a-card>
</a-col>
@ -28,7 +30,7 @@
用户账号:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入用户账号"
placeholder="请输入账号"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
@ -42,7 +44,8 @@
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange,type: getType}"
:loading="loading"
@change="handleTableChange">
</a-table>
</a-card>
@ -52,16 +55,17 @@
</template>
<script>
import { filterObj } from '@/utils/util'
import { queryDepartTreeList, getUserList, queryUserByDepId, queryUserRoleMap } from '@/api/api'
import {filterObj} from '@/utils/util'
import {queryDepartTreeList, getUserList, queryUserByDepId} from '@/api/api'
export default {
name: 'JSelectUserByDepModal',
components: {},
props:['modalWidth'],
props: ['modalWidth', 'multi', 'userIds'],
data() {
return {
queryParam: {
username:"",
username: "",
},
columns: [
{
@ -70,20 +74,15 @@
dataIndex: 'username'
},
{
title: '真实姓名',
title: '用户姓名',
align: 'center',
dataIndex: 'realname'
},
{
title: '角色名称',
align: 'center',
dataIndex: 'roleName'
},
{
title: '性别',
align: 'center',
dataIndex: 'sex',
customRender: function(text) {
customRender: function (text) {
if (text === 1) {
return '男'
} else if (text === 2) {
@ -94,22 +93,21 @@
}
},
{
title: '手机号码',
title: '手机',
align: 'center',
dataIndex: 'phone'
},
{
title: '邮箱',
title: '部门',
align: 'center',
dataIndex: 'email'
dataIndex: 'orgCode'
}
],
scrollTrigger: {},
dataSource: [],
selectedKeys: [],
userNameArr: [],
departName: '',
userRolesMap: {},
selectedRowKeys: [],
selectUserRows: [],
selectUserIds: [],
title: '根据部门选择用户',
ipagination: {
current: 1,
@ -126,53 +124,92 @@
column: 'createTime',
order: 'desc'
},
selectedRowKeys: [],
selectedRows: [],
selectedDepIds: [],
departTree: [],
visible: false,
form: this.$form.createForm(this)
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
}
},
computed: {
// 计算属性的 getter
getType: function () {
return this.multi == true ? 'checkbox' : 'radio';
}
},
watch: {
userIds: {
immediate: true,
handler() {
this.initUserNames()
}
},
},
created() {
// 该方法触发屏幕自适应
this.resetScreenSize();
this.queryUserRoleMap();
this.loadData()
},
methods: {
loadData(arg) {
initUserNames() {
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'])
selectedRowKeys.push(user['id'])
})
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', realNames.join(','))
}
})
} else {
// JSelectUserByDep组件bug issues/I16634
this.$emit('initComp', '')
}
},
async loadData(arg) {
if (arg === 1) {
this.ipagination.current = 1;
}
let params = this.getQueryParams();//查询条件
getUserList(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records;
this.assignRoleName(this.dataSource);
this.ipagination.total = res.result.total;
}
})
},
queryUserRoleMap(){
queryUserRoleMap().then((res) => {
if (res.success) {
this.userRolesMap = res.result;
this.loadData();
}
})
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
})
}
},
// 触发屏幕自适应
resetScreenSize() {
let screenWidth = document.body.clientWidth;
if (screenWidth < 500) {
this.scrollTrigger = { x: 800 };
this.scrollTrigger = {x: 800};
} else {
this.scrollTrigger = {};
}
},
showModal() {
this.visible = true;
this.assignRoleName(this.dataSource);
this.queryDepartTree();
this.initUserNames()
this.loadData();
this.form.resetFields();
},
getQueryParams() {
@ -191,13 +228,13 @@
},
searchReset(num) {
let that = this;
if(num !== 0){
if (num !== 0) {
that.queryParam = {};
that.loadData(1);
}
that.selectedRowKeys = [];
that.userNameArr = [];
that.selectedKeys = [];
that.selectUserIds = [];
that.selectedDepIds = [];
},
close() {
this.searchReset(0);
@ -214,27 +251,30 @@
},
handleSubmit() {
let that = this;
for (let i = 0, len = this.selectedRowKeys.length; i < len; i++) {
this.getUserNames(this.selectedRowKeys[i]);
}
that.$emit('ok', that.userNameArr.join(','));
this.getSelectUserRows();
that.$emit('ok', that.selectUserRows, that.selectUserIds);
that.searchReset(0)
that.close();
},
// 遍历匹配,获取用户真实姓名
getUserNames(rowId) {
//获取选择用户信息
getSelectUserRows(rowId) {
let dataSource = this.dataSource;
let userIds = "";
this.selectUserRows = [];
for (let i = 0, len = dataSource.length; i < len; i++) {
if (rowId === dataSource[i].id) {
this.userNameArr.push(dataSource[i].realname);
if (this.selectedRowKeys.includes(dataSource[i].id)) {
this.selectUserRows.push(dataSource[i]);
userIds = userIds + "," + dataSource[i].username
}
}
this.selectUserIds = userIds.substring(1);
},
// 点击树节点,筛选出对应的用户
onSelect(selectedKeys) {
if (selectedKeys[0] != null) {
this.queryUserByDepId(selectedKeys); // 调用方法根据选选择的id查询用户信息
if (this.selectedKeys[0] !== selectedKeys[0]) {
this.selectedKeys = [selectedKeys[0]];
onDepSelect(selectedDepIds) {
if (selectedDepIds[0] != null) {
this.initQueryUserByDepId(selectedDepIds); // 调用方法根据选选择的id查询用户信息
if (this.selectedDepIds[0] !== selectedDepIds[0]) {
this.selectedDepIds = [selectedDepIds[0]];
}
}
},
@ -246,43 +286,26 @@
this.loadData(1);
},
// 根据选择的id来查询用户信息
queryUserByDepId(selectedKeys) {
queryUserByDepId({ id: selectedKeys.toString() }).then((res) => {
initQueryUserByDepId(selectedDepIds) {
this.loading = true
return queryUserByDepId({id: selectedDepIds.toString()}).then((res) => {
if (res.success) {
this.dataSource = res.result;
this.ipagination.total = res.result.length;
this.assignRoleName(this.dataSource);
}
}).finally(() => {
this.loading = false
})
},
// 传入用户id,找到匹配的角色名称
queryUserRole(userId) {
let map = this.userRolesMap;
let roleName = [];
for (var key in map) {
if (userId === key) {
roleName.push(map[key]);
}
}
return roleName.join(',');
},
queryDepartTree() {
queryDepartTreeList().then((res) => {
if (res.success) {
this.departTree = res.result;
// 默认展开父节点
this.expandedKeys = this.departTree.map(item => item.id)
}
})
},
// 为角色名称赋值
assignRoleName(data) {
let userId = '';
let role = '';
for (let i = 0, length = data.length; i < length; i++) {
userId = this.dataSource[i].id;
role = this.queryUserRole(userId);
this.dataSource[i].roleName = role;
}
},
modalFormOk() {
this.loadData();
}

View File

@ -46,7 +46,7 @@
width: 200
},
{
title: '用户真实姓名',
title: '用户姓名',
align: "center",
dataIndex: 'realname',
},

View File

@ -33,7 +33,7 @@
}
</script>
<style lang="scss">
<style lang="less">
/*
* The following styles are auto-applied to elements with

View File

@ -5,11 +5,14 @@
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from "@/store/mutation-types"
import PageLayout from '../page/PageLayout'
import RouteView from './RouteView'
export default {
name: "IframePageContent",
inject:['closeCurrent'],
data () {
return {
url: "",
@ -36,7 +39,19 @@
console.log("------url------"+url)
if (url !== null && url !== undefined) {
this.url = url;
//window.open(this.url);
/*update_begin author:wuxianquan date:20190908 for:判断打开方式新窗口打开时this.$route.meta.internalOrExternal==true */
if(this.$route.meta.internalOrExternal != undefined && this.$route.meta.internalOrExternal==true){
this.closeCurrent();
//外部url加入token
let tokenStr = "${token}";
if(url.indexOf(tokenStr)!=-1){
let token = Vue.ls.get(ACCESS_TOKEN);
this.url = url.replace(tokenStr,token);
}
window.open(this.url);
}
/*update_end author:wuxianquan date:20190908 for:判断打开方式新窗口打开时this.$route.meta.internalOrExternal==true */
}
}
}

View File

@ -60,7 +60,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.extra-img {
margin-top: -60px;
text-align: center;

View File

@ -1,6 +1,8 @@
<template>
<global-layout @dynamicRouterShow="dynamicRouterShow">
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" @select="onMenuSelect"/>
<!-- update-begin- author:sunjianlei --- date:20191009 --- for: 提升右键菜单的层级 -->
<contextmenu :itemList="menuItemList" :visible.sync="menuVisible" style="z-index: 9999;" @select="onMenuSelect"/>
<!-- update-end- author:sunjianlei --- date:20191009 --- for: 提升右键菜单的层级 -->
<a-tabs
@contextmenu.native="e => onContextmenu(e)"
v-if="multipage"
@ -10,6 +12,7 @@
:hide-add="true"
type="editable-card"
@change="changePage"
@tabClick="tabCallBack"
@edit="editPage">
<a-tab-pane :id="page.fullPath" :key="page.fullPath" v-for="page in pageList">
<span slot="tab" :pagekey="page.fullPath">{{ page.meta.title }}</span>
@ -18,9 +21,11 @@
<div style="margin: 12px 12px 0;">
<transition name="page-toggle">
<keep-alive v-if="multipage">
<router-view/>
<router-view v-if="reloadFlag"/>
</keep-alive>
<router-view v-else/>
<template v-else>
<router-view v-if="reloadFlag"/>
</template>
</transition>
</div>
</global-layout>
@ -30,6 +35,7 @@
import GlobalLayout from '@/components/page/GlobalLayout'
import Contextmenu from '@/components/menu/Contextmenu'
import { mixin, mixinDevice } from '@/utils/mixin.js'
import { triggerWindowResizeEvent } from '@/utils/util'
const indexKey = '/dashboard/analysis'
@ -47,12 +53,21 @@
activePage: '',
menuVisible: false,
menuItemList: [
{ key: '4', icon: 'reload', text: '刷 新' },
{ key: '1', icon: 'arrow-left', text: '关闭左侧' },
{ key: '2', icon: 'arrow-right', text: '关闭右侧' },
{ key: '3', icon: 'close', text: '关闭其它' }
]
],
reloadFlag:true
}
},
/* update_begin author:wuxianquan date:20190828 for: 关闭当前tab页供子页面调用 ->望菜单能配置外链直接弹出新页面而不是嵌入iframe #428 */
provide(){
return{
closeCurrent:this.closeCurrent
}
},
/* update_end author:wuxianquan date:20190828 for: 关闭当前tab页供子页面调用->望菜单能配置外链直接弹出新页面而不是嵌入iframe #428 */
computed: {
multipage() {
//判断如果是手机模式,自动切换为单页面模式
@ -65,30 +80,43 @@
},
created() {
if (this.$route.path != indexKey) {
this.pageList.push({
name: 'dashboard-analysis',
path: indexKey,
fullPath: indexKey,
meta: {
icon: 'dashboard',
title: '首页'
}
})
this.linkList.push(indexKey)
this.addIndexToFirst()
}
// update-begin-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
let storeKey = 'route:title:' + this.$route.fullPath
let routeTitle = this.$ls.get(storeKey)
if (routeTitle) {
this.$route.meta.title = routeTitle
}
// update-end-author:sunjianlei date:20191223 for: 修复刷新后菜单Tab名字显示异常
this.pageList.push(this.$route)
this.linkList.push(this.$route.fullPath)
this.activePage = this.$route.fullPath
},
mounted() {
},
watch: {
'$route': function(newRoute) {
//console.log("新的路由",newRoute)
this.activePage = newRoute.fullPath
if (!this.multipage) {
this.linkList = [newRoute.fullPath]
this.pageList = [Object.assign({},newRoute)]
} else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
// update-begin-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
} else if(indexKey==newRoute.fullPath) {
//首页时 判断是否缓存 没有缓存 刷新之
if (newRoute.meta.keepAlive === false) {
this.routeReload()
}
// update-end-author:taoyan date:20200211 for: TASK #3368 【路由缓存】首页的缓存设置有问题,需要根据后台的路由配置来实现是否缓存
}else if (this.linkList.indexOf(newRoute.fullPath) < 0) {
this.linkList.push(newRoute.fullPath)
this.pageList.push(Object.assign({},newRoute))
// update-begin-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍
if (newRoute.meta.keepAlive) {
this.routeReload()
}
// update-end-author:sunjianlei date:20200103 for: 如果新增的页面配置了缓存路由,那么就强制刷新一遍
} else if (this.linkList.indexOf(newRoute.fullPath) >= 0) {
let oldIndex = this.linkList.indexOf(newRoute.fullPath)
let oldPositionRoute = this.pageList[oldIndex]
@ -99,18 +127,61 @@
let index = this.linkList.lastIndexOf(key)
let waitRouter = this.pageList[index]
this.$router.push(Object.assign({},waitRouter));
this.changeTitle(waitRouter.meta.title)
},
'multipage': function(newVal) {
if (!newVal) {
this.linkList = [this.$route.fullPath]
this.pageList = [this.$route]
if(this.reloadFlag){
if (!newVal) {
this.linkList = [this.$route.fullPath]
this.pageList = [this.$route]
}
}
}
},
// update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
device() {
if (this.multipage && this.linkList.indexOf(indexKey) === -1) {
this.addIndexToFirst()
}
},
// update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
},
methods: {
// update-begin-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
// 将首页添加到第一位
addIndexToFirst() {
this.pageList.splice(0, 0, {
name: 'dashboard-analysis',
path: indexKey,
fullPath: indexKey,
meta: {
icon: 'dashboard',
title: '首页'
}
})
this.linkList.splice(0, 0, indexKey)
},
// update-end-author:sunjianlei date:20191223 for: 修复从单页模式切换回多页模式后首页不居第一位的 BUG
// update-begin-author:sunjianlei date:20200120 for: 动态更改页面标题
changeTitle(title) {
let projectTitle = "Jeecg-Boot 企业级快速开发平台"
// 首页特殊处理
if (this.$route.path === indexKey) {
document.title = projectTitle
} else {
document.title = title + ' · ' + projectTitle
}
},
// update-end-author:sunjianlei date:20200120 for: 动态更改页面标题
changePage(key) {
this.activePage = key
},
tabCallBack() {
this.$nextTick(() => {
triggerWindowResizeEvent()
})
},
editPage(key, action) {
this[action](key)
},
@ -123,6 +194,7 @@
this.$message.warning('这是最后一页,不能再关闭了啦')
return
}
console.log("this.pageList ",this.pageList );
this.pageList = this.pageList.filter(item => item.fullPath !== key)
let index = this.linkList.indexOf(key)
this.linkList = this.linkList.filter(item => item !== key)
@ -157,13 +229,21 @@
case '3':
this.closeOthers(pageKey)
break
case '4':
this.routeReload()
break
default:
break
}
},
/* update_begin author:wuxianquan date:20190828 for: 关闭当前tab页供子页面调用->望菜单能配置外链直接弹出新页面而不是嵌入iframe #428 */
closeCurrent(){
this.remove(this.activePage);
},
/* update_end author:wuxianquan date:20190828 for: 关闭当前tab页供子页面调用->望菜单能配置外链直接弹出新页面而不是嵌入iframe #428 */
closeOthers(pageKey) {
let index = this.linkList.indexOf(pageKey)
if (pageKey == indexKey) {
if (pageKey == indexKey || pageKey.indexOf('?ticke=')>=0) {
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.activePage = this.linkList[0]
@ -171,7 +251,7 @@
let indexContent = this.pageList.slice(0, 1)[0]
this.linkList = this.linkList.slice(index, index + 1)
this.pageList = this.pageList.slice(index, index + 1)
this.linkList.unshift(indexKey)
this.linkList.unshift(indexContent.fullPath)
this.pageList.unshift(indexContent)
this.activePage = this.linkList[1]
}
@ -185,7 +265,7 @@
let index = this.linkList.indexOf(pageKey)
this.linkList = this.linkList.slice(index)
this.pageList = this.pageList.slice(index)
this.linkList.unshift(indexKey)
this.linkList.unshift(indexContent.fullPath)
this.pageList.unshift(indexContent)
if (this.linkList.indexOf(this.activePage) < 0) {
this.activePage = this.linkList[0]
@ -206,14 +286,29 @@
let currRouter = this.pageList[keyIndex]
let meta = Object.assign({},currRouter.meta,{title:title})
this.pageList.splice(keyIndex, 1, Object.assign({},currRouter,{meta:meta}))
if (key === this.activePage) {
this.changeTitle(title)
}
}
}
},
//update-end-author:taoyan date:20190430 for:动态路由title显示配置的菜单title而不是其对应路由的title
//update-begin-author:taoyan date:20191008 for:路由刷新
routeReload(){
this.reloadFlag = false
let ToggleMultipage = "ToggleMultipage"
this.$store.dispatch(ToggleMultipage,false)
this.$nextTick(()=>{
this.$store.dispatch(ToggleMultipage,true)
this.reloadFlag = true
})
}
//update-end-author:taoyan date:20191008 for:路由刷新
}
}
</script>
<style lang="scss">
<style lang="less">
/*
* The following styles are auto-applied to elements with
@ -291,7 +386,7 @@
border-bottom: 1px solid transparent !important;
}
.ant-tabs-tab-active {
border-color: #1890ff !important;
border-color: @primary-color!important;
}
}

View File

@ -17,12 +17,12 @@
<div class="footer">
<div class="links">
<a href="http://jeecg-boot.mydoc.io" target="_blank">帮助</a>
<a href="http://doc.jeecg.com" target="_blank">帮助</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">隐私</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">条款</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE" target="_blank">条款</a>
</div>
<div class="copyright">
Copyright &copy; 2019 <a href="http://www.jeecg.org" target="_blank">JEECG开源社区</a> 出品
Copyright &copy; 2019 <a href="http://www.jeecg.com" target="_blank">JEECG开源社区</a> 出品
</div>
</div>
</div>
@ -49,7 +49,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
#userLayout.user-layout-wrapper {
height: 100%;

View File

@ -43,7 +43,7 @@ export default {
},
methods: {
closeMenu (e) {
if (['menuitemicon', 'menuitem'].indexOf(e.target.getAttribute('role')) < 0) {
if (this.visible === true && ['menuitemicon', 'menuitem'].indexOf(e.target.getAttribute('role')) < 0) {
this.$emit('update:visible', false)
}
},

View File

@ -72,18 +72,18 @@
}
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
/* update_begin author:sunjianlei date:20190509 for: 修改侧边导航栏滚动条的样式 */
.sider {
$scrollBarSize: 10px;
@scrollBarSize: 10px;
ul.ant-menu {
/* 定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
&::-webkit-scrollbar {
width: $scrollBarSize;
height: $scrollBarSize;
width: @scrollBarSize;
height: @scrollBarSize;
background-color: transparent;
display: none;
}
@ -109,7 +109,7 @@
/* 定义滑块 */
&::-webkit-scrollbar-thumb {
border-radius: $scrollBarSize;
border-radius: @scrollBarSize;
background-color: #eee;
box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1);
@ -145,7 +145,7 @@
</style>
<!-- update_begin author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
<style lang="scss">
<style lang="less">
.ant-menu.ant-menu-root {
& > .ant-menu-item:first-child {
background-color: transparent;
@ -156,7 +156,7 @@
&.ant-menu-item-selected {
& > a, & > a:hover {
color: #1890ff;
color: @primary-color;
}
}
}

View File

@ -1,7 +1,7 @@
<template>
<div class="footer">
<div class="links">
<a href="http://www.jeecg.org" target="_blank">JEECG 首页</a>
<a href="http://www.jeecg.com" target="_blank">JEECG 首页</a>
<a href="https://github.com/zhangdaiscott/jeecg-boot" target="_blank">
<a-icon type="github"/>
</a>
@ -22,7 +22,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.footer {
padding: 0 16px;
margin: 48px 0 24px;

View File

@ -10,12 +10,12 @@
v-if="device==='mobile'"
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click.native="toggle"></a-icon>
@click="toggle"></a-icon>
<a-icon
v-else
class="trigger"
:type="collapsed ? 'menu-unfold' : 'menu-fold'"
@click.native="toggle"/>
@click="toggle"/>
<span v-if="device === 'desktop'">欢迎进入 Jeecg-Boot 企业级快速开发平台</span>
<span v-else>Jeecg-Boot</span>
@ -37,7 +37,7 @@
v-else
class="trigger"
:type="collapsed ? 'menu-fold' : 'menu-unfold'"
@click.native="toggle"></a-icon>
@click="toggle"></a-icon>
</div>
<user-menu class="header-index-right" :theme="theme" :style="topMenuStyle.headerIndexRight"/>
</div>
@ -161,10 +161,10 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
/* update_begin author:scott date:20190220 for: 缩小首页布局顶部的高度*/
$height: 59px;
@height: 59px;
.layout {
@ -174,8 +174,8 @@
margin-left: 10px;
.ant-menu.ant-menu-horizontal {
height: $height;
line-height: $height;
height: @height;
line-height: @height;
}
}
.trigger {
@ -189,8 +189,8 @@
.header {
z-index: 2;
color: white;
height: $height;
background-color: #1890ff;
height: @height;
background-color: @primary-color;
transition: background 300ms;
/* dark 样式 */
@ -209,8 +209,8 @@
}
.ant-layout-header {
height: $height;
line-height: $height;
height: @height;
line-height: @height;
}
/* update_end author:scott date:20190220 for: 缩小首页布局顶部的高度*/

View File

@ -74,7 +74,9 @@
</a-layout-footer>
</a-layout>
<setting-drawer></setting-drawer>
<!-- update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ---- -->
<!--<setting-drawer></setting-drawer>-->
<!-- update-end---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ---- -->
</a-layout>
</template>
@ -82,7 +84,11 @@
import SideMenu from '@/components/menu/SideMenu'
import GlobalHeader from '@/components/page/GlobalHeader'
import GlobalFooter from '@/components/page/GlobalFooter'
import SettingDrawer from '@/components/setting/SettingDrawer'
// 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'
@ -93,7 +99,11 @@
SideMenu,
GlobalHeader,
GlobalFooter,
SettingDrawer
// update-start---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
// // SettingDrawer
// 注释这个因为在个人设置模块已经加载了SettingDrawer页面
// update-end ---- author:os_chengtgen -- date:20190830 -- for:issues/463 -编译主题颜色已生效,但还一直转圈,显示主题 正在编译 ------
},
mixins: [mixin, mixinDevice],
data() {
@ -144,6 +154,10 @@
//此处触发动态路由被点击事件
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){
@ -160,7 +174,7 @@
</script>
<style lang="scss">
<style lang="less">
body {
// 打开滚动条固定显示
overflow-y: scroll;
@ -327,6 +341,10 @@
font-size: 16px;
padding: 4px;
}
.anticon {
color: white;
}
}
}
@ -339,6 +357,10 @@
&:hover {
background: rgba(0, 0, 0, 0.05);
}
.anticon {
color: black;
}
}
}
}

View File

@ -100,7 +100,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.page-header {
background: #fff;

View File

@ -84,7 +84,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.content {
margin: 24px 24px 0;

View File

@ -47,7 +47,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.header-notice{
display: inline-block;
transition: all 0.3s;

View File

@ -255,7 +255,7 @@
}
</script>
<style lang="scss" scoped>
<style lang="less" scoped>
.setting-drawer-index-content {

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