mirror of
https://github.com/jeecgboot/JeecgBoot.git
synced 2025-12-08 17:12:28 +08:00
Compare commits
106 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 09d9e79f40 | |||
| 7f30a186df | |||
| 65d1e6a879 | |||
| e963603d22 | |||
| 3b68f4d4d9 | |||
| 4fd30c495f | |||
| 37158397be | |||
| b4bdb0b3f7 | |||
| cd6cbf8c26 | |||
| bb5fe95d30 | |||
| 1c63f74a6d | |||
| 0094b17ec2 | |||
| 80f377ecd6 | |||
| 55ce5ce919 | |||
| a09fa134f8 | |||
| f2854b29aa | |||
| 1ff06c2bc3 | |||
| 604ca8b312 | |||
| e6bb5eb0a1 | |||
| 109a95a96b | |||
| cb7ae9ca6f | |||
| e4b4898e27 | |||
| 5066ef526d | |||
| 088ff85dcc | |||
| 96cfb8a2b2 | |||
| 32ee9f040c | |||
| dadf6a78a1 | |||
| 8a8e92cda3 | |||
| 4566e50dd2 | |||
| 01ce2e8dab | |||
| dc804d9372 | |||
| 9c64b9a8ed | |||
| d52979621f | |||
| 5ffc3e4d13 | |||
| ea7ac65814 | |||
| cdfa6f8ac6 | |||
| 6ce3a892b6 | |||
| 28e9a0ddfb | |||
| 1f27948814 | |||
| 3bff16a446 | |||
| d5bfccdeb0 | |||
| 9e046a07d4 | |||
| 046831e700 | |||
| e7ad502390 | |||
| 9f1a2b89b7 | |||
| 5878fcb12b | |||
| 27e280bd97 | |||
| 91594ddb1a | |||
| 4c0c0945a1 | |||
| 2b3e157d88 | |||
| e2d4bb29db | |||
| 865ace6db0 | |||
| 7bc3931e1a | |||
| 94a945291d | |||
| 49a303d5e8 | |||
| 1cabc94cea | |||
| 760f8e9f46 | |||
| 38e6586c84 | |||
| d7ca307fa2 | |||
| 188cec1646 | |||
| 4a4f236772 | |||
| 06847cd801 | |||
| 365810d4e6 | |||
| ed83135062 | |||
| beabf9b6dd | |||
| 238b4898c7 | |||
| 4372773924 | |||
| d399ec904a | |||
| 8ecc971940 | |||
| 66b3b290fb | |||
| a9c7f3e547 | |||
| b54dada91e | |||
| 3ead7c3ae1 | |||
| 9c44ffaa8e | |||
| ea5ef384f2 | |||
| 73f7acfd5a | |||
| 728d62f851 | |||
| cd471735d7 | |||
| 30b35af000 | |||
| 3372c88607 | |||
| ce95008fdd | |||
| bb54b20734 | |||
| bd790ee24b | |||
| 11018ab29f | |||
| 67c4f7b3d9 | |||
| 9bd67f9905 | |||
| 283be0480e | |||
| f0d372d008 | |||
| 65180733e1 | |||
| c1dab6276a | |||
| a6b0dccded | |||
| bb24448ea2 | |||
| 07694cf42b | |||
| 2b544b74f7 | |||
| ac891d81ab | |||
| 0640f0b421 | |||
| ee5fa7ec22 | |||
| 8b41076a27 | |||
| 492ce922e9 | |||
| fce50e5209 | |||
| 107e901853 | |||
| c69884c84f | |||
| 5e2eba86e7 | |||
| 9c6f68fd4a | |||
| fd57f233e5 | |||
| 04c0ea55f2 |
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
## ide
|
||||
**/.idea
|
||||
*.iml
|
||||
|
||||
## backend
|
||||
**/target
|
||||
**/logs
|
||||
|
||||
## front
|
||||
**/*.lock
|
||||
2
LICENSE
2
LICENSE
@ -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.
|
||||
|
||||
146
README.md
146
README.md
@ -1,16 +1,18 @@
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
Jeecg-Boot 快速开发平台(前后端分离版本)
|
||||
JEECG BOOT 低代码开发平台(前后端分离版本)
|
||||
===============
|
||||
|
||||
当前最新版本: 2.1.0(发布日期:20190826)
|
||||
当前最新版本: 2.3(发布日期:2020-09-14)
|
||||
|
||||
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
|
||||
[](http://www.jeecg.com)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](http://www.jeecg.com)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](https://github.com/zhangdaiscott/jeecg-boot)
|
||||
[](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.x,Ant Design&Vue,Mybatis-plus,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码! 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、③816531124、①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.权限控制采用 RBAC(Role-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.权限控制采用 RBAC(Role-Based Access Control,基于角色的访问控制)
|
||||
|
||||
|
||||
|
||||
@ -158,10 +167,13 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
│ ├─权限设置(支持按钮权限、数据权限)
|
||||
│ ├─表单权限(控制字段禁用、隐藏)
|
||||
│ ├─部门管理
|
||||
│ ├─我的部门(二级管理员)
|
||||
│ └─字典管理
|
||||
│ └─树分类字典
|
||||
│ └─分类字典
|
||||
│ └─系统公告
|
||||
│ └─我的组织机构
|
||||
│ └─职务管理
|
||||
│ └─通讯录
|
||||
│ └─多租户管理
|
||||
├─消息中心
|
||||
│ ├─消息管理
|
||||
│ ├─模板管理
|
||||
@ -174,6 +186,7 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
│ ├─Excel导入导出工具集成(支持单表,一对多 导入导出)
|
||||
│ ├─平台移动自适应支持
|
||||
├─系统监控
|
||||
│ ├─Gateway路由网关
|
||||
│ ├─性能扫描监控
|
||||
│ │ ├─监控 Redis
|
||||
│ │ ├─Tomcat
|
||||
@ -199,7 +212,13 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
│ └─进度条
|
||||
│ └─排名列表
|
||||
│ └─等等
|
||||
│─大屏模板
|
||||
│ ├─作战指挥中心大屏
|
||||
│ └─物流服务中心大屏
|
||||
│─常用示例
|
||||
│ ├─自定义组件
|
||||
│ ├─对象存储(对接阿里云)
|
||||
│ ├─JVXETable示例(各种复杂ERP布局示例)
|
||||
│ ├─单表模型例子
|
||||
│ └─一对多模型例子
|
||||
│ └─打印例子
|
||||
@ -208,7 +227,10 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
│ └─常用选择组件
|
||||
│ └─异步树table
|
||||
│ └─接口模拟测试
|
||||
│ └─表格合计示例
|
||||
│ └─异步树列表示例
|
||||
│ └─一对多JEditable
|
||||
│ └─JEditable组件示例
|
||||
│ └─图片拖拽排序
|
||||
│ └─图片翻页
|
||||
│ └─图片预览
|
||||
@ -240,15 +262,17 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
│ └─异常页面
|
||||
│ └─个人页面
|
||||
├─高级功能
|
||||
│ ├─系统编码规则
|
||||
│ ├─提供单点登录CAS集成方案
|
||||
│ ├─提供APP发布方案
|
||||
│ ├─集成Websocket消息通知机制
|
||||
├─Online在线开发(暂未开源)
|
||||
├─Online在线低代码开发(暂未开源)
|
||||
│ ├─Online在线表单 - 功能已开放
|
||||
│ ├─在线代码生成器 - 功能已开放
|
||||
│ ├─Online在线报表 - 功能已开放
|
||||
│ ├─多数据源管理
|
||||
│ ├─Online在线图表
|
||||
│ ├─Online图表模板配置
|
||||
│ ├─Online在线报表
|
||||
│ ├─高级表单设计器
|
||||
│─流程模块功能 (暂不开源)
|
||||
│ ├─流程设计器
|
||||
@ -269,6 +293,9 @@ Jeecg-Boot快速开发平台,可以应用在任何J2EE项目的开发中,尤
|
||||
```
|
||||
|
||||
|
||||
### Jeecg Boot 产品功能蓝图
|
||||

|
||||
|
||||
|
||||
后台开发环境和依赖
|
||||
----
|
||||
@ -277,7 +304,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 +354,13 @@ yarn run lint
|
||||
|
||||
系统效果
|
||||
----
|
||||
##### 大屏模板
|
||||

|
||||
|
||||

|
||||
|
||||
##### PC端
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
@ -396,15 +426,15 @@ yarn run lint
|
||||
|
||||
附属文档
|
||||
----
|
||||
- [Ant Design Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn)
|
||||
- [Ant Design Vue](https://www.antdv.com/docs/vue/introduce-cn)
|
||||
|
||||
- [报表 viser-vue](https://viserjs.github.io/demo.html#/viser/bar/basic-bar)
|
||||
- [报表 viser-vue](https://viserjs.github.io/demo.html#/viser/line/basic-line)
|
||||
|
||||
- [Vue](https://cn.vuejs.org/v2/guide)
|
||||
|
||||
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/router/README.md)
|
||||
- [路由/菜单说明](https://gitee.com/jeecg/jeecg-boot/tree/v1.1/ant-design-jeecg-vue/src/router/README.md)
|
||||
|
||||
- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/defaultSettings.js)
|
||||
- [ANTD 默认配置项](https://gitee.com/jeecg/jeecg-boot/blob/v1.1/ant-design-jeecg-vue/src/defaultSettings.js)
|
||||
|
||||
- 其他待补充...
|
||||
|
||||
|
||||
23
ant-design-vue-jeecg/.dockerignore
Normal file
23
ant-design-vue-jeecg/.dockerignore
Normal file
@ -0,0 +1,23 @@
|
||||
docs
|
||||
public
|
||||
src
|
||||
.dockerignore
|
||||
.editorconfig
|
||||
.eslintignore
|
||||
.gitattributes
|
||||
.gitignore
|
||||
.prettierrc
|
||||
babel.config.js
|
||||
Dockerfile
|
||||
idea.config.js
|
||||
LICENSE
|
||||
package.json
|
||||
package-lock.json
|
||||
README.md
|
||||
vue.config.js
|
||||
yarn
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
.idea
|
||||
.svn
|
||||
node_modules
|
||||
29
ant-design-vue-jeecg/Dockerfile
Normal file
29
ant-design-vue-jeecg/Dockerfile
Normal file
@ -0,0 +1,29 @@
|
||||
FROM nginx
|
||||
MAINTAINER jeecgos@163.com
|
||||
VOLUME /tmp
|
||||
ENV LANG en_US.UTF-8
|
||||
RUN echo "server { \
|
||||
listen 80; \
|
||||
location ^~ /jeecg-boot { \
|
||||
proxy_pass http://jeecg-boot-system:8080/jeecg-boot/; \
|
||||
proxy_set_header Host jeecg-boot-system; \
|
||||
proxy_set_header X-Real-IP \$remote_addr; \
|
||||
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; \
|
||||
} \
|
||||
#解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题 \
|
||||
location / { \
|
||||
root /var/www/html/; \
|
||||
index index.html index.htm; \
|
||||
if (!-e \$request_filename) { \
|
||||
rewrite ^(.*)\$ /index.html?s=\$1 last; \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
access_log /var/log/nginx/access.log ; \
|
||||
} " > /etc/nginx/conf.d/default.conf \
|
||||
&& mkdir -p /var/www \
|
||||
&& mkdir -p /var/www/html
|
||||
|
||||
ADD dist/ /var/www/html/
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
@ -1,7 +1,7 @@
|
||||
Ant Design Jeecg Vue
|
||||
====
|
||||
|
||||
当前最新版本: 2.1.0(发布日期:20190826)
|
||||
当前最新版本: 2.3.0(发布日期:20200914)
|
||||
|
||||
Overview
|
||||
----
|
||||
@ -103,4 +103,33 @@ yarn run lint
|
||||
备注
|
||||
----
|
||||
|
||||
> @vue/cli 升级后,eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules
|
||||
> @vue/cli 升级后,eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules
|
||||
|
||||
|
||||
Docker 镜像使用
|
||||
----
|
||||
|
||||
```
|
||||
# 1.修改前端项目的后台域名
|
||||
public/index.html
|
||||
域名改成: http://jeecg-boot-system:8080/jeecg-boot
|
||||
|
||||
# 2.先进入打包前端项目
|
||||
yarn run build
|
||||
|
||||
# 3.构建镜像
|
||||
docker build -t nginx:jeecgboot .
|
||||
|
||||
# 4.启动镜像
|
||||
docker run --name jeecg-boot-nginx -p 80:80 -d nginx:jeecgboot
|
||||
|
||||
# 5.配置host
|
||||
|
||||
# jeecgboot
|
||||
127.0.0.1 jeecg-boot-redis
|
||||
127.0.0.1 jeecg-boot-mysql
|
||||
127.0.0.1 jeecg-boot-system
|
||||
|
||||
# 6.访问前台项目
|
||||
http://localhost:80
|
||||
```
|
||||
@ -1,5 +1,6 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
['@vue/app',
|
||||
{ useBuiltIns: 'entry' }]
|
||||
]
|
||||
}
|
||||
|
||||
14185
ant-design-vue-jeecg/package-lock.json
generated
14185
ant-design-vue-jeecg/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "vue-antd-jeecg",
|
||||
"version": "2.1.0",
|
||||
"private": false,
|
||||
"version": "2.3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
|
||||
"serve": "vue-cli-service serve",
|
||||
@ -9,14 +9,11 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@antv/data-set": "^0.10.2",
|
||||
"@jeecg/antd-onine": "^1.0.1",
|
||||
"@tinymce/tinymce-vue": "^2.0.0",
|
||||
"ant-design-vue": "^1.3.9",
|
||||
"apexcharts": "^3.6.5",
|
||||
"ant-design-vue": "^1.6.3",
|
||||
"@jeecg/antd-online-mini": "2.3.0",
|
||||
"@antv/data-set": "^0.11.4",
|
||||
"viser-vue": "^2.4.8",
|
||||
"axios": "^0.18.0",
|
||||
"clipboard": "^2.0.4",
|
||||
"codemirror": "^5.46.0",
|
||||
"dayjs": "^1.8.0",
|
||||
"enquire.js": "^2.1.6",
|
||||
"js-cookie": "^2.2.0",
|
||||
@ -24,23 +21,29 @@
|
||||
"lodash.pick": "^4.4.0",
|
||||
"md5": "^2.2.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"tinymce": "^5.0.2",
|
||||
"viser-vue": "^2.4.4",
|
||||
"vue": "^2.6.10",
|
||||
"vue-apexcharts": "^1.3.2",
|
||||
"vue-class-component": "^6.0.0",
|
||||
"vue-cropper": "^0.4.8",
|
||||
"vue-cropper": "^0.5.4",
|
||||
"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-router": "^3.0.1",
|
||||
"vuex": "^3.1.0",
|
||||
"vue-print-nb-jeecg": "^1.0.9",
|
||||
"clipboard": "^2.0.4",
|
||||
"vue-photo-preview": "^1.1.3",
|
||||
"vue-splitpane": "^1.0.4",
|
||||
"vuedraggable": "^2.20.0",
|
||||
"vuex": "^3.0.1",
|
||||
"vuex-class": "^0.3.1"
|
||||
"codemirror": "^5.46.0",
|
||||
"@tinymce/tinymce-vue": "^2.1.0",
|
||||
"tinymce": "^5.3.2",
|
||||
"@toast-ui/editor": "^2.1.2",
|
||||
"vue-area-linkage": "^5.1.0",
|
||||
"area-data": "^5.0.6",
|
||||
"jsoneditor": "^9.0.0",
|
||||
"dom-align": "1.12.0",
|
||||
"xe-utils": "2.4.8",
|
||||
"vxe-table": "2.9.13",
|
||||
"vxe-table-plugin-antd": "1.8.10"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/polyfill": "^7.2.5",
|
||||
@ -48,14 +51,15 @@
|
||||
"@vue/cli-plugin-eslint": "^3.3.0",
|
||||
"@vue/cli-service": "^3.3.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"babel-eslint": "7.2.3",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-plugin-vue": "^5.1.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"
|
||||
"vue-template-compiler": "^2.6.10",
|
||||
"html-webpack-plugin": "^4.2.0",
|
||||
"compression-webpack-plugin": "^3.1.0",
|
||||
"babel-plugin-import": "^1.13.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
@ -64,7 +68,7 @@
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/strongly-recommended",
|
||||
"eslint:recommended"
|
||||
"@vue/standard"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
@ -92,7 +96,10 @@
|
||||
"vue/no-use-v-if-with-v-for": 0,
|
||||
"vue/html-closing-bracket-newline": 0,
|
||||
"vue/no-parsing-error": 0,
|
||||
"no-console": 0
|
||||
"no-tabs": 0,
|
||||
"indent": ["off", 2],
|
||||
"no-console": 0,
|
||||
"space-before-function-paren": 0
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
@ -103,6 +110,6 @@
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not ie <= 8"
|
||||
"not ie <= 10"
|
||||
]
|
||||
}
|
||||
|
||||
6953
ant-design-vue-jeecg/public/cdn/babel-polyfill/polyfill_7_2_5.js
Normal file
6953
ant-design-vue-jeecg/public/cdn/babel-polyfill/polyfill_7_2_5.js
Normal file
File diff suppressed because it is too large
Load Diff
40
ant-design-vue-jeecg/public/color.less
vendored
40
ant-design-vue-jeecg/public/color.less
vendored
@ -7679,3 +7679,43 @@ font.medium {
|
||||
font.weak {
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
// 侧边导航栏首页颜色跟随主题变化
|
||||
.ant-menu.ant-menu-root > .ant-menu-item:first-child.ant-menu-item-selected {
|
||||
& > a,
|
||||
& > a:hover {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
.ant-menu-submenu-selected {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
// 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 三级联动样式 --------------
|
||||
|
||||
// TESTA-521
|
||||
.ant-menu-submenu-selected {
|
||||
color: @primary-color;
|
||||
}
|
||||
|
||||
// TESTA-521
|
||||
.tab-layout-tabs.ant-tabs.ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active {
|
||||
border-color: @primary-color !important;
|
||||
}
|
||||
9
ant-design-vue-jeecg/public/index.html
vendored
9
ant-design-vue-jeecg/public/index.html
vendored
@ -5,9 +5,10 @@
|
||||
<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 +245,9 @@
|
||||
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['downloadUrl'] = window._CONFIG['domianURL'] + '/sys/common/download';
|
||||
window._CONFIG['pdfDomainURL'] = window._CONFIG['domianURL'] + '/sys/common/pdf/pdfPreviewIframe';
|
||||
</script>
|
||||
</head>
|
||||
|
||||
@ -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'
|
||||
|
||||
30
ant-design-vue-jeecg/src/api/GroupRequest.js
Normal file
30
ant-design-vue-jeecg/src/api/GroupRequest.js
Normal 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);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
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/";
|
||||
@ -25,12 +27,18 @@ 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);
|
||||
/*update_begin author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
|
||||
const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
|
||||
const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
|
||||
const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
|
||||
/*update_end author:wuxianquan date:20190908 for:添加查询一级菜单和子菜单查询api */
|
||||
|
||||
// const deletePermission = (params)=>deleteAction("/sys/permission/delete",params);
|
||||
// const deletePermissionList = (params)=>deleteAction("/sys/permission/deleteBatch",params);
|
||||
const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
|
||||
@ -51,6 +59,14 @@ 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);
|
||||
@ -70,6 +86,14 @@ const editDictItem = (params)=>putAction("/sys/dictItem/edit",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);
|
||||
@ -84,9 +108,22 @@ const getVisitInfo = (params)=>getAction("/sys/visitInfo",params);
|
||||
const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
|
||||
|
||||
// 查询用户角色表里的所有信息
|
||||
const queryUserRoleMap = (params)=>getAction("/sys/user/queryUserRoleMap",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);
|
||||
const getTransitURL = url => `/sys/common/transitRESTful?url=${encodeURIComponent(url)}`
|
||||
// 中转HTTP请求
|
||||
export const transitRESTful = {
|
||||
get: (url, parameter) => getAction(getTransitURL(url), parameter),
|
||||
post: (url, parameter) => postAction(getTransitURL(url), parameter),
|
||||
put: (url, parameter) => putAction(getTransitURL(url), parameter),
|
||||
http: (url, parameter) => httpAction(getTransitURL(url), parameter),
|
||||
}
|
||||
|
||||
export {
|
||||
// imgView,
|
||||
@ -101,7 +138,7 @@ export {
|
||||
queryall,
|
||||
frozenBatch,
|
||||
checkOnlyUser,
|
||||
changPassword,
|
||||
changePassword,
|
||||
getPermissionList,
|
||||
addPermission,
|
||||
editPermission,
|
||||
@ -130,9 +167,21 @@ export {
|
||||
getLoginfo,
|
||||
getVisitInfo,
|
||||
queryUserByDepId,
|
||||
queryUserRoleMap,
|
||||
duplicateCheck,
|
||||
queryTreeListForRole,
|
||||
getSystemMenuList,
|
||||
getSystemSubmenu,
|
||||
getSystemSubmenuBatch,
|
||||
loadCategoryData,
|
||||
checkRuleByCode,
|
||||
queryDepartPermission,
|
||||
saveDepartPermission,
|
||||
queryTreeListForDeptRole,
|
||||
queryDeptRolePermission,
|
||||
saveDeptRolePermission,
|
||||
queryMyDepartTreeList,
|
||||
getUserNoticeInfo,
|
||||
getDictItemsFromCache
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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'
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
import Vue from 'vue'
|
||||
import { axios } from '@/utils/request'
|
||||
|
||||
const api = {
|
||||
@ -112,3 +113,69 @@ 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'
|
||||
try {
|
||||
if(avatar && avatar.startsWith(subStr)){
|
||||
return avatar;
|
||||
}else{
|
||||
if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
|
||||
return window._CONFIG['staticDomainURL'] + "/" + avatar;
|
||||
}
|
||||
}
|
||||
}catch(err){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BIN
ant-design-vue-jeecg/src/assets/checkcode.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/checkcode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
BIN
ant-design-vue-jeecg/src/assets/daiban.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/daiban.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
BIN
ant-design-vue-jeecg/src/assets/duban.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/duban.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
BIN
ant-design-vue-jeecg/src/assets/guaz.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/guaz.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
259
ant-design-vue-jeecg/src/assets/less/JAreaLinkage.less
Normal file
259
ant-design-vue-jeecg/src/assets/less/JAreaLinkage.less
Normal 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;
|
||||
}
|
||||
15
ant-design-vue-jeecg/src/assets/less/TableExpand.less
Normal file
15
ant-design-vue-jeecg/src/assets/less/TableExpand.less
Normal 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;
|
||||
}
|
||||
}
|
||||
1
ant-design-vue-jeecg/src/assets/less/codemirror_idea.css
Normal file
1
ant-design-vue-jeecg/src/assets/less/codemirror_idea.css
Normal file
@ -0,0 +1 @@
|
||||
.cm-s-idea span.cm-meta{color:olive}.cm-s-idea span.cm-number{color:#00f}.cm-s-idea span.cm-keyword{line-height:1em;font-weight:700;color:navy}.cm-s-idea span.cm-atom{font-weight:700;color:navy}.cm-s-idea span.cm-def{color:#000}.cm-s-idea span.cm-variable{color:#000}.cm-s-idea span.cm-variable-2{color:#000}.cm-s-idea span.cm-type,.cm-s-idea span.cm-variable-3{color:#000}.cm-s-idea span.cm-property{color:#000}.cm-s-idea span.cm-operator{color:#000}.cm-s-idea span.cm-comment{color:grey}.cm-s-idea span.cm-string{color:green}.cm-s-idea span.cm-string-2{color:green}.cm-s-idea span.cm-qualifier{color:#555}.cm-s-idea span.cm-error{color:red}.cm-s-idea span.cm-attribute{color:#00f}.cm-s-idea span.cm-tag{color:navy}.cm-s-idea span.cm-link{color:#00f}.cm-s-idea .CodeMirror-activeline-background{background:#fffae3}.cm-s-idea span.cm-builtin{color:#30a}.cm-s-idea span.cm-bracket{color:#cc7}.cm-s-idea{font-family:Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,serif}.cm-s-idea .CodeMirror-matchingbracket{outline:1px solid grey;color:#000!important}.CodeMirror-hints.idea{font-family:Menlo,Monaco,Consolas,'Courier New',monospace;color:#616569;background-color:#ebf3fd!important}.CodeMirror-hints.idea .CodeMirror-hint-active{background-color:#a2b8c9!important;color:#5c6065!important}
|
||||
@ -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,15 @@
|
||||
/*列表中范围查询样式*/
|
||||
.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;
|
||||
}
|
||||
|
||||
/**隐藏样式-modal确定按钮 */
|
||||
.jee-hidden{display: none}
|
||||
BIN
ant-design-vue-jeecg/src/assets/nodata.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/nodata.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 99 KiB |
BIN
ant-design-vue-jeecg/src/assets/zaiban.png
Normal file
BIN
ant-design-vue-jeecg/src/assets/zaiban.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
@ -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) => {
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.chart-card-header {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
@ -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>
|
||||
205
ant-design-vue-jeecg/src/components/JVxeCells/JVxeFileCell.vue
Normal file
205
ant-design-vue-jeecg/src/components/JVxeCells/JVxeFileCell.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
<span style="margin-left:5px">上传中…</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else-if="file.status==='done'" :title="file.name">
|
||||
<a-icon type="paper-clip"/>
|
||||
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip v-else :title="file.name">
|
||||
<a-icon type="paper-clip" style="color:red;"/>
|
||||
<span style="color:red;margin-left:5px">{{ ellipsisFileName }}</span>
|
||||
</a-tooltip>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon
|
||||
v-if="file.status!=='uploading'"
|
||||
type="setting"
|
||||
style="cursor: pointer;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download"/> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete"/> 删除</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleMoreOperation">
|
||||
<span><a-icon type="bars"/> 更多</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload"
|
||||
>
|
||||
<a-button icon="upload">{{originColumn.btnText || '上传文件'}}</a-button>
|
||||
</a-upload>
|
||||
<j-file-pop ref="filePop" @ok="handleFileSuccess"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import JFilePop from '@/components/jeecg/minipop/JFilePop'
|
||||
|
||||
import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
|
||||
|
||||
export default {
|
||||
name: 'JVxeFileCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {JFilePop},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerFile: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders() {
|
||||
let {originColumn: col} = this
|
||||
let headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
/** 上传请求地址 */
|
||||
uploadAction() {
|
||||
if (!this.originColumn.action) {
|
||||
return window._CONFIG['domianURL'] + '/sys/common/upload'
|
||||
} else {
|
||||
return this.originColumn.action
|
||||
}
|
||||
},
|
||||
|
||||
hasFile() {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
ellipsisFileName() {
|
||||
let length = 5
|
||||
let file = this.innerFile
|
||||
if (!file || !file.name) {
|
||||
return ''
|
||||
}
|
||||
if (file.name.length > length) {
|
||||
return file.name.substr(0, length) + '…'
|
||||
}
|
||||
return file.name
|
||||
},
|
||||
|
||||
responseName() {
|
||||
if (this.originColumn.responseName) {
|
||||
return this.originColumn.responseName
|
||||
} else {
|
||||
return 'message'
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 点击更多按钮
|
||||
handleMoreOperation() {
|
||||
let path = ''
|
||||
if (this.innerFile) {
|
||||
path = this.innerFile.path
|
||||
}
|
||||
this.$refs.filePop.show('', path)
|
||||
},
|
||||
|
||||
// 更多上传回调
|
||||
handleFileSuccess(file) {
|
||||
if (file) {
|
||||
this.innerFile.path = file.path
|
||||
this.handleChangeCommon(this.innerFile)
|
||||
}
|
||||
},
|
||||
|
||||
handleChangeUpload(info) {
|
||||
let {originColumn: col} = this
|
||||
let {file} = info
|
||||
let value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (file.response) {
|
||||
value['responseName'] = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
handleClickDownloadFile() {
|
||||
let {url, path} = this.innerFile || {}
|
||||
if (!url || url.length === 0) {
|
||||
if (path && path.length > 0) {
|
||||
url = getFileAccessHttpUrl(path.split(',')[0])
|
||||
}
|
||||
}
|
||||
if (url) {
|
||||
window.open(url)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile() {
|
||||
this.handleChangeCommon(null)
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {visible: true},
|
||||
getValue: value => JVxeUploadCell.enhanced.getValue(value),
|
||||
setValue: value => JVxeUploadCell.enhanced.setValue(value),
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
</style>
|
||||
230
ant-design-vue-jeecg/src/components/JVxeCells/JVxeImageCell.vue
Normal file
230
ant-design-vue-jeecg/src/components/JVxeCells/JVxeImageCell.vue
Normal file
@ -0,0 +1,230 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<div :key="fileKey" style="position: relative;">
|
||||
<template v-if="!file || !(file['url'] || file['path'] || file['message'])">
|
||||
<a-tooltip :title="'请稍后: ' + JSON.stringify (file) + ((file['url'] || file['path'] || file['message']))">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<template v-else-if="file['path']">
|
||||
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError"/>
|
||||
</template>
|
||||
<template slot="addonBefore" style="width: 30px">
|
||||
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<template style="width: 30px">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon
|
||||
v-if="file.status!=='uploading'"
|
||||
type="setting"
|
||||
style="cursor: pointer;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download"/> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete"/> 删除</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item @click="handleMoreOperation">
|
||||
<span><a-icon type="bars"/> 更多</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="uploadAction"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload"
|
||||
>
|
||||
<a-button icon="upload">{{originColumn.btnText || '上传图片'}}</a-button>
|
||||
</a-upload>
|
||||
<j-file-pop ref="filePop" @ok="handleFileSuccess"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import JFilePop from '@/components/jeecg/minipop/JFilePop'
|
||||
|
||||
import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
|
||||
|
||||
export default {
|
||||
name: 'JVxeImageCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {JFilePop},
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerFile: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders() {
|
||||
let {originColumn: col} = this
|
||||
let headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
/** 上传请求地址 */
|
||||
uploadAction() {
|
||||
if (!this.originColumn.action) {
|
||||
return window._CONFIG['domianURL'] + '/sys/common/upload'
|
||||
} else {
|
||||
return this.originColumn.action
|
||||
}
|
||||
},
|
||||
|
||||
hasFile() {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
/** 预览图片地址 */
|
||||
imgSrc() {
|
||||
if (this.innerFile) {
|
||||
if (this.innerFile['url']) {
|
||||
return this.innerFile['url']
|
||||
} else if (this.innerFile['path']) {
|
||||
let path = this.innerFile['path'].split(',')[0]
|
||||
return getFileAccessHttpUrl(path)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
|
||||
responseName() {
|
||||
if (this.originColumn.responseName) {
|
||||
return this.originColumn.responseName
|
||||
} else {
|
||||
return 'message'
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 点击更多按钮
|
||||
handleMoreOperation() {
|
||||
let path = ''
|
||||
if (this.innerFile) {
|
||||
path = this.innerFile.path
|
||||
}
|
||||
this.$refs.filePop.show('', path, 'img')
|
||||
},
|
||||
|
||||
// 更多上传回调
|
||||
handleFileSuccess(file) {
|
||||
if (file) {
|
||||
this.innerFile.path = file.path
|
||||
this.handleChangeCommon(this.innerFile)
|
||||
}
|
||||
},
|
||||
|
||||
// 弹出上传出错详细信息
|
||||
handleClickShowImageError() {
|
||||
let file = this.innerFile || null
|
||||
if (file && file['message']) {
|
||||
this.$error({title: '上传出错', content: '错误信息:' + file['message'], maskClosable: true})
|
||||
}
|
||||
},
|
||||
|
||||
handleChangeUpload(info) {
|
||||
let {originColumn: col} = this
|
||||
let {file} = info
|
||||
let value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (file.response) {
|
||||
value['responseName'] = file.response[this.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[this.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
handleClickDownloadFile() {
|
||||
if (this.imgSrc) {
|
||||
window.open(this.imgSrc)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile() {
|
||||
this.handleChangeCommon(null)
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {visible: true},
|
||||
getValue: value => JVxeUploadCell.enhanced.getValue(value),
|
||||
setValue: value => JVxeUploadCell.enhanced.setValue(value),
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.j-editable-image {
|
||||
height: 32px;
|
||||
max-width: 100px !important;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<j-popup
|
||||
v-bind="popupProps"
|
||||
@input="handlePopupInput"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins, { vModel, dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxePopupCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
popupProps() {
|
||||
const {innerValue, originColumn: col, caseId, cellProps} = this
|
||||
return {
|
||||
...cellProps,
|
||||
value: innerValue,
|
||||
field: col.field || col.key,
|
||||
code: col.popupCode,
|
||||
orgFields: col.orgFields,
|
||||
destFields: col.destFields,
|
||||
groupId: caseId,
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** popup回调 */
|
||||
handlePopupInput(value, others) {
|
||||
const {row, originColumn: col} = this
|
||||
// 存储输入的值
|
||||
let popupValue = value
|
||||
if (others && Object.keys(others).length > 0) {
|
||||
Object.keys(others).forEach(key => {
|
||||
let currentValue = others[key]
|
||||
// 当前列直接赋值,其他列通过vModel赋值
|
||||
if (key === col.key) {
|
||||
popupValue = currentValue
|
||||
} else {
|
||||
vModel.call(this, currentValue, row, key)
|
||||
}
|
||||
})
|
||||
}
|
||||
this.handleChangeCommon(popupValue)
|
||||
},
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-input'),
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<a-radio-group
|
||||
:class="clazz"
|
||||
:value="innerValue"
|
||||
v-bind="cellProps"
|
||||
@change="(e)=>handleChangeCommon(e.target.value)"
|
||||
>
|
||||
<a-radio
|
||||
v-for="item of originColumn.options"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
@click="$event=>handleRadioClick(item,$event)"
|
||||
>{{ item.text }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeRadioCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
scrolling() {
|
||||
return !!this.renderOptions.scrolling
|
||||
},
|
||||
clazz() {
|
||||
return {
|
||||
'j-vxe-radio': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleRadioClick(item) {
|
||||
if (this.originColumn.allowClear === true) {
|
||||
// 取消选择
|
||||
if (item.value === this.innerValue) {
|
||||
this.handleChangeCommon(null)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {visible: true},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// 关闭动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-radio.no-animation {
|
||||
.ant-radio-inner,
|
||||
.ant-radio-inner::after {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,260 @@
|
||||
import debounce from 'lodash/debounce'
|
||||
import { getAction } from '@/api/manage'
|
||||
import { cloneObject } from '@/utils/util'
|
||||
import { filterDictText } from '@/components/dict/JDictSelectUtil'
|
||||
import { ajaxGetDictItems, getDictItemsFromCache } from '@/api/api'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
/** 公共资源 */
|
||||
const common = {
|
||||
/** value - label map,防止重复查询(刷新清空缓存) */
|
||||
labelMap: new Map(),
|
||||
|
||||
/** 公共data */
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
innerSelectValue: null,
|
||||
innerOptions: [],
|
||||
}
|
||||
},
|
||||
/** 公共计算属性 */
|
||||
computed: {
|
||||
dict() {
|
||||
return this.originColumn.dict
|
||||
},
|
||||
options() {
|
||||
if (this.isAsync) {
|
||||
return this.innerOptions
|
||||
} else {
|
||||
return this.originColumn.options || []
|
||||
}
|
||||
},
|
||||
// 是否是异步模式
|
||||
isAsync() {
|
||||
let isAsync = this.originColumn.async
|
||||
return (isAsync != null && isAsync !== '') ? !!isAsync : true
|
||||
},
|
||||
},
|
||||
/** 公共属性监听 */
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
if (value == null || value === '') {
|
||||
this.innerSelectValue = null
|
||||
} else {
|
||||
this.loadDataByValue(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
dict() {
|
||||
this.loadDataByDict()
|
||||
}
|
||||
},
|
||||
/** 公共方法 */
|
||||
methods: {
|
||||
|
||||
// 根据 value 查询数据,用于回显
|
||||
async loadDataByValue(value) {
|
||||
if (this.isAsync) {
|
||||
if (this.innerSelectValue !== value) {
|
||||
if (common.labelMap.has(value)) {
|
||||
this.innerOptions = cloneObject(common.labelMap.get(value))
|
||||
} else {
|
||||
let {success, result} = await getAction(`/sys/dict/loadDictItem/${this.dict}`, {key: value})
|
||||
if (success && result && result.length > 0) {
|
||||
this.innerOptions = [{value: value, text: result[0]}]
|
||||
common.labelMap.set(value, cloneObject(this.innerOptions))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.innerSelectValue = (value || '').toString()
|
||||
},
|
||||
|
||||
// 初始化字典
|
||||
async loadDataByDict() {
|
||||
if (!this.isAsync) {
|
||||
// 如果字典项集合有数据
|
||||
if (!this.originColumn.options || this.originColumn.options.length === 0) {
|
||||
// 根据字典Code, 初始化字典数组
|
||||
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) {
|
||||
//优先从缓存中读取字典配置
|
||||
let cache = getDictItemsFromCache(this.dict)
|
||||
if (cache) {
|
||||
this.innerOptions = cache
|
||||
return
|
||||
}
|
||||
}
|
||||
let {success, result} = await ajaxGetDictItems(dictStr, null)
|
||||
if (success) {
|
||||
this.innerOptions = result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
// 显示组件,自带翻译
|
||||
export const DictSearchSpanCell = {
|
||||
name: 'JVxeSelectSearchSpanCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data() {
|
||||
return {
|
||||
...common.data.apply(this),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...common.computed,
|
||||
},
|
||||
watch: {
|
||||
...common.watch,
|
||||
},
|
||||
methods: {
|
||||
...common.methods,
|
||||
},
|
||||
render(h) {
|
||||
return h('span', {}, [
|
||||
filterDictText(this.innerOptions, this.innerSelectValue || this.innerValue)
|
||||
])
|
||||
},
|
||||
}
|
||||
|
||||
// 请求id
|
||||
let requestId = 0
|
||||
|
||||
// 输入选择组件
|
||||
export const DictSearchInputCell = {
|
||||
name: 'JVxeSelectSearchInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data() {
|
||||
return {
|
||||
...common.data.apply(this),
|
||||
|
||||
hasRequest: false,
|
||||
scopedSlots: {
|
||||
notFoundContent: () => {
|
||||
if (this.loading) {
|
||||
return <a-spin size="small"/>
|
||||
} else if (this.hasRequest) {
|
||||
return <div>没有查询到任何数据</div>
|
||||
} else {
|
||||
return <div>{this.tipsContent}</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...common.computed,
|
||||
tipsContent() {
|
||||
return this.originColumn.tipsContent || '请输入搜索内容'
|
||||
},
|
||||
filterOption() {
|
||||
if (this.isAsync) {
|
||||
return null
|
||||
}
|
||||
return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
...common.watch,
|
||||
},
|
||||
created() {
|
||||
this.loadData = debounce(this.loadData, 300)//消抖
|
||||
},
|
||||
methods: {
|
||||
...common.methods,
|
||||
|
||||
loadData(value) {
|
||||
const currentRequestId = ++requestId
|
||||
this.loading = true
|
||||
this.innerOptions = []
|
||||
if (value == null || value.trim() === '') {
|
||||
this.loading = false
|
||||
this.hasRequest = false
|
||||
return
|
||||
}
|
||||
// 字典code格式:table,text,code
|
||||
this.hasRequest = true
|
||||
getAction(`/sys/dict/loadDict/${this.dict}`, {keyword: value}).then(res => {
|
||||
if (currentRequestId !== requestId) {
|
||||
return
|
||||
}
|
||||
let {success, result, message} = res
|
||||
if (success) {
|
||||
this.innerOptions = result
|
||||
result.forEach((item) => {
|
||||
common.labelMap.set(item.value, [item])
|
||||
})
|
||||
} else {
|
||||
this.$message.warning(message)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
handleChange(selectedValue) {
|
||||
this.innerSelectValue = selectedValue
|
||||
this.handleChangeCommon(this.innerSelectValue)
|
||||
},
|
||||
handleSearch(value) {
|
||||
if (this.isAsync) {
|
||||
// 在输入时也应该开启加载,因为loadData加了消抖,所以会有800ms的用户主观上认为的卡顿时间
|
||||
this.loading = true
|
||||
if (this.innerOptions.length > 0) {
|
||||
this.innerOptions = []
|
||||
}
|
||||
this.loadData(value)
|
||||
}
|
||||
},
|
||||
|
||||
renderOptionItem() {
|
||||
let options = []
|
||||
this.options.forEach(({value, text, label, title, disabled}) => {
|
||||
options.push(
|
||||
<a-select-option key={value} value={value} disabled={disabled}>{text || label || title}</a-select-option>
|
||||
)
|
||||
})
|
||||
return options
|
||||
},
|
||||
},
|
||||
render() {
|
||||
return (
|
||||
<a-select
|
||||
showSearch
|
||||
allowClear
|
||||
value={this.innerSelectValue}
|
||||
filterOption={this.filterOption}
|
||||
style="width: 100%"
|
||||
{...this.cellProps}
|
||||
onSearch={this.handleSearch}
|
||||
onChange={this.handleChange}
|
||||
scopedSlots={this.scopedSlots}
|
||||
>
|
||||
{this.renderOptionItem()}
|
||||
</a-select>
|
||||
)
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-select'),
|
||||
},
|
||||
}
|
||||
}
|
||||
36
ant-design-vue-jeecg/src/components/JVxeCells/install.js
Normal file
36
ant-design-vue-jeecg/src/components/JVxeCells/install.js
Normal file
@ -0,0 +1,36 @@
|
||||
import { installCell, JVXETypes } from '@/components/jeecg/JVxeTable'
|
||||
import JVxePopupCell from './JVxePopupCell'
|
||||
import { DictSearchInputCell, DictSearchSpanCell } from './JVxeSelectDictSearchCell'
|
||||
import JVxeFileCell from './JVxeFileCell'
|
||||
import JVxeImageCell from './JVxeImageCell'
|
||||
import JVxeRadioCell from './JVxeRadioCell'
|
||||
import JVxeSelectCell from '@comp/jeecg/JVxeTable/components/cells/JVxeSelectCell'
|
||||
import JVxeTextareaCell from '@comp/jeecg/JVxeTable/components/cells/JVxeTextareaCell'
|
||||
|
||||
// 注册online组件
|
||||
JVXETypes.input_pop = 'input_pop'
|
||||
JVXETypes.list_multi = 'list_multi'
|
||||
JVXETypes.sel_search = 'sel_search'
|
||||
installCell(JVXETypes.input_pop, JVxeTextareaCell)
|
||||
installCell(JVXETypes.list_multi, JVxeSelectCell)
|
||||
installCell(JVXETypes.sel_search, JVxeSelectCell)
|
||||
|
||||
// 注册【popup】组件(普通封装方式)
|
||||
JVXETypes.popup = 'popup'
|
||||
installCell(JVXETypes.popup, JVxePopupCell)
|
||||
|
||||
// 注册【字典搜索下拉】组件(高级封装方式)
|
||||
JVXETypes.selectDictSearch = 'select-dict-search'
|
||||
installCell(JVXETypes.selectDictSearch, DictSearchInputCell, DictSearchSpanCell)
|
||||
|
||||
// 注册【文件上传】组件
|
||||
JVXETypes.file = 'file'
|
||||
installCell(JVXETypes.file, JVxeFileCell)
|
||||
|
||||
// 注册【图片上传】组件
|
||||
JVXETypes.image = 'image'
|
||||
installCell(JVXETypes.image, JVxeImageCell)
|
||||
|
||||
// 注册【单选框】组件
|
||||
JVXETypes.radio = 'radio'
|
||||
installCell(JVXETypes.radio, JVxeRadioCell)
|
||||
79
ant-design-vue-jeecg/src/components/_util/Area.js
Normal file
79
ant-design-vue-jeecg/src/components/_util/Area.js
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -83,6 +83,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@ -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
|
||||
|
||||
@ -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" :scale="scale" :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,30 @@
|
||||
})
|
||||
|
||||
// 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
|
||||
},
|
||||
scale() {
|
||||
return [
|
||||
{
|
||||
type: 'cat',
|
||||
dataKey: 'x'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
61
ant-design-vue-jeecg/src/components/chart/IndexBar.vue
Normal file
61
ant-design-vue-jeecg/src/components/chart/IndexBar.vue
Normal 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>
|
||||
@ -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
|
||||
@ -50,6 +57,7 @@
|
||||
data() {
|
||||
return {
|
||||
scale: [{
|
||||
type: 'cat',
|
||||
dataKey: 'x',
|
||||
min: 0,
|
||||
max: 1
|
||||
@ -66,7 +74,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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,6 +64,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@ -71,6 +71,6 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
@import "chart";
|
||||
</style>
|
||||
@ -34,7 +34,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.chart-mini-progress {
|
||||
padding: 5px 0;
|
||||
position: relative;
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
|
||||
.rank {
|
||||
padding: 0 32px 32px 72px;
|
||||
|
||||
54
ant-design-vue-jeecg/src/components/chart/StackBar.vue
Normal file
54
ant-design-vue-jeecg/src/components/chart/StackBar.vue
Normal 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>
|
||||
@ -52,7 +52,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
<style lang="less" scoped>
|
||||
.chart-trend {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
|
||||
@ -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)
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -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 = "getPopupContainer" :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,8 +27,12 @@
|
||||
placeholder: String,
|
||||
triggerChange: Boolean,
|
||||
disabled: Boolean,
|
||||
value: String,
|
||||
type: String
|
||||
value: [String, Number],
|
||||
type: String,
|
||||
getPopupContainer:{
|
||||
type: Function,
|
||||
default: (node) => node.parentNode
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@ -32,18 +40,40 @@
|
||||
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(){
|
||||
// update-begin author:wangshuai date:20200601 for: 不显示placeholder的文字 ------
|
||||
// 当有null或“” placeholder不显示
|
||||
return this.value != null ? this.value.toString() : undefined;
|
||||
// update-end author:wangshuai date:20200601 for: 不显示placeholder的文字 ------
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
initDictData() {
|
||||
//优先从缓存中读取字典配置
|
||||
if(getDictItemsFromCache(this.dictCode)){
|
||||
this.dictOptions = getDictItemsFromCache(this.dictCode);
|
||||
return
|
||||
}
|
||||
|
||||
//根据字典Code, 初始化字典数组
|
||||
ajaxGetDictItems(this.dictCode, null).then((res) => {
|
||||
if (res.success) {
|
||||
|
||||
@ -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,30 @@ 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 && Array.isArray(dictOptions)) {
|
||||
let result = []
|
||||
// 允许多个逗号分隔,允许传数组对象
|
||||
let splitText
|
||||
if (Array.isArray(text)) {
|
||||
splitText = text
|
||||
} else {
|
||||
splitText = text.toString().trim().split(',')
|
||||
}
|
||||
});
|
||||
return re;
|
||||
for (let txt of splitText) {
|
||||
let dictText = txt
|
||||
for (let dictItem of dictOptions) {
|
||||
if (txt.toString() === dictItem.value.toString()) {
|
||||
dictText = (dictItem.text || dictItem.title || dictItem.label)
|
||||
break
|
||||
}
|
||||
}
|
||||
result.push(dictText)
|
||||
}
|
||||
return result.join(',')
|
||||
}
|
||||
return text
|
||||
// --update-end----author:sunjianlei---date:20200323------for: 字典翻译 text 允许逗号分隔 ---
|
||||
}
|
||||
|
||||
/**
|
||||
@ -44,21 +68,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 +106,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([])
|
||||
})
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
@ -80,6 +90,14 @@
|
||||
handler(){
|
||||
this.initDictData()
|
||||
}
|
||||
},
|
||||
'dictOptions':{
|
||||
deep: true,
|
||||
handler(val){
|
||||
if(val && val.length>0){
|
||||
this.options = [...val]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
@ -98,7 +116,7 @@
|
||||
})
|
||||
}
|
||||
}else{
|
||||
this.selectedValue = this.value
|
||||
this.selectedValue = this.value.toString()
|
||||
}
|
||||
},
|
||||
loadData(value){
|
||||
@ -130,11 +148,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;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
163
ant-design-vue-jeecg/src/components/jeecg/JAreaLinkage.vue
Normal file
163
ant-design-vue-jeecg/src/components/jeecg/JAreaLinkage.vue
Normal file
@ -0,0 +1,163 @@
|
||||
<template>
|
||||
<div class="j-area-linkage">
|
||||
<div v-if="reloading">
|
||||
<span> Reloading... </span>
|
||||
</div>
|
||||
<area-cascader
|
||||
v-else-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: {
|
||||
|
||||
/** 重新加载组件 */
|
||||
reload() {
|
||||
this.reloading = true
|
||||
this.$nextTick(() => this.reloading = false)
|
||||
},
|
||||
|
||||
/** 通过 value 反推 options */
|
||||
loadDataByValue(value) {
|
||||
if (!value || value.length === 0) {
|
||||
this.innerValue = []
|
||||
} else {
|
||||
this.initAreaData()
|
||||
let arr = this.areaData.getRealCode(value)
|
||||
this.innerValue = arr
|
||||
}
|
||||
this.reload()
|
||||
},
|
||||
/** 通过地区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>
|
||||
243
ant-design-vue-jeecg/src/components/jeecg/JCategorySelect.vue
Normal file
243
ant-design-vue-jeecg/src/components/jeecg/JCategorySelect.vue
Normal file
@ -0,0 +1,243 @@
|
||||
<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 (Array.isArray(value)) {
|
||||
let labels = []
|
||||
let values = value.map(item => {
|
||||
labels.push(item.label)
|
||||
return item.value
|
||||
})
|
||||
this.backValue(values.join(','), labels.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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
@ -46,6 +46,8 @@
|
||||
import 'codemirror/mode/swift/swift.js'
|
||||
import 'codemirror/mode/vue/vue.js'
|
||||
|
||||
import { isIE11, isIE } from '@/utils/browser'
|
||||
|
||||
// 尝试获取全局实例
|
||||
const CodeMirror = window.CodeMirror || _CodeMirror
|
||||
|
||||
@ -85,12 +87,27 @@
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 999
|
||||
}
|
||||
},
|
||||
// 是否自适应高度,可以传String或Boolean
|
||||
// 传 String 类型只能写"!ie" ,
|
||||
// 填写这个字符串,代表其他浏览器自适应高度
|
||||
// 唯独IE下不自适应高度,因为IE下不支持min、max-height样式
|
||||
// 如果填写的不是"!ie"就视为true
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 不自适应高度的情况下生效的固定高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '240px'
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 内部真实的内容
|
||||
code: '',
|
||||
iconType: 'fullscreen',
|
||||
hasCode:false,
|
||||
// 默认的语法类型
|
||||
mode: 'javascript',
|
||||
@ -155,6 +172,15 @@
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullCoder:{
|
||||
handler(value) {
|
||||
if(value){
|
||||
this.iconType="fullscreen-exit"
|
||||
}else{
|
||||
this.iconType="fullscreen"
|
||||
}
|
||||
}
|
||||
},
|
||||
// value: {
|
||||
// immediate: false,
|
||||
// handler(value) {
|
||||
@ -207,14 +233,30 @@
|
||||
hintOptions: this.options.hintOptions
|
||||
}
|
||||
},
|
||||
fullScreenParentProps(){
|
||||
isAutoHeight() {
|
||||
let {autoHeight} = this
|
||||
if (typeof autoHeight === 'string' && autoHeight.toLowerCase().trim() === '!ie') {
|
||||
autoHeight = !(isIE() || isIE11())
|
||||
} else {
|
||||
autoHeight = true
|
||||
}
|
||||
return autoHeight
|
||||
},
|
||||
fullScreenParentProps() {
|
||||
let props = {
|
||||
class: ['full-screen-parent', this.fullCoder ? 'full-screen' : ''],
|
||||
class: {
|
||||
'full-screen-parent': true,
|
||||
'full-screen': this.fullCoder,
|
||||
'auto-height': this.isAutoHeight
|
||||
},
|
||||
style: {}
|
||||
}
|
||||
if (this.fullCoder) {
|
||||
props.style['z-index'] = this.zIndex
|
||||
}
|
||||
if (!this.isAutoHeight) {
|
||||
props.style['height'] = (typeof this.height === 'number' ? this.height + 'px' : this.height)
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
@ -228,10 +270,12 @@
|
||||
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
|
||||
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)
|
||||
this.setCodeContent(this.value || this.code)
|
||||
}else{
|
||||
this.coder.setValue('')
|
||||
this.hasCode=false
|
||||
}
|
||||
// 支持双向绑定
|
||||
@ -266,7 +310,13 @@
|
||||
return this.code
|
||||
},
|
||||
setCodeContent(val){
|
||||
this.coder.setValue(val)
|
||||
setTimeout(()=>{
|
||||
if(!val){
|
||||
this.coder.setValue('')
|
||||
}else{
|
||||
this.coder.setValue(val)
|
||||
}
|
||||
},300)
|
||||
},
|
||||
// 获取当前语法类型
|
||||
_getLanguage (language) {
|
||||
@ -391,6 +441,7 @@
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
@ -399,11 +450,27 @@
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
min-height: 120px;
|
||||
max-height: 320px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.auto-height {
|
||||
.full-screen-child {
|
||||
min-height: 120px;
|
||||
max-height: 320px;
|
||||
height: unset;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.full-screen .full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
.CodeMirror-cursor{
|
||||
height:18.4px !important;
|
||||
}
|
||||
</style>
|
||||
282
ant-design-vue-jeecg/src/components/jeecg/JCodeEditor2.vue
Normal file
282
ant-design-vue-jeecg/src/components/jeecg/JCodeEditor2.vue
Normal file
@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<div v-bind="fullScreenParentProps">
|
||||
<a-icon v-if="fullScreen" class="full-screen-icon" :type="iconType" @click="()=>fullCoder=!fullCoder"/>
|
||||
|
||||
<div class="code-editor-cust full-screen-child">
|
||||
<a-textarea auto-size v-model="textareaValue" :placeholder="placeholderShow" @change="handleChange" :style="{'max-height': maxHeight+'px','min-height': minHeight+'px'}"></a-textarea>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/ecmascript-6">
|
||||
export default {
|
||||
name: 'JCodeEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
fullScreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 全屏以后的z-index
|
||||
zIndex: {
|
||||
type: [Number, String],
|
||||
default: 999
|
||||
},
|
||||
// 是否自适应高度,可以传String或Boolean
|
||||
// 传 String 类型只能写"!ie" ,
|
||||
// 填写这个字符串,代表其他浏览器自适应高度
|
||||
// 唯独IE下不自适应高度,因为IE下不支持min、max-height样式
|
||||
// 如果填写的不是"!ie"就视为true
|
||||
autoHeight: {
|
||||
type: [String, Boolean],
|
||||
default: true
|
||||
},
|
||||
// 不自适应高度的情况下生效的固定高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: '240px'
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
minHeight:{
|
||||
type:Number,
|
||||
default: 100,
|
||||
required:false
|
||||
},
|
||||
maxHeight:{
|
||||
type:Number,
|
||||
default: 320,
|
||||
required:false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
textareaValue: '',
|
||||
// 内部真实的内容
|
||||
code: '',
|
||||
iconType: 'fullscreen',
|
||||
hasCode:false,
|
||||
// 默认的语法类型
|
||||
mode: 'javascript',
|
||||
// 编辑器实例
|
||||
coder: null,
|
||||
// 默认配置
|
||||
options: {
|
||||
// 缩进格式
|
||||
tabSize: 2,
|
||||
// 主题,对应主题库 JS 需要提前引入
|
||||
theme: 'panda-syntax',
|
||||
line: true,
|
||||
// extraKeys: {'Ctrl': 'autocomplete'},//自定义快捷键
|
||||
hintOptions: {
|
||||
tables: {
|
||||
users: ['name', 'score', 'birthDate'],
|
||||
countries: ['name', 'population', 'size']
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
// code 编辑器 是否全屏
|
||||
fullCoder: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
fullCoder:{
|
||||
handler(value) {
|
||||
if(value){
|
||||
this.iconType="fullscreen-exit"
|
||||
}else{
|
||||
this.iconType="fullscreen"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
placeholderShow() {
|
||||
if (this.placeholder == null) {
|
||||
return `请在此输入代码`
|
||||
} else {
|
||||
return this.placeholder
|
||||
}
|
||||
},
|
||||
isAutoHeight() {
|
||||
let {autoHeight} = this
|
||||
if (typeof autoHeight === 'string' && autoHeight.toLowerCase().trim() === '!ie') {
|
||||
autoHeight = !(isIE() || isIE11())
|
||||
} else {
|
||||
autoHeight = true
|
||||
}
|
||||
return autoHeight
|
||||
},
|
||||
fullScreenParentProps() {
|
||||
let props = {
|
||||
class: {
|
||||
'full-screen-parent': true,
|
||||
'full-screen': this.fullCoder,
|
||||
'auto-height': this.isAutoHeight
|
||||
},
|
||||
style: {}
|
||||
}
|
||||
if (this.fullCoder) {
|
||||
props.style['z-index'] = this.zIndex
|
||||
}
|
||||
if (!this.isAutoHeight) {
|
||||
props.style['height'] = (typeof this.height === 'number' ? this.height + 'px' : this.height)
|
||||
}
|
||||
return props
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
// 初始化
|
||||
this._initialize()
|
||||
},
|
||||
methods: {
|
||||
// 初始化
|
||||
_initialize () {
|
||||
this.setCodeContent(this.value)
|
||||
},
|
||||
handleChange(e){
|
||||
this.$emit('input', e.target.value)
|
||||
},
|
||||
getCodeContent(){
|
||||
return this.value
|
||||
},
|
||||
setCodeContent(val){
|
||||
setTimeout(()=>{
|
||||
if(!val){
|
||||
this.textareaValue = ''
|
||||
}else{
|
||||
this.textareaValue = val
|
||||
}
|
||||
},300)
|
||||
},
|
||||
nullTipClick(){
|
||||
this.coder.focus()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.code-editor-cust{
|
||||
flex-grow:1;
|
||||
display:flex;
|
||||
position:relative;
|
||||
height:100%;
|
||||
.CodeMirror{
|
||||
flex-grow:1;
|
||||
z-index:1;
|
||||
.CodeMirror-code{
|
||||
line-height:19px;
|
||||
}
|
||||
|
||||
}
|
||||
.code-mode-select{
|
||||
position:absolute;
|
||||
z-index:2;
|
||||
right:10px;
|
||||
top:10px;
|
||||
max-width:130px;
|
||||
}
|
||||
.CodeMirror{
|
||||
height: auto;
|
||||
min-height:100%;
|
||||
}
|
||||
.null-tip{
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: 36px;
|
||||
z-index: 10;
|
||||
color: #ffffffc9;
|
||||
line-height: initial;
|
||||
}
|
||||
.null-tip-hidden{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* 全屏样式 */
|
||||
.full-screen-parent {
|
||||
position: relative;
|
||||
|
||||
.full-screen-icon {
|
||||
opacity: 0;
|
||||
color: black;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 24px;
|
||||
background-color: white;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
z-index: 9;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.full-screen-icon {
|
||||
opacity: 1;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(255, 255, 255, 0.88);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.full-screen {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
width: calc(100% - 20px);
|
||||
height: calc(100% - 20px);
|
||||
padding: 10px;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
.full-screen-icon {
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.full-screen-child {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.auto-height {
|
||||
.full-screen-child {
|
||||
min-height: 120px;
|
||||
max-height: 320px;
|
||||
height: unset;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.full-screen .full-screen-child {
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.CodeMirror-cursor{
|
||||
height:18.4px !important;
|
||||
}
|
||||
</style>
|
||||
@ -18,9 +18,6 @@
|
||||
value: {
|
||||
required: false,
|
||||
type: String,
|
||||
default:()=>{
|
||||
return '* * * * * ? *'
|
||||
}
|
||||
}
|
||||
},
|
||||
data(){
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<a-date-picker
|
||||
dropdownClassName="j-date-picker"
|
||||
:disabled="disabled || readOnly"
|
||||
:placeholder="placeholder"
|
||||
@change="handleDateChange"
|
||||
@ -51,7 +52,7 @@
|
||||
},
|
||||
getCalendarContainer: {
|
||||
type: Function,
|
||||
default: () => document.body
|
||||
default: (node) => node.parentNode
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="tinymce-editor">
|
||||
<editor
|
||||
v-if="!reloading"
|
||||
v-model="myValue"
|
||||
:init="init"
|
||||
:disabled="disabled"
|
||||
@ -14,6 +15,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 +24,9 @@
|
||||
import 'tinymce/plugins/colorpicker'
|
||||
import 'tinymce/plugins/textcolor'
|
||||
import 'tinymce/plugins/fullscreen'
|
||||
import 'tinymce/icons/default'
|
||||
import { uploadAction,getFileAccessHttpUrl } from '@/api/manage'
|
||||
import { getVmParentByName } from '@/utils/util'
|
||||
export default {
|
||||
components: {
|
||||
Editor
|
||||
@ -42,11 +47,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,38 +67,86 @@
|
||||
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
|
||||
myValue: this.value,
|
||||
reloading: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
tinymce.init({})
|
||||
this.initATabsChangeAutoReload()
|
||||
},
|
||||
methods: {
|
||||
|
||||
reload() {
|
||||
this.reloading = true
|
||||
this.$nextTick(() => this.reloading = false)
|
||||
},
|
||||
|
||||
onClick(e) {
|
||||
this.$emit('onClick', e, tinymce)
|
||||
},
|
||||
//可以添加一些自己的自定义事件,如清空内容
|
||||
clear() {
|
||||
this.myValue = ''
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 自动判断父级是否是 <a-tabs/> 组件,然后添加事件监听,自动触发reload()
|
||||
*
|
||||
* 由于 tabs 组件切换会导致 tinymce 无法输入,
|
||||
* 只有重新加载才能使用(无论是vue版的还是jQuery版tinymce都有这个通病)
|
||||
*/
|
||||
initATabsChangeAutoReload() {
|
||||
// 获取父级
|
||||
let tabs = getVmParentByName(this, 'ATabs')
|
||||
let tabPane = getVmParentByName(this, 'ATabPane')
|
||||
if (tabs && tabPane) {
|
||||
// 用户自定义的 key
|
||||
let currentKey = tabPane.$vnode.key
|
||||
// 添加事件监听
|
||||
tabs.$on('change', (key) => {
|
||||
// 切换到自己时执行reload
|
||||
if (currentKey === key) {
|
||||
this.reload()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:富文本编辑器切换tab无法修改------
|
||||
let tabLayout = getVmParentByName(this, 'TabLayout')
|
||||
tabLayout.excuteCallback(()=>{
|
||||
this.reload()
|
||||
})
|
||||
//update--begin--autor:wangshuai-----date:20200724------for:文本编辑器切换tab无法修改------
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
value(newValue) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
@ -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>
|
||||
210
ant-design-vue-jeecg/src/components/jeecg/JImageUpload.vue
Normal file
210
ant-design-vue-jeecg/src/components/jeecg/JImageUpload.vue
Normal file
@ -0,0 +1,210 @@
|
||||
<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)
|
||||
}
|
||||
if(!val || val.length==0){
|
||||
this.picUrl = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
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(let a=0;a<uploadFiles.length;a++){
|
||||
// update-begin-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||
if(uploadFiles[a].status === 'done' ) {
|
||||
arr.push(uploadFiles[a].response.message)
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
// update-end-author:taoyan date:20200819 for:【开源问题z】上传图片组件 LOWCOD-783
|
||||
}
|
||||
}
|
||||
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>
|
||||
@ -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);
|
||||
});
|
||||
|
||||
111
ant-design-vue-jeecg/src/components/jeecg/JInput.vue
Normal file
111
ant-design-vue-jeecg/src/components/jeecg/JInput.vue
Normal file
@ -0,0 +1,111 @@
|
||||
<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:''
|
||||
},
|
||||
trim:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default:false
|
||||
}
|
||||
},
|
||||
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:
|
||||
//修复路由传参的值传送到jinput框被前后各截取了一位 #1336
|
||||
if(text.indexOf("*") != -1){
|
||||
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
|
||||
if(text && this.trim===true){
|
||||
text = text.trim()
|
||||
}
|
||||
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>
|
||||
@ -0,0 +1,30 @@
|
||||
export default {
|
||||
minHeight: '200px',
|
||||
previewStyle: 'vertical',
|
||||
useCommandShortcut: true,
|
||||
useDefaultHTMLSanitizer: true,
|
||||
usageStatistics: false,
|
||||
hideModeSwitch: false,
|
||||
toolbarItems: [
|
||||
'heading',
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'divider',
|
||||
'hr',
|
||||
'quote',
|
||||
'divider',
|
||||
'ul',
|
||||
'ol',
|
||||
'task',
|
||||
'indent',
|
||||
'outdent',
|
||||
'divider',
|
||||
'table',
|
||||
'image',
|
||||
'link',
|
||||
'divider',
|
||||
'code',
|
||||
'codeblock'
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,134 @@
|
||||
<template>
|
||||
<div class="j-markdown-editor" :id="id"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import '@toast-ui/editor/dist/toastui-editor.css';
|
||||
import '@toast-ui/editor/dist/i18n/zh-cn';
|
||||
|
||||
import Editor from '@toast-ui/editor';
|
||||
import defaultOptions from './default-options'
|
||||
|
||||
export default {
|
||||
name: 'JMarkdownEditor',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default() {
|
||||
return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default() {
|
||||
return defaultOptions
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'markdown'
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '300px'
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'zh-CN'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editorOptions() {
|
||||
const options = Object.assign({}, defaultOptions, this.options)
|
||||
options.initialEditType = this.mode
|
||||
options.height = this.height
|
||||
options.language = this.language
|
||||
return options
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(newValue, preValue) {
|
||||
if (newValue !== preValue && newValue !== this.editor.getMarkdown()) {
|
||||
this.editor.setMarkdown(newValue)
|
||||
}
|
||||
},
|
||||
language(val) {
|
||||
this.destroyEditor()
|
||||
this.initEditor()
|
||||
},
|
||||
height(newValue) {
|
||||
this.editor.height(newValue)
|
||||
},
|
||||
mode(newValue) {
|
||||
this.editor.changeMode(newValue)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initEditor()
|
||||
},
|
||||
destroyed() {
|
||||
this.destroyEditor()
|
||||
},
|
||||
methods: {
|
||||
initEditor() {
|
||||
this.editor = new Editor({
|
||||
el: document.getElementById(this.id),
|
||||
...this.editorOptions
|
||||
})
|
||||
if (this.value) {
|
||||
this.editor.setMarkdown(this.value)
|
||||
}
|
||||
this.editor.on('change', () => {
|
||||
this.$emit('change', this.editor.getMarkdown())
|
||||
})
|
||||
},
|
||||
destroyEditor() {
|
||||
if (!this.editor) return
|
||||
this.editor.off('change')
|
||||
this.editor.remove()
|
||||
},
|
||||
setMarkdown(value) {
|
||||
this.editor.setMarkdown(value)
|
||||
},
|
||||
getMarkdown() {
|
||||
return this.editor.getMarkdown()
|
||||
},
|
||||
setHtml(value) {
|
||||
this.editor.setHtml(value)
|
||||
},
|
||||
getHtml() {
|
||||
return this.editor.getHtml()
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
|
||||
.j-markdown-editor {
|
||||
/deep/ .tui-editor-defaultUI {
|
||||
.te-mode-switch,
|
||||
.tui-scrollsync
|
||||
{
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
228
ant-design-vue-jeecg/src/components/jeecg/JModal/index.vue
Normal file
228
ant-design-vue-jeecg/src/components/jeecg/JModal/index.vue
Normal file
@ -0,0 +1,228 @@
|
||||
<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'
|
||||
import { triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'JModal',
|
||||
props: {
|
||||
title: String,
|
||||
// 可使用 .sync 修饰符
|
||||
visible: Boolean,
|
||||
// 是否全屏弹窗,当全屏时无论如何都会禁止 body 滚动。可使用 .sync 修饰符
|
||||
fullscreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许切换全屏(允许后右上角会出现一个按钮)
|
||||
switchFullscreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 点击确定按钮的时候是否关闭弹窗
|
||||
okClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 内部使用的 slots ,不再处理
|
||||
usedSlots: ['title'],
|
||||
// 实际控制是否全屏的参数
|
||||
innerFullscreen: this.fullscreen,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 一些未处理的参数或特殊处理的参数绑定到 a-modal 上
|
||||
_attrs() {
|
||||
let attrs = { ...this.$attrs }
|
||||
// 如果全屏就将宽度设为 100%
|
||||
if (this.innerFullscreen) {
|
||||
attrs['width'] = '100%'
|
||||
}
|
||||
return attrs
|
||||
},
|
||||
modalClass() {
|
||||
return {
|
||||
'j-modal-box': true,
|
||||
'fullscreen': this.innerFullscreen,
|
||||
'no-title': this.isNoTitle,
|
||||
'no-footer': this.isNoFooter,
|
||||
}
|
||||
},
|
||||
modalStyle() {
|
||||
let style = {}
|
||||
// 如果全屏就将top设为 0
|
||||
if (this.innerFullscreen) {
|
||||
style['top'] = '0'
|
||||
}
|
||||
return style
|
||||
},
|
||||
isNoTitle() {
|
||||
return !this.title && !this.allSlotsKeys.includes('title')
|
||||
},
|
||||
isNoFooter() {
|
||||
return this._attrs['footer'] === null
|
||||
},
|
||||
slotsKeys() {
|
||||
return Object.keys(this.$slots).filter(key => !this.usedSlots.includes(key))
|
||||
},
|
||||
scopedSlotsKeys() {
|
||||
return Object.keys(this.$scopedSlots).filter(key => !this.usedSlots.includes(key))
|
||||
},
|
||||
allSlotsKeys() {
|
||||
return Object.keys(this.$slots).concat(Object.keys(this.$scopedSlots))
|
||||
},
|
||||
// 切换全屏的按钮图标
|
||||
fullscreenButtonIcon() {
|
||||
return this.innerFullscreen ? 'fullscreen-exit' : 'fullscreen'
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
visible() {
|
||||
if (this.visible) {
|
||||
this.innerFullscreen = this.fullscreen
|
||||
}
|
||||
},
|
||||
innerFullscreen(val) {
|
||||
this.$emit('update:fullscreen', val)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
getClass(clazz) {
|
||||
return { ...getClass(this), ...clazz }
|
||||
},
|
||||
getStyle(style) {
|
||||
return { ...getStyle(this), ...style }
|
||||
},
|
||||
|
||||
close() {
|
||||
this.$emit('update:visible', false)
|
||||
},
|
||||
|
||||
handleOk() {
|
||||
if (this.okClose) {
|
||||
this.close()
|
||||
}
|
||||
},
|
||||
handleCancel() {
|
||||
this.close()
|
||||
},
|
||||
|
||||
/** 切换全屏 */
|
||||
toggleFullscreen() {
|
||||
this.innerFullscreen = !this.innerFullscreen
|
||||
triggerWindowResizeEvent()
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-modal-box {
|
||||
|
||||
&.fullscreen {
|
||||
top: 0;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
|
||||
// 兼容1.6.2版本的antdv
|
||||
& .ant-modal {
|
||||
top: 0;
|
||||
padding: 0;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
& .ant-modal-content {
|
||||
height: 100vh;
|
||||
border-radius: 0;
|
||||
|
||||
& .ant-modal-body {
|
||||
/* title 和 footer 各占 55px */
|
||||
height: calc(100% - 55px - 55px);
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
&.no-title, &.no-footer {
|
||||
.ant-modal-body {
|
||||
height: calc(100% - 55px);
|
||||
}
|
||||
}
|
||||
|
||||
&.no-title.no-footer {
|
||||
.ant-modal-body {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.j-modal-title-row {
|
||||
.left {
|
||||
width: calc(100% - 56px - 56px);
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 56px;
|
||||
position: inherit;
|
||||
|
||||
.ant-modal-close {
|
||||
right: 56px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.75);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.j-modal-box.fullscreen {
|
||||
margin: 0;
|
||||
max-width: 100vw;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
205
ant-design-vue-jeecg/src/components/jeecg/JPopup.vue
Normal file
205
ant-design-vue-jeecg/src/components/jeecg/JPopup.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="components-input-demo-presuffix" v-if="avalid">
|
||||
<!---->
|
||||
<a-input @click="openModal" :placeholder="placeholder" v-model="showText" readOnly :disabled="disabled">
|
||||
<a-icon slot="prefix" type="cluster" :title="title"/>
|
||||
<a-icon v-if="showText" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
|
||||
</a-input>
|
||||
|
||||
<j-popup-onl-report
|
||||
ref="jPopupOnlReport"
|
||||
:code="code"
|
||||
:multi="multi"
|
||||
:groupId="uniqGroupId"
|
||||
@ok="callBack"
|
||||
/>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JPopupOnlReport from './modal/JPopupOnlReport'
|
||||
|
||||
export default {
|
||||
name: 'JPopup',
|
||||
components: {
|
||||
JPopupOnlReport
|
||||
},
|
||||
props: {
|
||||
code: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
orgFields: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
destFields: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 1200,
|
||||
required: false
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择',
|
||||
required: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
triggerChange: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
multi: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
/** 分组ID,用于将多个popup的请求合并到一起,不传不分组 */
|
||||
groupId: String
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showText: '',
|
||||
title: '',
|
||||
avalid: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
uniqGroupId() {
|
||||
if (this.groupId) {
|
||||
let { groupId, code, field, orgFields, destFields } = this
|
||||
return `${groupId}_${code}_${field}_${orgFields}_${destFields}`
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler: function(val) {
|
||||
if (!val) {
|
||||
this.showText = ''
|
||||
} else {
|
||||
this.showText = val
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
mounted() {
|
||||
if (!this.orgFields || !this.destFields || !this.code) {
|
||||
this.$message.error('popup参数未正确配置!')
|
||||
this.avalid = false
|
||||
}
|
||||
if (this.destFields.split(',').length != this.orgFields.split(',').length) {
|
||||
this.$message.error('popup参数未正确配置,原始值和目标值数量不一致!')
|
||||
this.avalid = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openModal() {
|
||||
if (this.disabled === false) {
|
||||
this.$refs.jPopupOnlReport.show()
|
||||
}
|
||||
},
|
||||
handleEmpty() {
|
||||
this.showText = ''
|
||||
let destFieldsArr = this.destFields.split(',')
|
||||
if (destFieldsArr.length === 0) {
|
||||
return
|
||||
}
|
||||
let res = {}
|
||||
for (let i = 0; i < destFieldsArr.length; i++) {
|
||||
res[destFieldsArr[i]] = ''
|
||||
}
|
||||
if (this.triggerChange) {
|
||||
this.$emit('callback', res)
|
||||
} else {
|
||||
this.$emit('input', '', res)
|
||||
}
|
||||
},
|
||||
callBack(rows) {
|
||||
// update--begin--autor:lvdandan-----date:20200630------for:多选时未带回多个值------
|
||||
let orgFieldsArr = this.orgFields.split(',')
|
||||
let destFieldsArr = this.destFields.split(',')
|
||||
let resetText = false
|
||||
if (this.field && this.field.length > 0) {
|
||||
this.showText = ''
|
||||
resetText = true
|
||||
}
|
||||
let res = {}
|
||||
if (orgFieldsArr.length > 0) {
|
||||
for (let i = 0; i < orgFieldsArr.length; i++) {
|
||||
let tempDestArr = []
|
||||
for(let rw of rows){
|
||||
let val = rw[orgFieldsArr[i]]
|
||||
if(!val){
|
||||
val = ""
|
||||
}
|
||||
tempDestArr.push(val)
|
||||
}
|
||||
res[destFieldsArr[i]] = tempDestArr.join(",")
|
||||
}
|
||||
if (resetText === true) {
|
||||
let tempText = []
|
||||
for(let rw of rows){
|
||||
let val = rw[orgFieldsArr[destFieldsArr.indexOf(this.field)]]
|
||||
if(!val){
|
||||
val = ""
|
||||
}
|
||||
tempText.push(val)
|
||||
}
|
||||
this.showText = tempText.join(",")
|
||||
}
|
||||
// update--end--autor:lvdandan-----date:20200630------for:多选时未带回多个值------
|
||||
}
|
||||
if (this.triggerChange) {
|
||||
//v-dec时即triggerChange为true时 将整个对象给form页面 让他自己setFieldsValue
|
||||
this.$emit('callback', res)
|
||||
} else {
|
||||
//v-model时 需要传一个参数field 表示当前这个字段 从而根据这个字段的顺序找到原始值
|
||||
// this.$emit("input",row[orgFieldsArr[destFieldsArr.indexOf(this.field)]])
|
||||
this.$emit('input', this.showText, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.components-input-demo-presuffix .anticon-close-circle {
|
||||
cursor: pointer;
|
||||
color: #ccc;
|
||||
transition: color 0.3s;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.components-input-demo-presuffix .anticon-close-circle:hover {
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
.components-input-demo-presuffix .anticon-close-circle:active {
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
@ -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事件
|
||||
},
|
||||
|
||||
@ -1,152 +1,652 @@
|
||||
<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-select v-else-if="item.type=='switch'" placeholder="请选择" v-model="item.val">
|
||||
<a-select-option value="Y">是</a-select-option>
|
||||
<a-select-option value="N">否</a-select-option>
|
||||
</a-select>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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 了
|
||||
//update-begin-author:taoyan date:20200819 for:【开源问题】 高级查询 下拉框作为并且选项很多多多 LOWCOD-779
|
||||
delete item.options
|
||||
//update-end-author:taoyan date:20200819 for:【开源问题】 高级查询 下拉框作为并且选项很多多多 LOWCOD-779
|
||||
}
|
||||
}
|
||||
}
|
||||
return array
|
||||
},
|
||||
|
||||
/** 渲染保存查询条件的 title(加个删除按钮) */
|
||||
renderSaveTreeData(item) {
|
||||
item.icon = this.treeIcon
|
||||
item.originTitle = item['title']
|
||||
item.title = (arg1, arg2) => {
|
||||
let vNode
|
||||
// 兼容旧版的Antdv
|
||||
if (arg1.dataRef) {
|
||||
vNode = arg1
|
||||
} else if (arg2.dataRef) {
|
||||
vNode = arg2
|
||||
} else {
|
||||
return <span style="color:red;">Antdv版本不支持</span>
|
||||
}
|
||||
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>
|
||||
83
ant-design-vue-jeecg/src/components/jeecg/JSwitch.vue
Normal file
83
ant-design-vue-jeecg/src/components/jeecg/JSwitch.vue
Normal file
@ -0,0 +1,83 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-select v-if="query" style="width: 100%" @change="handleSelectChange">
|
||||
<a-select-option v-for="(item, index) in queryOption" :key="index" :value="item.value">
|
||||
{{ item.text }}
|
||||
</a-select-option>
|
||||
</a-select>
|
||||
<a-switch v-else v-model="checkStatus" :disabled="disabled" @change="handleChange"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'JSwitch',
|
||||
props: {
|
||||
value:{
|
||||
type: String | Number,
|
||||
required: false
|
||||
},
|
||||
disabled:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
options:{
|
||||
type:Array,
|
||||
required:false,
|
||||
default:()=>['Y','N']
|
||||
},
|
||||
query:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
checkStatus: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value:{
|
||||
immediate: true,
|
||||
handler(val){
|
||||
if(!this.query){
|
||||
if(!val){
|
||||
this.checkStatus = false
|
||||
this.$emit('change', this.options[1]);
|
||||
}else{
|
||||
if(this.options[0]==val){
|
||||
this.checkStatus = true
|
||||
}else{
|
||||
this.checkStatus = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
queryOption(){
|
||||
let arr = []
|
||||
arr.push({value:this.options[0],text:'是'})
|
||||
arr.push({value:this.options[1],text:'否'})
|
||||
return arr;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(checked){
|
||||
let flag = checked===false?this.options[1]:this.options[0];
|
||||
this.$emit('change', flag);
|
||||
},
|
||||
handleSelectChange(value){
|
||||
this.$emit('change', value);
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
77
ant-design-vue-jeecg/src/components/jeecg/JTime.vue
Normal file
77
ant-design-vue-jeecg/src/components/jeecg/JTime.vue
Normal file
@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<a-time-picker
|
||||
:disabled="disabled || readOnly"
|
||||
:placeholder="placeholder"
|
||||
:value="momVal"
|
||||
:format="dateFormat"
|
||||
:getCalendarContainer="getCalendarContainer"
|
||||
@change="handleTimeChange"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
export default {
|
||||
name: 'JTime',
|
||||
props: {
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '',
|
||||
required: false
|
||||
},
|
||||
value:{
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
dateFormat:{
|
||||
type: String,
|
||||
default: 'HH:mm:ss',
|
||||
required: false
|
||||
},
|
||||
readOnly:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
disabled:{
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
getCalendarContainer: {
|
||||
type: Function,
|
||||
default: (node) => node.parentNode
|
||||
}
|
||||
},
|
||||
data () {
|
||||
let timeStr = this.value;
|
||||
return {
|
||||
decorator:"",
|
||||
momVal:!timeStr?null:moment(timeStr,this.dateFormat)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if(!val){
|
||||
this.momVal = null
|
||||
}else{
|
||||
this.momVal = moment(val,this.dateFormat)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
moment,
|
||||
handleTimeChange(mom,timeStr){
|
||||
this.$emit('change', timeStr);
|
||||
}
|
||||
},
|
||||
//2.2新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
|
||||
model: {
|
||||
prop: 'value',
|
||||
event: 'change'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -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,
|
||||
@ -121,7 +121,6 @@
|
||||
getAction(this.url_root,param).then(res=>{
|
||||
if(res.success){
|
||||
this.handleTreeNodeValue(res.result)
|
||||
console.log("aaaa",res.result)
|
||||
this.treeData = [...res.result]
|
||||
}else{
|
||||
this.$message.error(res.message)
|
||||
@ -180,7 +179,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){
|
||||
|
||||
@ -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,27 @@
|
||||
},
|
||||
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新增 在组件内定义 指定父组件调用时候的传值属性和事件类型 这个牛逼
|
||||
|
||||
@ -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 }))
|
||||
},
|
||||
|
||||
/** 点击展开图标时触发 */
|
||||
|
||||
@ -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]
|
||||
@ -114,7 +225,13 @@
|
||||
let arr = [];
|
||||
|
||||
for(var a=0;a<uploadFiles.length;a++){
|
||||
arr.push(uploadFiles[a].response.message)
|
||||
// update-begin-author:lvdandan date:20200603 for:【TESTA-514】【开源issue】多个文件同时上传时,控制台报错
|
||||
if(uploadFiles[a].status === 'done' ) {
|
||||
arr.push(uploadFiles[a].response.message)
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
// update-end-author:lvdandan date:20200603 for:【TESTA-514】【开源issue】多个文件同时上传时,控制台报错
|
||||
}
|
||||
if(arr.length>0){
|
||||
path = arr.join(",")
|
||||
@ -122,15 +239,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 +253,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 +278,143 @@
|
||||
}
|
||||
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++){
|
||||
// update-begin-author:lvdandan date:20200603 for:【TESTA-514】【开源issue】多个文件同时上传时,控制台报错
|
||||
if(fileList[a].status === 'done' ) {
|
||||
var fileJson = {
|
||||
fileName:fileList[a].name,
|
||||
filePath:fileList[a].response.message,
|
||||
fileSize:fileList[a].size
|
||||
};
|
||||
this.newFileList.push(fileJson);
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
// update-end-author:lvdandan date:20200603 for:【TESTA-514】【开源issue】多个文件同时上传时,控制台报错
|
||||
}
|
||||
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 +423,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>
|
||||
@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<j-modal
|
||||
title="详细信息"
|
||||
:width="1200"
|
||||
:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="close"
|
||||
switch-fullscreen
|
||||
:fullscreen.sync="fullscreen"
|
||||
>
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="visible">
|
||||
<slot name="mainForm" :row="row" :column="column"/>
|
||||
<slot name="subForm" :row="row" :column="column"/>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
</j-modal>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
import { cloneObject } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDetailsModal',
|
||||
inject: ['superTrigger'],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
fullscreen: false,
|
||||
row: null,
|
||||
column: null,
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
open(event) {
|
||||
let {row, column} = event
|
||||
this.row = cloneObject(row)
|
||||
this.column = column
|
||||
this.visible = true
|
||||
},
|
||||
|
||||
close() {
|
||||
this.visible = false
|
||||
},
|
||||
|
||||
handleOk() {
|
||||
this.superTrigger('detailsConfirm', {
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
callback: (success) => {
|
||||
this.visible = !success
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div :class="boxClass">
|
||||
<a-pagination
|
||||
:disabled="disabled"
|
||||
v-bind="bindProps"
|
||||
@change="handleChange"
|
||||
@showSizeChange="handleShowSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import PropTypes from 'ant-design-vue/es/_util/vue-types'
|
||||
|
||||
export default {
|
||||
name: 'JVxePagination',
|
||||
props: {
|
||||
size: String,
|
||||
disabled: PropTypes.bool,
|
||||
pagination: PropTypes.object.def({}),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
defaultPagination: {
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
pageSizeOptions: ['10', '20', '30'],
|
||||
showTotal: (total, range) => {
|
||||
return range[0] + '-' + range[1] + ' 共 ' + total + ' 条'
|
||||
},
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
total: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
bindProps() {
|
||||
return {
|
||||
...this.defaultPagination,
|
||||
...this.pagination,
|
||||
size: this.size === 'tiny' ? 'small' : ''
|
||||
}
|
||||
},
|
||||
boxClass() {
|
||||
return {
|
||||
'j-vxe-pagination': true,
|
||||
'show-quick-jumper': !!this.bindProps.showQuickJumper
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange(current, pageSize) {
|
||||
this.$set(this.pagination, 'current', current)
|
||||
this.$emit('change', {current, pageSize})
|
||||
},
|
||||
handleShowSizeChange(current, pageSize) {
|
||||
this.$set(this.pagination, 'pageSize', pageSize)
|
||||
this.$emit('change', {current, pageSize})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<a-popover :visible="visible" placement="bottom" overlayClassName="j-vxe-popover-overlay" :overlayStyle="overlayStyle">
|
||||
<div class="j-vxe-popover-title" slot="title">
|
||||
<div>子表</div>
|
||||
<div class="j-vxe-popover-title-close" @click="close">
|
||||
<a-icon type="close"/>
|
||||
</div>
|
||||
</div>
|
||||
<template slot="content">
|
||||
<transition name="fade">
|
||||
<slot v-if="visible" name="subForm" :row="row" :column="column"/>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<div ref="div" class="j-vxe-popover-div"></div>
|
||||
|
||||
</a-popover>
|
||||
</template>
|
||||
<script>
|
||||
import domAlign from 'dom-align'
|
||||
import { getParentNodeByTagName } from '../utils/vxeUtils'
|
||||
import { cloneObject, triggerWindowResizeEvent } from '@/utils/util'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSubPopover',
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
// 当前行
|
||||
row: null,
|
||||
column: null,
|
||||
|
||||
overlayStyle: {
|
||||
width: null,
|
||||
zIndex: 100
|
||||
},
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
|
||||
toggle(event) {
|
||||
if (this.row == null) {
|
||||
this.open(event)
|
||||
} else {
|
||||
this.row.id === event.row.id ? this.close() : this.reopen(event)
|
||||
}
|
||||
},
|
||||
|
||||
open(event, level = 0) {
|
||||
if (level > 3) {
|
||||
this.$message.error('打开子表失败')
|
||||
console.warn('【JVxeSubPopover】打开子表失败')
|
||||
return
|
||||
}
|
||||
|
||||
let {row, column, $table, $event: {target}} = event
|
||||
this.row = cloneObject(row)
|
||||
this.column = column
|
||||
|
||||
let className = target.className || ''
|
||||
className = typeof className === 'string' ? className : className.toString()
|
||||
|
||||
// 点击的是expand,不做处理
|
||||
if (className.includes('vxe-table--expand-btn')) {
|
||||
return
|
||||
}
|
||||
// 点击的是checkbox,不做处理
|
||||
if (className.includes('vxe-checkbox--icon') || className.includes('vxe-cell--checkbox')) {
|
||||
return
|
||||
}
|
||||
// 点击的是radio,不做处理
|
||||
if (className.includes('vxe-radio--icon') || className.includes('vxe-cell--radio')) {
|
||||
return
|
||||
}
|
||||
let table = $table.$el
|
||||
let tr = getParentNodeByTagName(target, 'tr')
|
||||
if (table && tr) {
|
||||
let clientWidth = table.clientWidth
|
||||
let clientHeight = tr.clientHeight
|
||||
this.$refs.div.style.width = clientWidth + 'px'
|
||||
this.$refs.div.style.height = clientHeight + 'px'
|
||||
this.overlayStyle.width = Number.parseInt((clientWidth - clientWidth * 0.04)) + 'px'
|
||||
this.overlayStyle.maxWidth = this.overlayStyle.width
|
||||
domAlign(this.$refs.div, tr, {
|
||||
points: ['tl', 'tl'],
|
||||
offset: [0, 0],
|
||||
overflow: {
|
||||
alwaysByViewport: true
|
||||
},
|
||||
})
|
||||
this.$nextTick(() => {
|
||||
this.visible = true
|
||||
this.$nextTick(() => {
|
||||
triggerWindowResizeEvent()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
let num = ++level
|
||||
console.warn('【JVxeSubPopover】table or tr 获取失败,正在进行第 ' + num + '次重试', {event, table, tr})
|
||||
window.setTimeout(() => this.open(event, num), 100)
|
||||
}
|
||||
},
|
||||
close() {
|
||||
if (this.visible) {
|
||||
this.row = null
|
||||
this.visible = false
|
||||
}
|
||||
},
|
||||
reopen(event) {
|
||||
this.close()
|
||||
this.open(event)
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="less">
|
||||
.j-vxe-popover-title {
|
||||
.j-vxe-popover-title-close {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 31px;
|
||||
height: 31px;
|
||||
text-align: center;
|
||||
line-height: 31px;
|
||||
color: rgba(0, 0, 0, 0.45);
|
||||
cursor: pointer;
|
||||
transition: color 300ms;
|
||||
|
||||
&:hover {
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-popover-div {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 31px;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.j-vxe-popover-overlay.ant-popover {
|
||||
.ant-popover-title {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
opacity: 1;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
.fade-enter,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<div :class="boxClass">
|
||||
<!-- 工具按钮 -->
|
||||
<div class="j-vxe-tool-button div" :size="btnSize">
|
||||
<slot v-if="showPrefix" name="toolbarPrefix" :size="btnSize"/>
|
||||
|
||||
<a-button v-if="showAdd" icon="plus" @click="trigger('add')" :disabled="disabled" type="primary">新增</a-button>
|
||||
<a-button v-if="showSave" icon="save" @click="trigger('save')" :disabled="disabled">保存</a-button>
|
||||
<template v-if="selectedRowIds.length > 0">
|
||||
<a-popconfirm
|
||||
v-if="showRemove"
|
||||
:title="`确定要删除这 ${selectedRowIds.length} 项吗?`"
|
||||
@confirm="trigger('remove')"
|
||||
>
|
||||
<a-button icon="minus" :disabled="disabled">删除</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-if="showClearSelection">
|
||||
<a-button icon="delete" @click="trigger('clearSelection')">清空选择</a-button>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<slot v-if="showSuffix" name="toolbarSuffix" :size="btnSize"/>
|
||||
<a v-if="showCollapse" @click="toggleCollapse" style="margin-left: 4px">
|
||||
<span>{{ collapsed ? '展开' : '收起' }}</span>
|
||||
<a-icon :type="collapsed ? 'down' : 'up'"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'JVxeToolbar',
|
||||
props: {
|
||||
toolbarConfig: Object,
|
||||
excludeCode: Array,
|
||||
size: String,
|
||||
disabled: Boolean,
|
||||
disabledRows: Object,
|
||||
selectedRowIds: Array,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 是否收起
|
||||
collapsed: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
boxClass() {
|
||||
return {
|
||||
'j-vxe-toolbar': true,
|
||||
'j-vxe-toolbar-collapsed': this.collapsed,
|
||||
}
|
||||
},
|
||||
|
||||
btns() {
|
||||
let arr = this.toolbarConfig.btn || ['add', 'remove', 'clearSelection']
|
||||
let exclude = [...this.excludeCode]
|
||||
// TODO 需要将remove替换batch_delete
|
||||
// 系统默认的批量删除编码配置为 batch_delete 此处需要转化一下
|
||||
if(exclude.indexOf('batch_delete')>=0){
|
||||
exclude.add('remove')
|
||||
}
|
||||
// 按钮权限 需要去掉不被授权的按钮
|
||||
return arr.filter(item=>{
|
||||
return exclude.indexOf(item)<0
|
||||
})
|
||||
},
|
||||
slots() {
|
||||
return this.toolbarConfig.slot || ['prefix', 'suffix']
|
||||
},
|
||||
showPrefix() {
|
||||
return this.slots.includes('prefix')
|
||||
},
|
||||
showSuffix() {
|
||||
return this.slots.includes('suffix')
|
||||
},
|
||||
showAdd() {
|
||||
return this.btns.includes('add')
|
||||
},
|
||||
showSave() {
|
||||
return this.btns.includes('save')
|
||||
},
|
||||
showRemove() {
|
||||
return this.btns.includes('remove')
|
||||
},
|
||||
showClearSelection() {
|
||||
if (this.btns.includes('clearSelection')) {
|
||||
// 有禁用行时才显示清空选择按钮
|
||||
// 因为禁用行会阻止选择行,导致无法取消全选
|
||||
let length = Object.keys(this.disabledRows).length
|
||||
return length > 0
|
||||
}
|
||||
return false
|
||||
},
|
||||
showCollapse() {
|
||||
return this.btns.includes('collapse')
|
||||
},
|
||||
|
||||
btnSize() {
|
||||
return this.size === 'tiny' ? 'small' : null
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 触发事件 */
|
||||
trigger(name) {
|
||||
this.$emit(name)
|
||||
},
|
||||
// 切换展开收起
|
||||
toggleCollapse() {
|
||||
this.collapsed = !this.collapsed
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-vxe-toolbar-collapsed {
|
||||
[data-collapse] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-tool-button.div .ant-btn {
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<div :class="clazz" :style="boxStyle">
|
||||
<a-checkbox
|
||||
ref="checkbox"
|
||||
:checked="innerValue"
|
||||
v-bind="cellProps"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { neverNull } from '@/utils/util'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeCheckboxCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
computed: {
|
||||
bordered() {
|
||||
return !!this.renderOptions.bordered
|
||||
},
|
||||
scrolling() {
|
||||
return !!this.renderOptions.scrolling
|
||||
},
|
||||
clazz() {
|
||||
return {
|
||||
'j-vxe-checkbox': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
},
|
||||
boxStyle() {
|
||||
const style = {}
|
||||
// 如果有边框且未设置align属性,就强制居中
|
||||
if (this.bordered && !this.originColumn.align) {
|
||||
style['text-align'] = 'center'
|
||||
}
|
||||
return style
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleChange(event) {
|
||||
this.handleChangeCommon(event.target.checked)
|
||||
},
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
visible: true,
|
||||
},
|
||||
getValue(value) {
|
||||
let {own: col} = this.column
|
||||
// 处理 customValue
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
if (typeof value === 'boolean') {
|
||||
return value ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue(value) {
|
||||
let {own: col} = this.column
|
||||
// 判断是否设定了customValue(自定义值)
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
return neverNull(value).toString() === customValue[0].toString()
|
||||
} else {
|
||||
return !!value
|
||||
}
|
||||
},
|
||||
createValue({column}) {
|
||||
let {own: col} = column
|
||||
if (Array.isArray(col.customValue)) {
|
||||
let customValue = getCustomValue(col)
|
||||
return col.defaultChecked ? customValue[0] : customValue[1]
|
||||
} else {
|
||||
return !!col.defaultChecked
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function getCustomValue(col) {
|
||||
let customTrue = neverNull(col.customValue[0], true)
|
||||
let customFalse = neverNull(col.customValue[1], false)
|
||||
return [customTrue, customFalse]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
// 关闭动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-checkbox.no-animation {
|
||||
.ant-checkbox-inner,
|
||||
.ant-checkbox-inner::after {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<a-date-picker
|
||||
ref="datePicker"
|
||||
:value="innerDateValue"
|
||||
allowClear
|
||||
:format="dateFormat"
|
||||
:showTime="isDatetime"
|
||||
dropdownClassName="j-vxe-date-picker"
|
||||
style="min-width: 0;"
|
||||
v-bind="cellProps"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import moment from 'moment'
|
||||
import { JVXETypes } from '@/components/jeecg/JVxeTable/index'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDateCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerDateValue: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isDatetime() {
|
||||
return this.$type === JVXETypes.datetime
|
||||
},
|
||||
dateFormat() {
|
||||
let format = this.originColumn.format
|
||||
return format ? format : (this.isDatetime ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val == null || val === '') {
|
||||
this.innerDateValue = null
|
||||
} else {
|
||||
this.innerDateValue = moment(val, this.dateFormat)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleChange(mom, dateStr) {
|
||||
this.handleChangeCommon(dateStr)
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-calendar-picker', el => el.children[0].dispatchEvent(event.$event)),
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,138 @@
|
||||
<template>
|
||||
<a-dropdown :trigger="['click']">
|
||||
<div class="j-vxe-ds-icons">
|
||||
<a-icon type="align-left"/>
|
||||
<a-icon type="align-right"/>
|
||||
</div>
|
||||
|
||||
<!-- <div class="j-vxe-ds-btns">-->
|
||||
<!-- <a-button icon="caret-up" size="small" :disabled="disabledMoveUp" @click="handleRowMoveUp"/>-->
|
||||
<!-- <a-button icon="caret-down" size="small" :disabled="disabledMoveDown" @click="handleRowMoveDown"/>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item key="0" :disabled="disabledMoveUp" @click="handleRowMoveUp">向上移</a-menu-item>
|
||||
<a-menu-item key="1" :disabled="disabledMoveDown" @click="handleRowMoveDown">向下移</a-menu-item>
|
||||
<a-menu-divider/>
|
||||
<a-menu-item key="3" @click="handleRowInsertDown">插入一行</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeDragSortCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
// 排序结果保存字段
|
||||
dragSortKey() {
|
||||
return this.renderOptions.dragSortKey || 'orderNum'
|
||||
},
|
||||
disabledMoveUp() {
|
||||
return this.rowIndex === 0
|
||||
},
|
||||
disabledMoveDown() {
|
||||
return this.rowIndex === (this.rows.length - 1)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
/** 向上移 */
|
||||
handleRowMoveUp(event) {
|
||||
// event.target.blur()
|
||||
if (!this.disabledMoveUp) {
|
||||
this.trigger('rowMoveUp', this.rowIndex)
|
||||
}
|
||||
},
|
||||
/** 向下移 */
|
||||
handleRowMoveDown(event) {
|
||||
// event.target.blur()
|
||||
if (!this.disabledMoveDown) {
|
||||
this.trigger('rowMoveDown', this.rowIndex)
|
||||
}
|
||||
},
|
||||
/** 插入一行 */
|
||||
handleRowInsertDown() {
|
||||
this.trigger('rowInsertDown', this.rowIndex)
|
||||
},
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
// 【功能开关】
|
||||
switches: {
|
||||
editRender: false
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.j-vxe-ds-icons {
|
||||
position: relative;
|
||||
/*cursor: move;*/
|
||||
cursor: pointer;
|
||||
width: 14px;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
|
||||
.anticon-align-left,
|
||||
.anticon-align-right {
|
||||
position: absolute;
|
||||
top: 30%;
|
||||
}
|
||||
|
||||
.anticon-align-left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.anticon-align-right {
|
||||
right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.j-vxe-ds-btns {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
width: 24px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-content: center;
|
||||
|
||||
.ant-btn {
|
||||
border: none;
|
||||
|
||||
z-index: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
/*height: 30%;*/
|
||||
height: 40%;
|
||||
display: block;
|
||||
border-radius: 0;
|
||||
|
||||
&:hover {
|
||||
z-index: 1;
|
||||
/* height: 40%;*/
|
||||
|
||||
/* & .anticon-caret-up,*/
|
||||
/* & .anticon-caret-down {*/
|
||||
/* top: 2px;*/
|
||||
/* }*/
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
& .anticon-caret-up,
|
||||
& .anticon-caret-down {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: 0;
|
||||
transition: top 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<a-input
|
||||
ref="input"
|
||||
:value="innerValue"
|
||||
v-bind="cellProps"
|
||||
@blur="handleBlur"
|
||||
@change="handleChange"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { JVXETypes } from '@/components/jeecg/JVxeTable'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
const NumberRegExp = /^-?\d+\.?\d*$/
|
||||
export default {
|
||||
name: 'JVxeInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
methods: {
|
||||
|
||||
/** 处理change事件 */
|
||||
handleChange(event) {
|
||||
let {$type} = this
|
||||
let {target} = event
|
||||
let {value, selectionStart} = target
|
||||
let change = true
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
// 判断输入的值是否匹配数字正则表达式,不匹配就还原
|
||||
if (!NumberRegExp.test(value) && (value !== '' && value !== '-')) {
|
||||
change = false
|
||||
value = this.innerValue
|
||||
target.value = value || ''
|
||||
if (typeof selectionStart === 'number') {
|
||||
target.selectionStart = selectionStart - 1
|
||||
target.selectionEnd = selectionStart - 1
|
||||
}
|
||||
}
|
||||
}
|
||||
// 触发事件,存储输入的值
|
||||
if (change) {
|
||||
this.handleChangeCommon(value)
|
||||
}
|
||||
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
// this.recalcOneStatisticsColumn(col.key)
|
||||
}
|
||||
},
|
||||
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur(event) {
|
||||
let {$type} = this
|
||||
let {target} = event
|
||||
// 判断输入的值是否匹配数字正则表达式,不匹配就置空
|
||||
if ($type === JVXETypes.inputNumber) {
|
||||
if (!NumberRegExp.test(target.value)) {
|
||||
target.value = ''
|
||||
} else {
|
||||
target.value = Number.parseFloat(target.value)
|
||||
}
|
||||
this.handleChangeCommon(target.value)
|
||||
}
|
||||
|
||||
this.handleBlurCommon(target.value)
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
installOptions: {
|
||||
// 自动聚焦的 class 类名
|
||||
autofocus: '.ant-input',
|
||||
},
|
||||
getValue(value) {
|
||||
if (this.$type === JVXETypes.inputNumber && typeof value === 'string') {
|
||||
if (NumberRegExp.test(value)) {
|
||||
return Number.parseFloat(value)
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<reload-effect
|
||||
:vNode="innerValue"
|
||||
:effect="reloadEffect"
|
||||
@effect-end="handleEffectEnd"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ReloadEffect from './ReloadEffect'
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeNormalCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {ReloadEffect},
|
||||
computed: {
|
||||
reloadEffectRowKeysMap() {
|
||||
return this.renderOptions.reloadEffectRowKeysMap
|
||||
},
|
||||
reloadEffect() {
|
||||
return (this.renderOptions.reloadEffect && this.reloadEffectRowKeysMap[this.row.id]) === true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// 特效结束
|
||||
handleEffectEnd() {
|
||||
this.$delete(this.reloadEffectRowKeysMap, this.row.id)
|
||||
},
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<a-progress
|
||||
:class="clazz"
|
||||
:percent="innerValue"
|
||||
size="small"
|
||||
v-bind="cellProps"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// JVxe 进度条组件
|
||||
export default {
|
||||
name: 'JVxeProgressCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
clazz() {
|
||||
return {
|
||||
'j-vxe-progress': true,
|
||||
'no-animation': this.scrolling
|
||||
}
|
||||
},
|
||||
scrolling() {
|
||||
return !!this.renderOptions.scrolling
|
||||
},
|
||||
},
|
||||
methods: {},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false,
|
||||
},
|
||||
setValue(value) {
|
||||
try {
|
||||
if (typeof value !== 'number') {
|
||||
return Number.parseFloat(value)
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
} catch {
|
||||
return 0
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
// 关闭进度条的动画,防止滚动时动态赋值出现问题
|
||||
.j-vxe-progress.no-animation {
|
||||
/deep/ .ant-progress-success-bg,
|
||||
/deep/ .ant-progress-bg {
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<a-select
|
||||
ref="select"
|
||||
:value="innerValue"
|
||||
allowClear
|
||||
:filterOption="handleSelectFilterOption"
|
||||
v-bind="selectProps"
|
||||
style="width: 100%;"
|
||||
@blur="handleBlur"
|
||||
@change="handleChangeCommon"
|
||||
@search="handleSearchSelect"
|
||||
>
|
||||
|
||||
<template v-for="option of originColumn.options">
|
||||
<a-select-option :key="option.value" :value="option.value" :disabled="option.disabled">
|
||||
<span>{{option.text || option.label || option.title|| option.value}}</span>
|
||||
</a-select-option>
|
||||
</template>
|
||||
|
||||
</a-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
|
||||
|
||||
export default {
|
||||
name: 'JVxeSelectCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
selectProps() {
|
||||
let props = {...this.cellProps}
|
||||
// 判断select是否允许输入
|
||||
let {allowSearch, allowInput} = this.originColumn
|
||||
if (allowInput === true || allowSearch === true) {
|
||||
props['showSearch'] = true
|
||||
}
|
||||
return props
|
||||
},
|
||||
},
|
||||
created() {
|
||||
let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
|
||||
let search = [JVXETypes.selectSearch, JVXETypes.sel_search]
|
||||
if (multiple.includes(this.$type)) {
|
||||
// 处理多选
|
||||
let props = this.originColumn.props || {}
|
||||
props['mode'] = 'multiple'
|
||||
props['maxTagCount'] = 1
|
||||
this.$set(this.originColumn, 'props', props)
|
||||
} else if (search.includes(this.$type)) {
|
||||
// 处理搜索
|
||||
this.$set(this.originColumn, 'allowSearch', true)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
/** 处理blur失去焦点事件 */
|
||||
handleBlur(value) {
|
||||
let {allowInput, options} = this.originColumn
|
||||
|
||||
if (allowInput === true) {
|
||||
// 删除无用的因搜索(用户输入)而创建的项
|
||||
if (typeof value === 'string') {
|
||||
let indexes = []
|
||||
options.forEach((option, index) => {
|
||||
if (option.value.toLocaleString() === value.toLocaleString()) {
|
||||
delete option.searchAdd
|
||||
} else if (option.searchAdd === true) {
|
||||
indexes.push(index)
|
||||
}
|
||||
})
|
||||
// 翻转删除数组中的项
|
||||
for (let index of indexes.reverse()) {
|
||||
options.splice(index, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.handleBlurCommon(value)
|
||||
},
|
||||
|
||||
/** 用于搜索下拉框中的内容 */
|
||||
handleSelectFilterOption(input, option) {
|
||||
let {allowSearch, allowInput} = this.originColumn
|
||||
if (allowSearch === true || allowInput === true) {
|
||||
//update-begin-author:taoyan date:20200820 for:【专项任务】大连项目反馈行编辑问题处理 下拉框搜索
|
||||
return option.componentOptions.children[0].children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
|
||||
//update-end-author:taoyan date:20200820 for:【专项任务】大连项目反馈行编辑问题处理 下拉框搜索
|
||||
}
|
||||
return true
|
||||
},
|
||||
|
||||
/** select 搜索时的事件,用于动态添加options */
|
||||
handleSearchSelect(value) {
|
||||
let {allowSearch, allowInput, options} = this.originColumn
|
||||
|
||||
if (allowSearch !== true && allowInput === true) {
|
||||
// 是否找到了对应的项,找不到则添加这一项
|
||||
let flag = false
|
||||
for (let option of options) {
|
||||
if (option.value.toLocaleString() === value.toLocaleString()) {
|
||||
flag = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// !!value :不添加空值
|
||||
if (!flag && !!value) {
|
||||
// searchAdd 是否是通过搜索添加的
|
||||
options.push({title: value, value: value, searchAdd: true})
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'ant-select'),
|
||||
},
|
||||
translate: {enabled: true},
|
||||
getValue(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value.join(',')
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
setValue(value) {
|
||||
let {column: {own: col}, params: {$table}} = this
|
||||
// 判断是否是多选
|
||||
if ((col.props || {})['mode'] === 'multiple') {
|
||||
$table.$set(col.props, 'maxTagCount', 1)
|
||||
}
|
||||
if (value != null && value !== '') {
|
||||
if (typeof value === 'string') {
|
||||
return value === '' ? [] : value.split(',')
|
||||
}
|
||||
return value
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,46 @@
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// 插槽
|
||||
export default {
|
||||
name: 'JVxeSlotCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
computed: {
|
||||
slotProps() {
|
||||
return {
|
||||
value: this.innerValue,
|
||||
row: this.row,
|
||||
column: this.originColumn,
|
||||
|
||||
params: this.params,
|
||||
$table: this.params.$table,
|
||||
rowId: this.params.rowid,
|
||||
index: this.params.rowIndex,
|
||||
rowIndex: this.params.rowIndex,
|
||||
columnIndex: this.params.columnIndex,
|
||||
|
||||
target: this.renderOptions.target,
|
||||
caseId: this.renderOptions.target.caseId,
|
||||
scrolling: this.renderOptions.scrolling,
|
||||
reloadEffect: this.renderOptions.reloadEffect,
|
||||
|
||||
triggerChange: (v) => this.handleChangeCommon(v),
|
||||
}
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
let {slot} = this.renderOptions
|
||||
if (slot) {
|
||||
return h('div', {}, slot(this.slotProps))
|
||||
} else {
|
||||
return h('div')
|
||||
}
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {
|
||||
editRender: false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// :isNotPass="notPassedIds.includes(col.key+row.id)"
|
||||
@ -0,0 +1,145 @@
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
// tags 组件的显示组件
|
||||
export const TagsSpanCell = {
|
||||
name: 'JVxeTagsCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data() {
|
||||
return {
|
||||
innerTags: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
if (value !== this.innerTags.join(';')) {
|
||||
let rv = replaceValue(value)
|
||||
this.innerTags = rv.split(';')
|
||||
this.handleChangeCommon(rv)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
renderTags(h) {
|
||||
let tags = []
|
||||
for (let tag of this.innerTags) {
|
||||
if (tag) {
|
||||
let tagProps = {}
|
||||
let tagStyle = {}
|
||||
let setTagColor = this.originColumn.setTagColor
|
||||
if (typeof setTagColor === 'function') {
|
||||
/**
|
||||
* 设置 tag 颜色
|
||||
*
|
||||
* @param event 包含的字段:
|
||||
* event.tagValue 当前tag的值
|
||||
* event.value 当前原始值
|
||||
* event.row 当前行的所有值
|
||||
* event.column 当前列的配置
|
||||
* event.column.own 当前列的原始配置
|
||||
* @return Array | String 可以返回一个数组,数据第一项是tag背景颜色,第二项是字体颜色。也可以返回一个字符串,即tag背景颜色
|
||||
*/
|
||||
let color = setTagColor({
|
||||
tagValue: tag,
|
||||
value: this.innerValue,
|
||||
row: this.row,
|
||||
column: this.column,
|
||||
})
|
||||
if (Array.isArray(color)) {
|
||||
tagProps.color = color[0]
|
||||
tagStyle.color = color[1]
|
||||
} else if (color && typeof color === 'string') {
|
||||
tagProps.color = color
|
||||
}
|
||||
}
|
||||
tags.push(h('a-tag', {
|
||||
props: tagProps,
|
||||
style: tagStyle,
|
||||
}, [tag]))
|
||||
}
|
||||
}
|
||||
return tags
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
return h('div', {}, [
|
||||
this.renderTags(h)
|
||||
])
|
||||
},
|
||||
}
|
||||
|
||||
// tags 组件的输入框
|
||||
export const TagsInputCell = {
|
||||
name: 'JVxeTagsInputCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
data() {
|
||||
return {
|
||||
innerTagValue: '',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
if (value !== this.innerTagValue) {
|
||||
this.handleInputChange(value)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleInputChange(value, event) {
|
||||
this.innerTagValue = replaceValue(value, event)
|
||||
this.handleChangeCommon(this.innerTagValue)
|
||||
return this.innerTagValue
|
||||
},
|
||||
|
||||
},
|
||||
render(h) {
|
||||
return h('a-input', {
|
||||
props: {
|
||||
value: this.innerValue,
|
||||
...this.cellProps
|
||||
},
|
||||
on: {
|
||||
change: (event) => {
|
||||
let {target, target: {value}} = event
|
||||
let newValue = this.handleInputChange(value, event)
|
||||
if (newValue !== value) {
|
||||
target.value = newValue
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
// 将值每隔两位加上一个分号
|
||||
function replaceValue(value, event) {
|
||||
if (value) {
|
||||
// 首先去掉现有的分号
|
||||
value = value.replace(/;/g, '')
|
||||
// 然后再遍历添加分号
|
||||
let rv = ''
|
||||
let splitArr = value.split('')
|
||||
let count = 0
|
||||
splitArr.forEach((val, index) => {
|
||||
rv += val
|
||||
let position = index + 1
|
||||
if (position % 2 === 0 && position < splitArr.length) {
|
||||
count++
|
||||
rv += ';'
|
||||
}
|
||||
})
|
||||
if (event && count > 0) {
|
||||
let {target, target: {selectionStart}} = event
|
||||
target.selectionStart = selectionStart + count
|
||||
target.selectionEnd = selectionStart + count
|
||||
}
|
||||
return rv
|
||||
}
|
||||
return ''
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<j-input-pop
|
||||
:value="innerValue"
|
||||
:width="300"
|
||||
:height="210"
|
||||
v-bind="cellProps"
|
||||
style="width: 100%;"
|
||||
@change="handleChangeCommon"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JInputPop from '@/components/jeecg/minipop/JInputPop'
|
||||
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
|
||||
export default {
|
||||
name: 'JVxeTextareaCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
components: {JInputPop},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
installOptions: {
|
||||
autofocus: '.ant-input',
|
||||
},
|
||||
aopEvents: {
|
||||
editActived: event => dispatchEvent(event, 'anticon-fullscreen'),
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,178 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
|
||||
<a-input
|
||||
:key="fileKey"
|
||||
:readOnly="true"
|
||||
:value="file.name"
|
||||
>
|
||||
|
||||
<template slot="addonBefore" style="width: 30px">
|
||||
<a-tooltip v-if="file.status === 'uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
|
||||
<a-icon type="loading"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else-if="file.status === 'done'" title="上传完成">
|
||||
<a-icon type="check-circle" style="color:#00DB00;"/>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-else title="上传失败">
|
||||
<a-icon type="exclamation-circle" style="color:red;"/>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<span v-if="file.status === 'uploading'" slot="addonAfter">{{ Math.floor(file.percent) }}%</span>
|
||||
<template v-else-if="originColumn.allowDownload !== false || originColumn.allowRemove !== false" slot="addonAfter">
|
||||
<a-dropdown :trigger="['click']" placement="bottomRight">
|
||||
<a-tooltip title="操作">
|
||||
<a-icon
|
||||
type="setting"
|
||||
style="cursor: pointer;"/>
|
||||
</a-tooltip>
|
||||
|
||||
<a-menu slot="overlay">
|
||||
<!-- <a-menu-item @click="handleClickPreviewFile">-->
|
||||
<!-- <span><a-icon type="eye"/> 预览</span>-->
|
||||
<!-- </a-menu-item>-->
|
||||
<a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
|
||||
<span><a-icon type="download"/> 下载</span>
|
||||
</a-menu-item>
|
||||
<a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
|
||||
<span><a-icon type="delete"/> 删除</span>
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</a-dropdown>
|
||||
</template>
|
||||
|
||||
</a-input>
|
||||
</template>
|
||||
<a-upload
|
||||
v-show="!hasFile"
|
||||
name="file"
|
||||
:data="{'isup': 1}"
|
||||
:multiple="false"
|
||||
:action="originColumn.action"
|
||||
:headers="uploadHeaders"
|
||||
:showUploadList="false"
|
||||
v-bind="cellProps"
|
||||
@change="handleChangeUpload"
|
||||
>
|
||||
<a-button icon="upload">{{originColumn.btnText || '点击上传'}}</a-button>
|
||||
</a-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
|
||||
import { ACCESS_TOKEN } from '@/store/mutation-types'
|
||||
import { getFileAccessHttpUrl } from '@api/manage'
|
||||
|
||||
export default {
|
||||
name: 'JVxeUploadCell',
|
||||
mixins: [JVxeCellMixins],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
innerFile: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/** upload headers */
|
||||
uploadHeaders() {
|
||||
let {originColumn: col} = this
|
||||
let headers = {}
|
||||
if (col.token === true) {
|
||||
headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
|
||||
}
|
||||
return headers
|
||||
},
|
||||
|
||||
hasFile() {
|
||||
return this.innerFile != null
|
||||
},
|
||||
|
||||
},
|
||||
watch: {
|
||||
innerValue: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
if (this.innerValue) {
|
||||
this.innerFile = this.innerValue
|
||||
} else {
|
||||
this.innerFile = null
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
handleChangeUpload(info) {
|
||||
let {row, originColumn: col} = this
|
||||
let {file} = info
|
||||
let value = {
|
||||
name: file.name,
|
||||
type: file.type,
|
||||
size: file.size,
|
||||
status: file.status,
|
||||
percent: file.percent
|
||||
}
|
||||
if (col.responseName && file.response) {
|
||||
value['responseName'] = file.response[col.responseName]
|
||||
}
|
||||
if (file.status === 'done') {
|
||||
value['path'] = file.response[col.responseName]
|
||||
this.handleChangeCommon(value)
|
||||
} else if (file.status === 'error') {
|
||||
value['message'] = file.response.message || '未知错误'
|
||||
}
|
||||
this.innerFile = value
|
||||
},
|
||||
|
||||
// handleClickPreviewFile(id) {
|
||||
// this.$message.info('尚未实现')
|
||||
// },
|
||||
|
||||
handleClickDownloadFile(id) {
|
||||
let {path} = this.value || {}
|
||||
if (path) {
|
||||
let url = getFileAccessHttpUrl(path)
|
||||
window.open(url)
|
||||
}
|
||||
},
|
||||
|
||||
handleClickDeleteFile() {
|
||||
this.handleChangeCommon(null)
|
||||
},
|
||||
|
||||
},
|
||||
// 【组件增强】注释详见:JVxeCellMixins.js
|
||||
enhanced: {
|
||||
switches: {visible: true},
|
||||
getValue: value => fileGetValue(value),
|
||||
setValue: value => fileSetValue(value),
|
||||
}
|
||||
}
|
||||
|
||||
function fileGetValue(value) {
|
||||
if (value && value.path) {
|
||||
return value.path
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
function fileSetValue(value) {
|
||||
if (value) {
|
||||
let first = value.split(',')[0]
|
||||
let name = first.substring(first.lastIndexOf('/') + 1)
|
||||
return {
|
||||
name: name,
|
||||
path: value,
|
||||
status: 'done',
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@ -0,0 +1,84 @@
|
||||
import '../../less/reload-effect.less'
|
||||
import { randomString } from '@/utils/util'
|
||||
|
||||
// 修改数据特效
|
||||
export default {
|
||||
props: {
|
||||
vNode: null,
|
||||
// 是否启用特效
|
||||
effect: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// vNode: null,
|
||||
innerEffect: false,
|
||||
// 应付同时多个特效
|
||||
effectIdx: 0,
|
||||
effectList: [],
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
vNode: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler(vNode, old) {
|
||||
this.innerEffect = this.effect
|
||||
if (this.innerEffect && old != null) {
|
||||
let topLayer = this.renderSpan(old, 'top')
|
||||
this.effectList.push(topLayer)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
// 条件渲染内容 span
|
||||
renderVNode() {
|
||||
if (this.vNode == null) {
|
||||
return null
|
||||
}
|
||||
let bottom = this.renderSpan(this.vNode, 'bottom')
|
||||
// 启用了特效,并且有旧数据,就渲染特效顶层
|
||||
if (this.innerEffect && this.effectList.length > 0) {
|
||||
this.$emit('effect-begin')
|
||||
// 1.4s 以后关闭特效
|
||||
window.setTimeout(() => {
|
||||
let item = this.effectList[this.effectIdx]
|
||||
if (item && item.elm) {
|
||||
// 特效结束后,展示先把 display 设为 none,而不是直接删掉该元素,
|
||||
// 目的是为了防止页面重新渲染,导致动画重置
|
||||
item.elm.style.display = 'none'
|
||||
}
|
||||
// 当所有的层级动画都结束时,再删掉所有元素
|
||||
if (++this.effectIdx === this.effectList.length) {
|
||||
this.innerEffect = false
|
||||
this.effectIdx = 0
|
||||
this.effectList = []
|
||||
this.$emit('effect-end')
|
||||
}
|
||||
}, 1400)
|
||||
return [this.effectList, bottom]
|
||||
} else {
|
||||
return bottom
|
||||
}
|
||||
},
|
||||
// 渲染内容 span
|
||||
renderSpan(vNode, layer) {
|
||||
let options = {
|
||||
key: layer + this.effectIdx + randomString(6),
|
||||
class: ['j-vxe-reload-effect-span', `layer-${layer}`],
|
||||
style: {},
|
||||
}
|
||||
if (layer === 'top') {
|
||||
// 最新渲染的在下面
|
||||
options.style['z-index'] = (9999 - this.effectIdx)
|
||||
}
|
||||
return this.$createElement('span', options, [vNode])
|
||||
},
|
||||
},
|
||||
render(h) {
|
||||
return h('div', {
|
||||
class: ['j-vxe-reload-effect-box'],
|
||||
}, [this.renderVNode()])
|
||||
},
|
||||
}
|
||||
81
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/index.js
Normal file
81
ant-design-vue-jeecg/src/components/jeecg/JVxeTable/index.js
Normal file
@ -0,0 +1,81 @@
|
||||
import { installCell, mapCell } from './install'
|
||||
import JVxeTable from './components/JVxeTable'
|
||||
|
||||
import JVxeSlotCell from './components/cells/JVxeSlotCell'
|
||||
import JVxeNormalCell from './components/cells/JVxeNormalCell'
|
||||
import JVxeInputCell from './components/cells/JVxeInputCell'
|
||||
import JVxeDateCell from './components/cells/JVxeDateCell'
|
||||
import JVxeSelectCell from './components/cells/JVxeSelectCell'
|
||||
import JVxeCheckboxCell from './components/cells/JVxeCheckboxCell'
|
||||
import JVxeUploadCell from './components/cells/JVxeUploadCell'
|
||||
import { TagsInputCell, TagsSpanCell } from './components/cells/JVxeTagsCell'
|
||||
import JVxeProgressCell from './components/cells/JVxeProgressCell'
|
||||
import JVxeTextareaCell from './components/cells/JVxeTextareaCell'
|
||||
import JVxeDragSortCell from './components/cells/JVxeDragSortCell'
|
||||
|
||||
// 组件类型
|
||||
export const JVXETypes = {
|
||||
// 为了防止和 vxe 内置的类型冲突,所以加上一个前缀
|
||||
// 前缀是自动加的,代码中直接用就行(JVXETypes.input)
|
||||
_prefix: 'j-',
|
||||
|
||||
// 行号列
|
||||
rowNumber: 'row-number',
|
||||
// 选择列
|
||||
rowCheckbox: 'row-checkbox',
|
||||
// 单选列
|
||||
rowRadio: 'row-radio',
|
||||
// 展开列
|
||||
rowExpand: 'row-expand',
|
||||
// 上下排序
|
||||
rowDragSort: 'row-drag-sort',
|
||||
|
||||
input: 'input',
|
||||
inputNumber: 'inputNumber',
|
||||
textarea: 'textarea',
|
||||
select: 'select',
|
||||
date: 'date',
|
||||
datetime: 'datetime',
|
||||
checkbox: 'checkbox',
|
||||
upload: 'upload',
|
||||
// 下拉搜索
|
||||
selectSearch: 'select-search',
|
||||
// 下拉多选
|
||||
selectMultiple: 'select-multiple',
|
||||
// 进度条
|
||||
progress: 'progress',
|
||||
|
||||
// 拖轮Tags(暂无用)
|
||||
tags: 'tags',
|
||||
|
||||
slot: 'slot',
|
||||
normal: 'normal',
|
||||
hidden: 'hidden',
|
||||
}
|
||||
|
||||
// 注册自定义组件
|
||||
export const AllCells = {
|
||||
...mapCell(JVXETypes.normal, JVxeNormalCell),
|
||||
...mapCell(JVXETypes.input, JVxeInputCell),
|
||||
...mapCell(JVXETypes.inputNumber, JVxeInputCell),
|
||||
...mapCell(JVXETypes.checkbox, JVxeCheckboxCell),
|
||||
...mapCell(JVXETypes.select, JVxeSelectCell),
|
||||
...mapCell(JVXETypes.selectSearch, JVxeSelectCell), // 下拉搜索
|
||||
...mapCell(JVXETypes.selectMultiple, JVxeSelectCell), // 下拉多选
|
||||
...mapCell(JVXETypes.date, JVxeDateCell),
|
||||
...mapCell(JVXETypes.datetime, JVxeDateCell),
|
||||
...mapCell(JVXETypes.upload, JVxeUploadCell),
|
||||
...mapCell(JVXETypes.textarea, JVxeTextareaCell),
|
||||
|
||||
...mapCell(JVXETypes.tags, TagsInputCell, TagsSpanCell),
|
||||
...mapCell(JVXETypes.progress, JVxeProgressCell),
|
||||
|
||||
...mapCell(JVXETypes.rowDragSort, JVxeDragSortCell),
|
||||
...mapCell(JVXETypes.slot, JVxeSlotCell),
|
||||
|
||||
/* hidden 是特殊的组件,不在这里注册 */
|
||||
}
|
||||
|
||||
export { installCell, mapCell }
|
||||
|
||||
export default JVxeTable
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user