Compare commits

..

153 Commits

Author SHA1 Message Date
efd17cdd60 【I57I6O】 修复路由添加时Path无法添加问题
【I59M95】自定义JeecgCloudException异常供微服务,及starter模块使用,解决注解添加之后无法实现重复提交的拦截提示
【I58FJ5】修改quartz默认配置参数,延迟启动解决服务重启多次执行问题
【#3755】优化rabbitmq代码删除setQueueNames方法避免发送延迟
升级springcloud到2021.0.3,解决Spring Cloud Gateway SpEL表达式注入问题
升级积木报表到最新版
【issues/3666】同步钉钉用户到本地,违反数据库唯一主键规则,导致插入失败
2022-05-31 10:35:46 +08:00
63505e8f6e 代码提示:
2.切换微服务 勾选profile的SpringCloud,这个类就无法启动,启动会报错
2022-05-24 17:50:55 +08:00
bd4814581a 升级fastjson版本至1.2.83,解决fastjson低版本高危漏洞 2022-05-24 17:20:39 +08:00
c7840a7382 文档与默认配置的库名不一致
nacos服务模块指定版本号
2022-05-19 23:55:49 +08:00
c3e049cbd0 SpringCloud运行环境的配置,排除system模块jar里的yaml 2022-05-18 18:00:24 +08:00
a6ae4080cb SpringCloud运行环境,排除system模块jar中的application*.yaml配置文件
测试类默认注释掉,减少启动问题
yml格式不规范,导致启动报错
2022-05-18 16:16:36 +08:00
2e90f73da2 优化单元测试代码 2022-05-10 16:17:44 +08:00
85b26230d1 修复vue3代码生成器的一些问题 2022-05-09 23:06:01 +08:00
fa70a83391 修改vue3、vue2模板问题
代码规范化补充对应注释和修正规范写法
2022-05-07 20:48:34 +08:00
bbf85093d5 [issues/I55DJD] 启动报错,单体应用升级至V3.2.0版本 2022-05-05 18:02:19 +08:00
d712776c46 版本号错误 2022-05-03 00:10:03 +08:00
9710b8bc7d 修复代码生成器模板的一些bug(重点vue3的) 2022-04-29 14:43:06 +08:00
3909eb5610 【代码生成器】修复vue3模板引用路径和文件路径大小写不一致的问题 2022-04-27 10:57:36 +08:00
cd5fd2f4d4 【代码生成器】修复vue3模板引用路径和文件路径大小写不一致的问题 2022-04-27 10:24:30 +08:00
c8a26d73e3 模板问题修复 2022-04-25 09:28:49 +08:00
e89423c666 微服务优化 2022-04-24 17:49:34 +08:00
67d8cdbc6c 微服务优化 2022-04-24 17:49:15 +08:00
c14a793906 JeecgBoot3.2.0 版本发布 [优化微服务模块] 2022-04-24 15:51:09 +08:00
7f87042e59 JeecgBoot3.2.0 版本发布 2022-04-24 15:01:06 +08:00
4aac17a5e2 版本发布 3.2.0 2022-04-24 13:40:40 +08:00
5f1d0dafa4 vue3代码生成器功能优化,几个bug处理 2022-04-23 17:06:20 +08:00
727b67a50d 【issues/I52J6R】gateway,在网关路由页面如何设置路由条件Header
默认不启用xxljob
2022-04-22 19:12:10 +08:00
dfbbd1bd1f JeecgBoot3.2.0-beta代码发布了,重构很大!
- 改了类名大小写,git对大小写不敏感,导致提交出问题修复。
2022-04-18 11:33:59 +08:00
2d9de317c4 Delete VXESocket.java 2022-04-18 11:21:51 +08:00
56912b766d Delete IPUtils.java 2022-04-18 11:21:20 +08:00
8baced93a3 Delete MD5Util.java 2022-04-18 11:21:09 +08:00
0c545517b6 Delete VXESocketConst.java 2022-04-18 11:20:52 +08:00
a3d6e0ce08 git大小写不敏感,导致重构类名未提交成功 2022-04-18 11:10:38 +08:00
9dcff93372 3.2.0-beta,重构很大:升级springboot2.6.6、spring-cloud-alibaba 2021.1、mybatisplus3.5.1、代码规范部分重构 2022-04-18 09:37:42 +08:00
948e1668b6 【issues/I515ZE】online代码生成vue3,activeKey.value不需要ref赋值] 2022-04-16 17:16:36 +08:00
b17908b581 【issues/I515ZE】online代码生成vue3,activeKey.value不需要ref赋值] 2022-04-16 17:13:03 +08:00
3bfe099e21 开源协议 2022-04-01 09:59:39 +08:00
93e755a09c 【issues/I4ZRF3】代码生成器componentProps{}后面少一个逗号 2022-03-31 09:38:53 +08:00
6196f0a463 升级:简化微服务切换,增加profile模式 SpringCloud 2022-03-30 13:45:43 +08:00
1481b86fb3 issues/I4SKUS 分子字典树前端代码生成错误 2022-03-23 18:30:45 +08:00
eacaa7ed81 生成代码返回结果泛型支持问题处理 2022-03-22 22:51:28 +08:00
bc711932f7 小bug处理和vue3代码生成器模板完善 2022-03-18 22:38:45 +08:00
44d6f3228f GUI代码生成器适用于vue3 2022-03-18 19:54:18 +08:00
dad784228b vue3最近版本代码生成模板 2022-03-18 16:24:05 +08:00
ce02dbbd30 入门视频教程 2022-03-18 15:19:21 +08:00
d4c2c02602 jdk版本支持描述错误 2022-03-17 21:42:39 +08:00
7935ce86de 解决 3.1版本 online表单在线-数据库表格式为Datetime时,通过日期查询组件(yyyy-MM-dd格式)查询报错 #3489
解决代码生成器路径不支持含中文和空格的问题
2022-03-17 13:49:18 +08:00
1224660d1d 1 2022-03-15 11:23:41 +08:00
1b9fc77ec4 1 2022-03-15 11:05:07 +08:00
4a21f16ade 1 2022-03-15 11:01:11 +08:00
a9e6d2ec0c 简化介绍 2022-03-15 10:58:48 +08:00
e00ac210fe readme调整 2022-03-15 10:50:34 +08:00
cdc245699e Apache Shiro 1.8.0 2022-03-15 10:40:56 +08:00
9de7f261bb 升级Shiro版本,解决安全漏洞提示 #3498 2022-03-14 22:15:16 +08:00
ae434bfce4 代码生成器模板问题、docker file 3306端口冲突 2022-03-14 14:14:26 +08:00
0ee985da5a Vue3版前端(Beta版)发布 2022-03-10 11:33:08 +08:00
dbba190980 升级积木报表到最新版1.4.32,解决严重安全漏洞 2022-03-09 09:45:07 +08:00
07c538a1b2 代码提醒,减少走弯路 2022-03-02 15:29:32 +08:00
472bf3f35a JeecgBoot 3.1.0 版本发布,基于代码生成器的企业级低代码平台 2022-03-01 22:13:54 +08:00
b66fff6c42 严重安全漏洞修复
1.SQL注入检测存在绕过风险
2./upload接口存在任意文件上传漏洞
2022-02-26 22:46:52 +08:00
2be616ee49 拆分独立微服务测试模块jeecg-cloud-test,提供更多测试示例: 分布式事务、分库分表、mq、xxljob等 2022-02-26 17:43:55 +08:00
51c63e8057 Nacos服务地址配置,默认用host jeecg-boot-nacos
默认注释掉mq测试示例代码,减少入门难度
2022-02-25 22:19:41 +08:00
758c347eb0 微服务Docker镜像制作 3.1+ 2022-02-25 14:26:00 +08:00
7bdc1b06d3 解决okhttp引用了kotlin,应用启动有警告日志问题
文件大小超限提示不准确问题
2022-02-25 11:09:17 +08:00
e364b950ca 删除非必须maven插件,降低使用难度 2022-02-24 22:33:11 +08:00
3690a6e014 swagger接口返回值,显示的是object问题 2022-02-24 20:01:42 +08:00
897dcbca78 JeecgBoot 3.1.0 版本发布,基于代码生成器的企业级低代码平台 2022-02-24 17:03:59 +08:00
f8c7ddd223 JeecgBoot 3.1.0 版本发布,基于代码生成器的企业级低代码平台 2022-02-24 15:13:05 +08:00
8c143f35f8 生产环境swagger默认不关闭,需要自己设置,减少疑问 2022-02-18 11:28:23 +08:00
a5f30ec51f 开源协议补充 2022-02-16 11:24:27 +08:00
9226ae0aeb 11 2022-02-05 20:37:38 +08:00
baefc1338d 导入mysql db时报错 2022-01-11 09:44:23 +08:00
bb6ec4cc2f 微服务启动报错:Could not resolve placeholder 'jeecg.signatureSecret' in value "${jeecg.signatureSecret}" 2021-12-20 11:18:52 +08:00
94053034b4 Log4j2惊爆0Day漏洞 2021-12-13 15:45:01 +08:00
bdbc714233 Log4j2惊爆0Day漏洞,大家排查修复 2021-12-11 13:12:03 +08:00
84f3ce8340 【#3232】JS增强中获取用户名为空 2021-12-10 12:02:36 +08:00
b6219301e1 【issues/I4KTU1】点击查询不会清空列表选中行 2021-12-10 11:46:09 +08:00
a4603a2963 【issues/I4K3Z7】横板顶部栏导航,会把退出登陆按钮挤掉 2021-12-10 11:43:37 +08:00
5bfa15d628 【#3225】常见案例-自定义组件-cron表达式显示错误 2021-12-10 11:19:29 +08:00
a39bb0ce5f 修改mysql脚本执行报错问题、支持生成大写QRTZ表 2021-12-07 19:16:25 +08:00
a43ccf6f31 用户数据量较大时,功能测试报错 issues/3196 2021-12-06 09:32:13 +08:00
1e2a74a65f 用户数据量较大时,功能测试报错(通过部门选择用户,左侧部门数据改成异步加载) issues/3196 2021-12-06 00:15:27 +08:00
6c362861a0 【#3203】JSelectBizComponent columns 建议开放customRender等方法类配置] 2021-12-06 00:08:29 +08:00
2a5ec11660 【issues/I4IFWX】定时任务表达式,”星期“一栏错误] 2021-12-06 00:04:58 +08:00
509be1e382 Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D issues/3195 2021-11-22 15:13:59 +08:00
80601e0ccd 修改Dockerfile,将代码生成模板文件直接放到与jar包同级目录,确保发布后,代码生成功能直接可用,不需要做二次处理 2021-11-22 09:35:33 +08:00
435f467646 富文本框JEditor,属性设置下拉选层级显示 #3170 2021-11-18 09:37:13 +08:00
644c6801a0 富文本框JEditor,属性设置下拉选层级显示 #3170 2021-11-18 09:36:54 +08:00
1f75c124f1 在线表单的href跳转不能进行筛选 #3154
online 报表 bug #3162
2021-11-17 09:40:50 +08:00
bcbdd383ae 瘦jar打包方式下,所有 excel 文件导入失败 issues/I4CMHK 2021-11-17 09:17:38 +08:00
7a1d4cda96 自定义树控件只显示父节点,子节点无法展开 (此处还原不可再改) /issues/I4HZAL 2021-11-16 21:17:17 +08:00
8bae42049c jeecg.path.webapp配置无效 issues/3126 2021-11-16 21:16:58 +08:00
09a63610cf oracle数据库,在线表单编辑保存会报错issues/I4HW20 2021-11-16 18:08:12 +08:00
46e024bfbc 暂时删除vue3模板,因为vue3前端项目未正式发布,减少大家的误解 2021-11-08 15:25:38 +08:00
c767f094fc 我的部门下面的用户分配角色报错(无角色的情况下) 2021-11-06 19:04:20 +08:00
42e0a938da 我的部门空数据的时候报错 2021-11-06 19:02:53 +08:00
3f4b40ea74 云存储上传文件,不自定义域名出问题 2021-11-06 19:01:04 +08:00
f210df80e6 严重bug,解决部分功能操作,提示文本空问题 2021-11-06 19:00:08 +08:00
0f79e8efef 严重bug,解决【用户管理】选择部门和上级以后,负责部门没有数据可选 2021-11-06 18:59:24 +08:00
d6251419e6 严重bug,解决菜单数据规则,选择自定义SQL 规则值无法输入空格问题 2021-11-06 18:58:57 +08:00
7db5f7f3e9 1 2021-11-05 12:53:35 +08:00
cd79eb7597 开源协议补充:不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争 2021-11-05 12:40:39 +08:00
812cbce06b 在线演示(VUE3beta版) 2021-10-29 09:40:25 +08:00
3b92086047 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3到来!! 2021-10-27 14:33:50 +08:00
0fa24b8518 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 11:48:19 +08:00
7e7ea37857 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 11:33:09 +08:00
e550843fd1 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 10:55:52 +08:00
3cb20a6a43 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 10:28:39 +08:00
0acea1abff JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 10:28:15 +08:00
9760185bf6 JeecgBoot低代码平台 3.0版本发布—新里程牌开始,迎接VUE3版本到来!! 2021-10-27 10:26:33 +08:00
75be8dd5b1 online下拉搜索组件,配置字典sql带'=' 提示sign失败问题处理 2021-10-21 16:31:10 +08:00
a7893c4941 qq群满了,新增⑤860162132 2021-10-19 13:23:47 +08:00
ef97f700ab 授权首页菜单后,自定义首页功能不生效 #3069 2021-10-16 19:41:55 +08:00
6fb01abbc5 online学习视频修改 2021-10-14 16:13:02 +08:00
fe1b58ade2 还原long精度改造,导致了两个严重问题
1表单的日期字段,不能显示日期格式
2前端分页插件报错
2021-09-26 17:35:30 +08:00
1998867ca6 Long转json精度丢失的配置 未生效 2021-09-14 18:12:16 +08:00
a351204865 暂时还原代码,改的有问题 [Long转json精度丢失的配置 未生效] 2021-09-14 17:48:53 +08:00
4da1948cb0 路由网关禁用Demo配置后,系统仍可以通过网关路由到Demo服务。issues/I49457 2021-09-14 17:24:45 +08:00
55ebea88af 消息队列中报微服务异常 issues/I4977W 2021-09-14 17:08:24 +08:00
4b830b37c9 消息队列中报微服务异常 issues/I4977W 2021-09-14 16:43:14 +08:00
537cc05601 Long转json精度丢失的配置 未生效issues/I49DNI 2021-09-14 16:32:31 +08:00
8f9f27c550 同步钉钉人员到本地错误 #2990 2021-09-14 10:09:56 +08:00
57f72dd4d0 issues/I48I0E 省市三级联动列表无法显示 2021-09-11 18:45:23 +08:00
e2e19fa456 内容更新 2021-09-03 12:09:01 +08:00
98a5148e52 【issues/I471XE】 钉钉人员同步时手机号未能正确同步 2021-08-31 22:29:01 +08:00
c2ae049ad7 issues/2959 微服务版集成企业微信单点登录 2021-08-31 22:28:28 +08:00
b44acde9b5 【issues/2939】JEditable 下子表 addBefore()方法,在其中自定义调用其他方法不生效 2021-08-31 22:28:04 +08:00
db467f22a7 docker文档调整 2021-08-27 18:08:08 +08:00
53c91fc349 README.MD 里面前端目录写错了 issues/I477T1 2021-08-27 09:37:07 +08:00
92028a7e44 JVXETable提供更便捷的三级联动机制,解决联动展示与选择BUG #2867 2021-08-20 11:13:42 +08:00
8ce40fa3d4 升级2.4.6后Online表单开发无法使用“一对多”的“ERP主题” issues/I468JY 2021-08-18 20:23:53 +08:00
05c7f76484 列表点击详情,出现id,好难看 #2922 2021-08-18 16:25:10 +08:00
71a1e9a63b oline在线内嵌子表主表与附表,设置扩展参数限制宽度不起作用 #2881
online java 增强当设置的增强过多时,显示异常 #2880
文本太长时,会遮挡页面issues/I44F0R
2021-08-18 15:59:44 +08:00
0606aa560a mybatisPlus升级,拦截器排除写法修改 2021-08-17 13:48:26 +08:00
1bbca48ba8 sqlserver同步数据库报错,SQL to parse以后与sqlserver不兼容 #2915
【升级mybatisPlus导致,多租户插件SqlServer下给添加字段sql多加了一个column】
2021-08-17 13:35:30 +08:00
d0c15f2302 解决新版IE11兼容问题 2021-08-17 11:50:00 +08:00
c9ff5d51b4 解决2.4.6新版问题:js增强点击无效 #2912 2021-08-16 16:38:00 +08:00
cce5d785e4 解决 升级到2.4.6后button type='danger'时的样式 #2909 2021-08-15 10:14:25 +08:00
234022d905 修复低级bug #2906 2021-08-14 19:39:42 +08:00
ab529aaf6c 2.4.6版本发布 2021-08-13 20:36:27 +08:00
07c6d1a23d 2.4.6 版本发布 2021-08-13 20:35:59 +08:00
8f780e180e JeecgBoot 2.4.6版本发布 2021-08-13 16:55:43 +08:00
87f17b9fc5 JeecgBoot 2.4.6版本发布 2021-08-13 16:18:52 +08:00
5a93d001b4 JeecgBoot 2.4.6版本发布(gateway默认采用database中的路由配置) 2021-08-13 16:18:31 +08:00
d822552e0c JeecgBoot 2.4.6版本发布(调整nacos配置) 2021-08-13 16:18:03 +08:00
832fa30cc9 JeecgBoot 2.4.6版本发布 2021-08-13 15:28:22 +08:00
3af1b390f1 JeecgBoot 2.4.6版本发布 2021-08-13 15:27:53 +08:00
a3695151dc JeecgBoot 2.4.6版本发布 2021-08-13 15:27:32 +08:00
c269d7637f JeecgBoot 2.4.6版本发布 2021-08-13 15:26:35 +08:00
664413e5d7 JeecgBoot 2.4.6版本发布 2021-08-13 15:26:20 +08:00
986f909628 issue格式要求 2021-08-09 16:25:21 +08:00
221940cc5f issue格式要求,发issue请详细 2021-08-09 16:13:32 +08:00
2a392fb738 issue格式要求 2021-08-02 18:06:14 +08:00
2b47cd0c34 升级下说明 2021-07-28 17:28:51 +08:00
1900f3fe77 Sign 签名校验失败 #2728 2021-07-01 12:45:17 +08:00
37fe6fea69 表字典接口存在SQL注入漏洞,增加签名拦截器 自定义组件验签失败 issues/I3XNK1 2021-06-25 15:30:47 +08:00
3fbb5ee4ad 登录账号提示修改 2021-06-25 13:41:33 +08:00
b1958fd295 HW21-0499 表字典接口存在SQL注入漏洞,增加签名拦截器 2021-06-25 11:50:34 +08:00
794 changed files with 61923 additions and 235975 deletions

View File

@ -9,4 +9,9 @@
友情提示: 未按格式要求发帖,会直接删掉。
#### 友情提示为了提高issue处理效率
- 未按格式要求发帖,会被直接删掉;
- 请自己初判问题描述是否清楚,是否方便我们调查处理;
- 针对问题请说明是Online在线功能(需说明用的主题模板),还是生成的代码功能;
- 描述过于简单或模糊,导致无法处理的,会被直接删掉;

12
LICENSE
View File

@ -199,3 +199,15 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
开源协议补充
JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京地址中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱jeecgos@163.com
本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
1.允许基于本平台软件开展业务系统开发。
2.不得基于该平台软件的基础修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售或与JeecgBoot参与同类软件产品市场的竞争。
违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
解释权归http://www.jeecg.com

263
README.md
View File

@ -1,18 +1,19 @@
![JEECG](https://static.oschina.net/uploads/img/201905/24164523_XDhg.png "JeecgBoot低代码开发平台")
![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/site/jeecgfengm.png "JeecgBoot低代码开发平台")
JEECG BOOT 低代码开发平台(前后端分离版本)
===============
当前最新版本: 2.4.5发布日期2021-06-07
当前最新版本: 3.2.0发布日期2022-04-25
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/version-2.4.5-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-3.2.0-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -44,30 +45,25 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
- 技术官网: [http://www.jeecg.com](http://www.jeecg.com)
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
- 微服务启动: [单体升级为微服务启动文档2.4+](http://doc.jeecg.com/2043906)
- 在线演示 [http://boot.jeecg.com](http://boot.jeecg.com)
- 视频教程 [JeecgBoot入门视频](http://www.jeecg.com/doc/video)
- 开发文档: [http://doc.jeecg.com](http://doc.jeecg.com)
- 常见问题 [入门常见问题Q&A](http://jeecg.com/doc/qa)
- 入门视频 [https://space.bilibili.com/454617261/channel/series](https://space.bilibili.com/454617261/channel/series)
- 更新日志: [版本日志](http://www.jeecg.com/doc/log)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [常见问题 ](http://www.jeecg.com/doc/qa) | [技术支持](http://jeecg.com/doc/help) | [1分钟体验低代码](https://my.oschina.net/jeecg/blog/3083313)
- 微服务开发: [单体切换为微服务](http://doc.jeecg.com/2704725)
- QQ交流群 ⑤860162132、683903138(VUE3版)、~~④774126647(满)、③816531124(满)、②769925425(满)、①284271917(满)~~
交流互动
Vue3版前端Beta版
-----------------------------------
- QQ交流群 ④774126647、③816531124、②769925425、①284271917
> 采用Vue3.0、Vite、 Ant-Design-Vue、TypeScript 等新技术方案包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能 是在 Vben-Admin 基础上研发的适合于JeecgBoot的新版前端VUE3框架。
- 反馈问题: [反馈问题请按格式发Issues](https://github.com/zhangdaiscott/jeecg-boot/issues/new)
- 参与开源: [欢迎加入JEECG开源团队共同进步](http://www.jeecg.com/doc/join)
- Online一分钟 [1分钟快速学习](https://jeecg.blog.csdn.net/article/details/106078912)
- 源码下载:https://github.com/jeecgboot/jeecgboot-vue3
- 入门指南: [开发文档](http://vue3.jeecg.com/2398845) | [ VUE3版演示 ](http://boot3.jeecg.com) | [入门视频](https://www.bilibili.com/video/BV1V34y187Y9)
为什么选择JEECG-BOOT?
@ -121,37 +117,38 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
-----------------------------------
#### 开发环境
- 语言Java 8
- 语言Java 8+ (小于17)
- IDE(JAVA) IDEA / Eclipse安装lombok插件
- IDE(JAVA) IDEA (必须安装lombok插件 )
- IDE(前端) WebStorm 或者 IDEA
- IDE(前端) IDEA 或者 WebStorm
- 依赖管理Maven
- 数据库MySQL5.7+ & Oracle 11g & Sqlserver2017
- 缓存Redis
- 数据库脚本MySQL5.7+ & Oracle 11g & Sqlserver2017默认只提供三个库脚本其他库需要自己转
#### 后端
- 基础框架Spring Boot 2.3.5.RELEASE
- 微服务框架: Spring Cloud Alibaba 2.2.3.RELEASE
- 基础框架Spring Boot 2.6.6
- 持久层框架Mybatis-plus 3.4.1
- 微服务框架: Spring Cloud Alibaba 2021.1
- 安全框架Apache Shiro 1.7.0Jwt 3.11.0
- 持久层框架MybatisPlus 3.5.1
- 报表工具: JimuReport 1.5.0-beta
- 安全框架Apache Shiro 1.8.0Jwt 3.11.0
- 微服务技术栈Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
- 数据库连接池阿里巴巴Druid 1.1.22
- 缓存框架redis
- 日志打印logback
- 其他fastjsonpoiSwagger-uiquartz, lombok简化代码等。
- 其他:autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
#### 前端
@ -164,9 +161,68 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
- eslint[@vue/cli 3.2.1](https://cli.vuejs.org/zh/guide)
- vue-print-nb - 打印
- vue-print-nb-jeecg - 打印
#### 支持库
| 数据库 | 支持 |
| --- | --- |
| MySQL | √ |
| Oracle11g | √ |
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| DB2、Informix | √ |
| MariaDB | √ |
| SQLite、Hsqldb、Derby、H2 | √ |
| 达梦、人大金仓、神通 | √ |
| 华为高斯、虚谷、瀚高数据库 | √ |
| 阿里云PolarDB、PPAS、HerdDB | √ |
| Hive、HBase、CouchBase | √ |
## 微服务解决方案
1、服务注册和发现 Nacos √
2、统一配置中心 Nacos √
3、路由网关 gateway(三种加载方式) √
4、分布式 http feign √
5、熔断降级限流 Sentinel √
6、分布式文件 Minio、阿里OSS √
7、统一权限控制 JWT + Shiro √
8、服务监控 SpringBootAdmin√
9、链路跟踪 Skywalking [参考文档](https://www.kancloud.cn/zhangdaiscott/jeecgcloud/1771670)
10、消息中间件 RabbitMQ √
11、分布式任务 xxl-job √
12、分布式事务 Seata
13、分布式日志 elk + kafka
14、支持 docker-compose、k8s、jenkins
15、CAS 单点登录 √
16、路由限流 √
#### 微服务架构图
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot-weifuwu-cloud.png "在这里输入图片标题")
### Jeecg Boot 产品功能蓝图
![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")
@ -304,105 +360,14 @@ Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─。。。
└─其他模块
│─OA办公组件 (暂不开源)
│ ├─更多功能
│ └─。。。
└─其他模块 (暂不开源)
└─更多功能开发中。。
```
## 微服务整体解决方案(2.4+版本)
1、服务注册和发现 Nacos √
2、统一配置中心 Nacos √
3、路由网关 gateway(三种加载方式) √
4、分布式 http feign √
5、熔断和降级 Sentinel √
6、分布式文件 Minio、阿里OSS √
7、统一权限控制 JWT + Shiro √
8、服务监控 SpringBootAdmin√
9、链路跟踪 Skywalking [参考文档](https://www.kancloud.cn/zhangdaiscott/jeecgcloud/1771670)
10、消息中间件 RabbitMQ √
11、分布式任务 xxl-job √
12、分布式事务 Seata
13、分布式日志 elk + kafka
14、支持 docker-compose、k8s、jenkins
15、CAS 单点登录 √
16、路由限流 √
#### 微服务架构图
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot-weifuwu-cloud.png "在这里输入图片标题")
### Jeecg Boot 产品功能蓝图
![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")
后台开发环境和依赖
----
- java
- maven
- jdk8
- mysql
- redis
- 数据库脚本jeecg-boot/db/jeecgboot-mysql-5.7.sql
- 默认登录账号: admin/123456
前端开发环境和依赖
----
- node
- yarn
- webpack
- eslint
- @vue/cli 3.2.1
- [ant-design-vue](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation) - antv/g2 封装实现
- [jeecg-boot-angular 版本](https://gitee.com/dangzhenghui/jeecg-boot)
项目下载和运行
----
- 拉取项目代码
```bash
git clone https://github.com/zhangdaiscott/jeecg-boot.git
cd jeecg-boot/ant-design-jeecg-vue
```
1. 安装node.js
2. 切换到ant-design-jeecg-vue文件夹下
```
# 安装yarn
npm install -g yarn
# 下载依赖
yarn install
# 启动
yarn run serve
# 编译项目
yarn run build
# Lints and fixes files
yarn run lint
```
@ -452,54 +417,6 @@ yarn run lint
![](https://oscimg.oschina.net/oscnet/59c23b230f52384e588ee16309b44fa20de.jpg)
其他说明
----
- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请更新您的 cli
- 关闭 Eslint (不推荐) 移除 `package.json``eslintConfig` 整个节点代码
- 修改 Ant Design 配色,在文件 `vue.config.js` 中,其他 less 变量覆盖参考 [ant design](https://ant.design/docs/react/customize-theme-cn) 官方说明
```ecmascript 6
css: {
loaderOptions: {
less: {
modifyVars: {
/* less 变量覆盖,用于自定义 ant design 主题 */
'primary-color': '#F5222D',
'link-color': '#F5222D',
'border-radius-base': '4px',
},
javascriptEnabled: true,
}
}
}
```
附属文档
----
- [Ant Design Vue](https://www.antdv.com/docs/vue/introduce-cn)
- [报表 viser-vue](https://viserjs.github.io/demo.html#/viser/line/basic-line)
- [Vue](https://cn.vuejs.org/v2/guide)
- [路由/菜单说明](https://gitee.com/jeecg/jeecg-boot/tree/v1.1/ant-design-jeecg-vue/src/router/README.md)
- [ANTD 默认配置项](https://gitee.com/jeecg/jeecg-boot/blob/v1.1/ant-design-jeecg-vue/src/defaultSettings.js)
- 其他待补充...
备注
----
> @vue/cli 升级后eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules
## 捐赠
如果觉得还不错,请作者喝杯咖啡吧 ☺

View File

@ -1,3 +1,6 @@
NODE_ENV=production
VUE_APP_PLATFORM_NAME=JeecgBoot 企业级低代码平台
# 开启单点登录
VUE_APP_SSO=false
# 开启微应用模式
VUE_APP_QIANKUN=false

View File

@ -2,3 +2,6 @@ NODE_ENV=development
VUE_APP_API_BASE_URL=http://localhost:8080/jeecg-boot
VUE_APP_CAS_BASE_URL=http://cas.example.org:8443/cas
VUE_APP_ONLINE_BASE_URL=http://fileview.jeecg.com/onlinePreview
# 微应用列表必须VUE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
VUE_APP_SUB_jeecg-app-1 = '//localhost:8092'

View File

@ -1,21 +1,213 @@
MIT License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Copyright (c) 2019 DaiHao Zhang
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
1. Definitions.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
开源协议补充
JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京地址中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱jeecgos@163.com
本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
1.允许基于本平台软件开展业务系统开发。
2.不得基于该平台软件的基础修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售或与JeecgBoot参与同类软件产品市场的竞争。
违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
解释权归http://www.jeecg.com
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,7 +1,7 @@
Ant Design Jeecg Vue
====
当前最新版本: 2.4.5发布日期20210607
当前最新版本: 3.1.0发布日期20220301
Overview
----
@ -33,7 +33,7 @@ Jeecg-boot 的前端UI框架采用前后端分离方案提供强大代码
- 拉取项目代码
```bash
git clone https://github.com/zhangdaiscott/jeecg-boot.git
cd jeecg-boot/ant-design-jeecg-vue
cd jeecg-boot/ant-design-vue-jeecg
```
- 安装依赖
@ -93,9 +93,9 @@ yarn run lint
- [Vue](https://cn.vuejs.org/v2/guide)
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/router/README.md)
- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/router/README.md)
- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-jeecg-vue/src/defaultSettings.js)
- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/defaultSettings.js)
- 其他待补充...

View File

@ -1,6 +1,6 @@
{
"name": "vue-antd-jeecg",
"version": "2.4.5",
"version": "3.1.0",
"private": true,
"scripts": {
"pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
@ -11,7 +11,7 @@
},
"dependencies": {
"ant-design-vue": "^1.7.2",
"@jeecg/antd-online-mini": "2.4.5-RC",
"@jeecg/antd-online-mini": "3.1.0-beta",
"@antv/data-set": "^0.11.4",
"viser-vue": "^2.4.8",
"axios": "^0.18.0",
@ -35,16 +35,17 @@
"vue-splitpane": "^1.0.4",
"vuedraggable": "^2.20.0",
"codemirror": "^5.46.0",
"@tinymce/tinymce-vue": "^2.1.0",
"tinymce": "^5.3.2",
"@tinymce/tinymce-vue": "2.1.0",
"tinymce": "5.4.1",
"@toast-ui/editor": "^2.1.2",
"vue-area-linkage": "^5.1.0",
"area-data": "^5.0.6",
"china-area-data": "^5.0.1",
"dom-align": "1.12.0",
"xe-utils": "2.4.8",
"vxe-table": "2.9.13",
"vxe-table-plugin-antd": "1.8.10",
"cron-parser": "^2.10.0"
"cron-parser": "^2.10.0",
"qiankun": "^2.5.1"
},
"devDependencies": {
"@babel/polyfill": "^7.2.5",

View File

@ -240,9 +240,7 @@
/* 滚动条优化 end */
</style>
<!-- 全局配置 -->
<script>
window._CONFIG = {};
</script>
<script src="<%= BASE_URL %>static/config.js"></script>
</head>
<body>

View File

@ -0,0 +1,11 @@
/**
* 存放配置常量(当值不为空时会覆盖env配置)
*/
window._CONFIG = {
//接口父路径
VUE_APP_API_BASE_URL: '',
//单点登录地址
VUE_APP_CAS_BASE_URL: '',
//文件预览路径
VUE_APP_ONLINE_BASE_URL: ''
}

View File

@ -0,0 +1,33 @@
@active-color: #11da75;
ul {
max-height: 700px;
overflow-y: auto;
padding-left: .5rem;
img {
width:64px;
height:64px;
padding: .2rem;
margin: .3rem;
cursor: pointer;
&.active, &:hover {
border: 1px solid @active-color;
border-radius: 2px;
color: #fff;
transition: all .3s;
}
}
li {
list-style: none;
float: left;
text-align: center;
cursor: pointer;
color: #555;
transition: color .3s ease-in-out,background-color .3s ease-in-out;
position: relative;
margin: 3px 0;
border-radius: 4px;
background-color: #fff;
overflow: hidden;
padding: 10px 0 0;
}
}

View File

@ -12,9 +12,9 @@
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<a-tooltip v-else :title="file.name">
<a-icon type="paper-clip" style="color:red;"/>
<span style="color:red;margin-left:5px">{{ ellipsisFileName }}</span>
<a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/>
<span style="margin-left:5px">{{ ellipsisFileName }}</span>
</a-tooltip>
<template style="width: 30px">
@ -179,8 +179,19 @@
value['responseName'] = file.response[this.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
}
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}

View File

@ -10,20 +10,9 @@
<template v-else-if="file['path']">
<img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
</template>
<template v-else>
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError"/>
</template>
<template slot="addonBefore" style="width: 30px">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
</template>
<a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
<template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
@ -196,8 +185,19 @@
value['responseName'] = file.response[this.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[this.responseName]
this.handleChangeCommon(value)
}
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}

View File

@ -1,5 +1,4 @@
import { pcaa } from 'area-data'
import Vue from 'vue'
/**
* 省市区
*/
@ -8,7 +7,10 @@ export default class Area {
* 构造器
* @param express
*/
constructor() {
constructor(pcaa) {
if(!pcaa){
pcaa = Vue.prototype.$Jpcaa;
}
let arr = []
const province = pcaa['86']
Object.keys(province).map(key=>{
@ -17,9 +19,11 @@ export default class Area {
Object.keys(city).map(key2=>{
arr.push({id:key2, text:city[key2], pid:key, index:2});
const qu = pcaa[key2];
Object.keys(qu).map(key3=>{
arr.push({id:key3, text:qu[key3], pid:key2, index:3});
})
if(qu){
Object.keys(qu).map(key3=>{
arr.push({id:key3, text:qu[key3], pid:key2, index:3});
})
}
})
})
this.all = arr;

View File

@ -33,3 +33,14 @@ export const cutStrByFullLength = (str = '', maxLength) => {
return pre
}, '')
}
// 下划线转换驼峰
export function underLinetoHump(name) {
return name.replace(/\_(\w)/g, function(all, letter){
return letter.toUpperCase();
});
}
// 驼峰转换下划线
export function humptoUnderLine(name) {
return name.replace(/([A-Z])/g,"_$1").toLowerCase();
}

View File

@ -90,6 +90,8 @@
}
console.log(val);
this.$emit('change', val);
//LOWCOD-2146 【菜单】数据规则选择自定义SQL 规则值无法输入空格
this.$emit('input', val);
},
setCurrentDictOptions(dictOptions){
this.dictOptions = dictOptions

View File

@ -48,7 +48,6 @@
props:{
disabled: Boolean,
value: [String, Number],
dict: String,
dictOptions: Array,
async: Boolean,
placeholder:{
@ -56,6 +55,11 @@
default:"请选择",
required:false
},
dict:{
type: String,
default: '',
required: false
},
popContainer:{
type:String,
default:'',
@ -119,7 +123,17 @@
if(this.async){
if(!this.selectedAsyncValue || !this.selectedAsyncValue.key || this.selectedAsyncValue.key!=this.value){
console.log("这才请求后台")
getAction(`/sys/dict/loadDictItem/${this.dict}`,{key:this.value}).then(res=>{
//update-begin-author:taoyan date:20220112 for: 方法initSelectValue 根据下拉框实际值查询下拉框的显示的文本 因后台接口只处理3个参数所以将过滤条件去掉
// TODO 隐患 查询效率问题 还是应该在后台作筛选
let itemDictStr = this.dict
let arr = itemDictStr.split(',')
if(arr && arr.length==4){
// 删除最后一个元素
arr.pop();
itemDictStr = arr.join(',')
}
//update-end-author:taoyan date:20220112 for: 方法initSelectValue 根据下拉框实际值查询下拉框的显示的文本 因后台接口只处理3个参数所以将过滤条件去掉
getAction(`/sys/dict/loadDictItem/${itemDictStr}`,{key:this.value}).then(res=>{
if(res.success){
let obj = {
key:this.value,
@ -186,16 +200,20 @@
}
}
}else{
//异步一开始也加载一点数据
this.loading=true
getAction(`/sys/dict/loadDict/${this.dict}`,{pageSize: this.pageSize, keyword:''}).then(res=>{
this.loading=false
if(res.success){
this.options = res.result
}else{
this.$message.warning(res.message)
}
})
if(!this.dict){
console.error('搜索组件未配置字典项')
}else{
//异步一开始也加载一点数据
this.loading=true
getAction(`/sys/dict/loadDict/${this.dict}`,{pageSize: this.pageSize, keyword:''}).then(res=>{
this.loading=false
if(res.success){
this.options = res.result
}else{
this.$message.warning(res.message)
}
})
}
}
},
filterOption(input, option) {

View File

@ -29,7 +29,6 @@
</template>
<script>
import { pcaa } from 'area-data'
import Area from '@/components/_util/Area'
export default {
@ -53,7 +52,7 @@
},
data() {
return {
pcaa,
pcaa: this.$Jpcaa,
innerValue: [],
usedListeners: ['change'],
enums: {
@ -114,7 +113,7 @@
/** 通过地区code获取子级 */
loadDataByCode(value) {
let options = []
let data = pcaa[value]
let data = this.pcaa[value]
if (data) {
for (let key in data) {
if (data.hasOwnProperty(key)) {
@ -139,7 +138,7 @@
},
initAreaData(){
if(!this.areaData){
this.areaData = new Area();
this.areaData = new Area(this.$Jpcaa);
}
},

View File

@ -400,6 +400,10 @@
.null-tip-hidden{
display: none;
}
/**选中样式偶然出现高度不够的情况*/
.CodeMirror-selected{
min-height: 19px !important;
}
}
/* 全屏样式 */

View File

@ -8,7 +8,7 @@
:showTime="showTime"
:format="dateFormat"
:getCalendarContainer="getCalendarContainer"
/>
v-bind="$attrs"/>
</template>
<script>
import moment from 'moment'

View File

@ -144,8 +144,12 @@ export default {
const v = this.cronValue_c
if (this.hideYear || this.hideSecond) return v
const vs = v.split(' ')
if (vs.length >= 6) {
// 将 Quartz 星期 的规则转换为 CronParser 的规则
vs[5] = this.convertQuartzWeekToCParser(vs[5])
}
return vs.slice(0, vs.length - 1).join(' ')
}
},
},
watch: {
cronValue(newVal, oldVal) {
@ -158,7 +162,49 @@ export default {
cronValue_c(newVal, oldVal) {
this.calTriggerList()
this.$emit('change', newVal)
this.assignInput()
},
minute() {
if (this.second === '*') {
this.second = '0'
}
},
hour() {
if (this.minute === '*') {
this.minute = '0'
}
},
day(day) {
if (day !== '?' && this.hour === '*') {
this.hour = '0'
}
},
week(week) {
if (week !== '?' && this.hour === '*') {
this.hour = '0'
}
},
month() {
if (this.day === '?' && this.week === '*') {
this.week = '1'
} else if (this.week === '?' && this.day === '*') {
this.day = '1'
}
},
year() {
if (this.month === '*') {
this.month = '1'
}
},
},
created() {
this.formatValue()
this.$nextTick(() => {
this.calTriggerListInner()
})
},
methods: {
assignInput() {
Object.assign(this.inputValues, {
second: this.second,
minute: this.minute,
@ -169,15 +215,7 @@ export default {
year: this.year,
cron: this.cronValue_c,
})
}
},
created() {
this.formatValue()
this.$nextTick(() => {
this.calTriggerListInner()
})
},
methods: {
},
formatValue() {
if (!this.cronValue) return
const values = this.cronValue.split(' ').filter(item => !!item)
@ -190,6 +228,39 @@ export default {
if (values.length > i) this.month = values[i++]
if (values.length > i) this.week = values[i++]
if (values.length > i) this.year = values[i]
this.assignInput()
},
// 将 Quartz 星期 的规则转换为 CronParser 的规则:
// Quartz 的规则1 = 周日2 = 周一3 = 周二4 = 周三5 = 周四6 = 周五7 = 周六
// CronParser 的规则: 0 = 周日1 = 周一2 = 周二3 = 周三4 = 周四5 = 周五6 = 周六7 = 周日
convertQuartzWeekToCParser(week) {
let convert = (v) => {
if (v === '0') {
return '1'
}
if (v === '1') {
return '0'
}
return (Number.parseInt(v) - 1).toString()
}
// 匹配示例 1-7 or 1/7
let patten1 = /^([0-7])([-/])([0-7])$/
// 匹配示例 1,4,7
let patten2 = /^([0-7])(,[0-7])+$/
if (/^[0-7]$/.test(week)) {
return convert(week)
} else if (patten1.test(week)) {
return week.replace(patten1, ($0, before, separator, after) => {
if (separator === '/') {
return convert(before) + separator + after
} else {
return convert(before) + separator + convert(after)
}
})
} else if (patten2.test(week)) {
return week.split(',').map(v => convert(v)).join(',')
}
return week
},
calTriggerList: simpleDebounce(function () {
this.calTriggerListInner()

View File

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

View File

@ -38,8 +38,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="i of specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template>
</a-checkbox-group>
</div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template>
</a-checkbox-group>
</div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template>
</a-checkbox-group>
</div>

View File

@ -89,6 +89,9 @@ export default {
result.push('L')
break
case TYPE_SPECIFY:
if (this.valueList.length === 0) {
this.valueList.push(this.minValue)
}
result.push(this.valueList.join(','))
break
default:
@ -96,7 +99,15 @@ export default {
break
}
return result.length > 0 ? result.join('') : this.DEFAULT_VALUE
}
},
// 指定值范围区间,介于最小值和最大值之间
specifyRange() {
let range = []
for (let i = this.minValue; i <= this.maxValue; i++) {
range.push(i)
}
return range
},
},
methods: {
parseProp (value) {

View File

@ -29,6 +29,13 @@
width: 4em;
}
.week {
.list-check-item {
width: 5em;
text-align: left;
}
}
.tip-info {
color: #999
}

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="i of specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template>
</a-checkbox-group>
</div>

View File

@ -25,8 +25,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disabled">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="i in specifyRange">
<a-checkbox class="list-check-item" :key="`key-${i}`" :value="i" :disabled="type!==TYPE_SPECIFY || disabled">{{i}}</a-checkbox>
</template>
</a-checkbox-group>
</div>

View File

@ -1,5 +1,5 @@
<template>
<div class="config-list">
<div class="config-list week">
<a-radio-group v-model="type">
<div class="item">
<a-radio value="TYPE_NOT_SET" class="choice" :disabled="disableChoice">不设置</a-radio>
@ -36,8 +36,8 @@
<a-radio value="TYPE_SPECIFY" class="choice" :disabled="disableChoice">指定</a-radio>
<div class="list">
<a-checkbox-group v-model="valueList">
<template v-for="i in maxValue+1">
<a-checkbox class="list-check-item" :key="`key-${i-1}`" :value="i-1" :disabled="type!==TYPE_SPECIFY || disabled">{{i-1}}</a-checkbox>
<template v-for="(v,k) in WEEK_MAP">
<a-checkbox class="list-check-item" :key="`key-${v}`" :value="v" :disabled="type!==TYPE_SPECIFY || disabled">{{k}}</a-checkbox>
</template>
</a-checkbox-group>
</div>
@ -51,13 +51,14 @@ import mixin from './mixin'
import { replaceWeekName, WEEK_MAP_EN } from './const.js'
const WEEK_MAP = {
'周': 0,
'周': 1,
'周': 2,
'周': 3,
'周': 4,
'周': 5,
'周六': 6
'周': 2,
'周': 3,
'周': 4,
'周': 5,
'周': 6,
'周': 7,
// 按照国人习惯,将周日放到每周的最后一天
'周日': 1,
}
export default {
@ -101,10 +102,10 @@ export default {
created() {
this.DEFAULT_VALUE = '*'
// 0,7表示周日 1表示周一
this.minValue = 0
this.maxValue = 6
this.valueRange.start = 0
this.valueRange.end = 6
this.minValue = 1
this.maxValue = 7
this.valueRange.start = 1
this.valueRange.end = 7
this.valueLoop.start = 2
this.valueLoop.interval = 1
this.parseProp(this.prop)

View File

@ -1,5 +1,5 @@
<!-- JEditableTable -->
<!-- @version 1.6.1 -->
<!-- @version 1.6.2 -->
<!-- @author sjlei -->
<template>
<a-spin :spinning="loading">
@ -11,7 +11,33 @@
<a-col>
<!-- 操作按钮 -->
<div v-if="actionButton" class="action-button">
<a-button v-if="buttonPermission('add')" type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button>
<a-button-group v-if="buttonPermission('add')">
<a-button type="primary" icon="plus" @click="handleClickAdd" :disabled="disabled">新增</a-button>
<a-popover v-if="addButtonSettings" placement="right" overlayClassName="j-add-btn-settings">
<a-row slot="title">
<a-col :span="12">选项</a-col>
<a-col :span="12" style="text-align: right;">
<a-tooltip title="保存为默认值">
<a-button type="link" icon="save" size="small" style="position: relative;left:4px;" @click="onAddButtonSettingsSave"/>
</a-tooltip>
</a-col>
</a-row>
<template slot="content">
<a-form-model layout="horizontal" :labelCol="{span:8}" :wrapperCol="{span:16}">
<a-form-model-item label="添加行数">
<a-input-number v-model="settings.addRowNum" :min="1"/>
</a-form-model-item>
<a-form-model-item label="添加位置">
<a-input-number v-model="settings.addIndex" :min="0" :max="rows.length"/>
<p style="font-size: 12px;color:#aaa;line-height: 14px;text-align: right;margin: 0;">0 = 最底部</p>
</a-form-model-item>
<a-divider style="margin: 8px 0;"/>
<a-checkbox v-model="settings.addScrollToBottom">添加后滚动到底部</a-checkbox>
</a-form-model>
</template>
<a-button icon="setting" type="primary"></a-button>
</a-popover>
</a-button-group>
<span class="gap"></span>
<template v-if="selectedRowIds.length>0">
<a-popconfirm
@ -215,7 +241,7 @@
:value="departCompValues[id]"
:placeholder="replaceProps(col, col.placeholder)"
:trigger-change="true"
:multi="true"
:multi="isMultipleSelect(col)"
@change="(v)=>handleChangeDepartCommon(v,id,row,col)"
/>
<span
@ -239,7 +265,7 @@
:value="userCompValues[id]"
:placeholder="replaceProps(col, col.placeholder)"
:trigger-change="true"
:multi="true"
:multi="isMultipleSelect(col)"
@change="(v)=>handleChangeUserCommon(v,id,row,col)"
/>
<span
@ -277,8 +303,33 @@
>{{ jdateValues[id] }}</span>
</a-tooltip>
</template>
<!-- time -->
<template v-else-if="col.type === formTypes.time">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<j-time
v-if="isEditRow(row, col)"
:id="id"
:key="i"
v-bind="buildProps(row,col)"
style="width: 100%;"
:value="jdateValues[id]"
:getCalendarContainer="getParentContainer"
:placeholder="replaceProps(col, col.placeholder)"
allowClear
@change="(v)=>handleChangeJDateCommon(v,id,row,col)"
/>
<span
v-else
class="j-td-span no-edit"
:class="{disabled: buildProps(row,col).disabled}"
@click="handleEditRow(row, col)"
>{{ jdateValues[id] }}</span>
</a-tooltip>
</template>
<!-- input_pop -->
<template v-else-if="col.type === formTypes.input_pop">
<template v-else-if="col.type === formTypes.input_pop||col.type === 'textarea'">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<j-input-pop
v-if="isEditRow(row, col)"
@ -318,7 +369,7 @@
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
</template>
@ -397,7 +448,7 @@
<!-- update-beign-author:taoyan date:0827 for文件/图片逻辑新增 -->
<div v-else-if="col.type === formTypes.file" :key="i">
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<div :key="fileKey" style="position: relative;">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading" style="color:red;"/>
@ -409,9 +460,9 @@
<span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
</a-tooltip>
<a-tooltip v-else :title="file.name">
<a-icon type="paper-clip" style="color:red;"/>
<span style="color:red;margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
<a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/>
<span style="margin-left:5px">{{ getEllipsisWord(file.name,5) }}</span>
</a-tooltip>
<template style="width: 30px">
@ -436,7 +487,7 @@
</div>
</template>
<div :hidden="uploadValues[id] != null">
<div :hidden="hasUploadValue(id)">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<a-upload
name="file"
@ -456,7 +507,7 @@
</div>
<div v-else-if="col.type === formTypes.image" :key="i">
<template v-if="uploadValues[id] != null" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<template v-if="hasUploadValue(id)" v-for="(file,fileKey) of [(uploadValues[id]||{})]">
<div :key="fileKey" style="position: relative;">
<template v-if="!uploadValues[id] || !(uploadValues[id]['url'] || uploadValues[id]['path'] || uploadValues[id]['message'])">
<a-icon type="loading"/>
@ -464,20 +515,9 @@
<template v-else-if="uploadValues[id]['path']">
<img class="j-editable-image" :src="getCellImageView(id)" alt="无图片" @click="handleMoreOperation(id,'img',col)"/>
</template>
<template v-else>
<a-icon type="exclamation-circle" style="color: red;" @click="handleClickShowImageError(id)"/>
</template>
<template slot="addonBefore" style="width: 30px">
<a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
<a-icon type="loading"/>
</a-tooltip>
<a-tooltip v-else-if="file.status==='done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
</template>
<a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError(id)">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
<template style="width: 30px">
<a-dropdown :trigger="['click']" placement="bottomRight" :getPopupContainer="getParentContainer" style="margin-left: 10px;">
@ -505,7 +545,7 @@
</div>
</template>
<div :hidden="uploadValues[id] != null">
<div :hidden="hasUploadValue(id)">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<a-upload
name="file"
@ -626,6 +666,7 @@
<div v-else-if="col.type === formTypes.slot" :key="i">
<a-tooltip v-bind="buildTooltipProps(row, col, id)">
<!-- updatesunjianlei date2022-1-17 forbuildProps新增参数 -->
<slot
:name="(col.slot || col.slotName) || col.key"
:index="rowIndex"
@ -639,6 +680,7 @@
:target="getVM()"
:handleChange="(v)=>handleChangeSlotCommon(v,id,row,col)"
:isNotPass="notPassedIds.includes(col.key+row.id)"
:buildProps="()=>buildProps(row,col)"
/>
</a-tooltip>
</div>
@ -738,6 +780,11 @@
type: Boolean,
default: false
},
// 是否显示添加按钮选项
addButtonSettings: {
type: Boolean,
default: false
},
// 是否显示行号
rowNumber: {
type: Boolean,
@ -866,7 +913,16 @@
lastPushTimeMap: new Map(),
number:0,
//不显示的按钮编码
excludeCode:[]
excludeCode:[],
// 选项配置
settings: {
// 添加行数
addRowNum: 1,
// 添加位置下标0 = 最底部
addIndex: 0,
// 添加后滚动到底部
addScrollToBottom: false,
},
}
},
created() {
@ -881,6 +937,7 @@
event.stopPropagation()
}
}
this.getSavedAddButtonSettings()
},
// 计算属性
computed: {
@ -1033,7 +1090,11 @@
},
methods: {
// 判断文件/图片是否存在
hasUploadValue(id){
let flag = this.uploadValues[id] != null && this.uploadValues[id].toString().length>0
return flag;
},
getElement(id, noCaseId = false) {
if (!this.el[id]) {
this.el[id] = document.getElementById((noCaseId ? '' : this.caseId) + id)
@ -1244,7 +1305,7 @@
selectValues[inputId] = undefined
}
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
jdateValues[inputId] = sourceValue
} else if (column.type === FormTypes.slot) {
@ -1256,7 +1317,7 @@
departCompValues[inputId] = sourceValue
} else if (column.type === FormTypes.sel_user) {
userCompValues[inputId] = sourceValue
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
jInputPopValues[inputId] = sourceValue
} else if (column.type === FormTypes.radio) {
radioValues[inputId] = sourceValue
@ -1412,22 +1473,18 @@
let tbody = this.getElement('tbody')
let offsetHeight = tbody.offsetHeight
let realScrollTop = tbody.scrollTop + offsetHeight
if (forceScrollToBottom === false) {
// 只有滚动条在底部的时候才自动滚动
if (!((tbody.scrollHeight - realScrollTop) <= 10)) {
return
}
if (forceScrollToBottom) {
this.$nextTick(() => {
this.resetScrollTop(this.$refs.scrollView.scrollHeight)
})
}
this.$nextTick(() => {
tbody.scrollTop = tbody.scrollHeight
})
},
/**
* 在指定位置添加一行
* @param insertIndex 添加位置下标
* @param num 添加的行数默认1
*/
insert(insertIndex, num = 1) {
insert(insertIndex, num = 1, forceScrollToBottom = false) {
if (this.checkTooFastClick('insert', 1500)) {
return
}
@ -1455,6 +1512,12 @@
num, insertIndex,
target: this
})
// 设置滚动条位置
if (forceScrollToBottom) {
this.$nextTick(() => {
this.resetScrollTop(this.$refs.scrollView.scrollHeight)
})
}
},
/** 删除被选中的行 */
removeSelectedRows() {
@ -1556,7 +1619,7 @@
value[column.key] = selected
}
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
value[column.key] = this.jdateValues[inputId]
} else if (column.type === FormTypes.sel_depart) {
@ -1565,7 +1628,7 @@
} else if (column.type === FormTypes.sel_user) {
value[column.key] = this.userCompValues[inputId]
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
value[column.key] = this.jInputPopValues[inputId]
} else if (column.type === FormTypes.upload) {
@ -1761,13 +1824,13 @@
}
this.$set(this.checkboxValues, key, sourceValue)
edited = true
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime) {
} else if (column.type === FormTypes.date || column.type === FormTypes.datetime || column.type === FormTypes.time) {
edited = this.setOneValue(this.jdateValues, modelKey, newValue)
} else if (column.type === FormTypes.sel_depart) {
edited = this.setOneValue(this.departCompValues, modelKey, newValue)
} else if (column.type === FormTypes.sel_user) {
edited = this.setOneValue(this.userCompValues, modelKey, newValue)
} else if (column.type === FormTypes.input_pop) {
} else if (column.type === FormTypes.input_pop || column.type === 'textarea') {
edited = this.setOneValue(this.jInputPopValues, modelKey, newValue)
} else if (column.type === FormTypes.slot) {
edited = this.setOneValue(this.slotValues, modelKey, newValue)
@ -1788,7 +1851,9 @@
}
}
if (edited) {
this.elemValueChange(column.type, {[newValueKey]: newValue}, column, newValue)
// update-begin-author:sunjianlei date:20211222 for: 修复 setValues 触发的 valueChange 事件没有id的问题
this.elemValueChange(column.type, {id: rowKey}, column, newValue)
// update-end-author:sunjianlei date:20211222 for: 修复 setValues 触发的 valueChange 事件没有id的问题
}
}
}
@ -1951,7 +2016,7 @@
{ title: '网址', value: 'url', pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/ },
{ title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/ },
{ title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/ },
{ title: '邮政编码', value: 'p', pattern: /^[1-9]\d{5}$/ },
{ title: '邮政编码', value: 'p', pattern: /^[0-9]{6}$/ },
{ title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/ },
{ title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/ },
{ title: '整数', value: 'z', pattern: /^-?\d+$/ },
@ -2095,7 +2160,12 @@
},
handleClickAdd() {
this.add()
let {addRowNum, addIndex, addScrollToBottom} = this.settings
if (addIndex <= 0) {
this.add(addRowNum, addScrollToBottom)
} else {
this.insert(addIndex, addRowNum, addScrollToBottom)
}
},
handleConfirmDelete() {
this.removeSelectedRows()
@ -2106,6 +2176,29 @@
clearSelection() {
this.selectedRowIds = []
},
// 获取当前选中的行
getSelection() {
return this.selectedRowIds.map(id => this.getCleanId(id))
},
// 设置当前选中的行
async setSelection(selectedRowIds) {
if (Array.isArray(selectedRowIds) && selectedRowIds.length > 0) {
// 兼容IE
await this.getElementPromise('tbody')
await this.$nextTick()
this.selectedRowIds = selectedRowIds.map(id => {
let temp = id
if (!this.hasCaseId(id)) {
temp = this.caseId + id
}
return temp
})
}
},
// 切换全选状态
toggleSelectionAll() {
this.handleChangeCheckedAll()
},
/** 用于搜索下拉框中的内容 */
handleSelectFilterOption(input, option, column) {
if (column.allowSearch === true || column.allowInput === true) {
@ -2313,11 +2406,7 @@
this.validateOneInput(value, row, column, this.notPassedIds, true, 'change')
// 触发valueChange 事件
if (showTime) {
this.elemValueChange(FormTypes.datetime, row, column, value)
} else {
this.elemValueChange(FormTypes.date, row, column, value)
}
this.elemValueChange(column.type, row, column, value)
},
//部门组件值改变
handleChangeDepartCommon(value, id, row, column){
@ -2353,7 +2442,21 @@
value['responseName'] = file.response[column.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[column.responseName]
if (typeof file.response.success === 'boolean') {
// 如果文件上传被拦截器拦下还会返回最外层的status = done
// 但是内部的success会返回false并携带异常信息
// 整个上传操作还是失败的
// https://github.com/zhangdaiscott/jeecg-boot/issues/2691
if (file.response.success) {
value['path'] = file.response[column.responseName]
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[column.responseName]
}
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}
@ -2415,6 +2518,25 @@
})
}
},
/** 添加按钮设置保存为默认值 */
onAddButtonSettingsSave() {
let obj = {
addRowNum: this.settings.addRowNum,
addIndex: this.settings.addIndex,
addScrollToBottom: this.settings.addScrollToBottom,
}
this.$ls.set('jet-add-btn-settings', obj)
this.$message.success('保存成功')
},
/** 获取保存的添加按钮默认值 */
getSavedAddButtonSettings() {
let obj= this.$ls.get('jet-add-btn-settings')
if (obj) {
Object.assign(this.settings, obj)
}
},
/** 记录用到数据绑定的组件的值 */
bindValuesChange(value, id, key) {
this.$set(this[key], id, value)
@ -2666,6 +2788,11 @@
if (col.type === FormTypes.select && (col.allowInput === true || col.allowSearch === true)) {
props['showSearch'] = true
}
if (col.type === FormTypes.sel_depart || col.type === FormTypes.sel_user) {
let { storeField, textField } = this.getStoreAndTextField(col)
props['store'] = storeField
props['text'] = textField
}
// 判断是否是禁用的列
props['disabled'] = (typeof col['disabled'] === 'boolean' ? col['disabled'] : props['disabled'])
@ -2683,6 +2810,42 @@
return props
},
/**获取部门选择 、用户选择的存储字段、展示字段*/
getStoreAndTextField(col){
let storeField = '', textField = ''
if(col.type === FormTypes.sel_depart){
storeField = 'id'
textField = 'departName'
}else if(col.type === FormTypes.sel_user){
storeField = 'username'
textField = 'realname'
}
if(col.fieldExtendJson){
// online逻辑
let tempJson = JSON.parse(col.fieldExtendJson)
if(tempJson){
if(tempJson.store){
storeField = tempJson.store
}
if(tempJson.text){
textField = tempJson.text
}
}
}else{
// 实际开发逻辑
if(col.store){
storeField = col.store
}
if(col.text){
textField = col.text
}
}
return {
storeField,
textField
}
},
/** 辅助方法:防止过快点击,如果点击过快的话就返回 true */
checkTooFastClick(key = 'default', ms = 300) {
let nowTime = Date.now()
@ -2896,6 +3059,21 @@
}else{
return this.excludeCode.indexOf(code)<0
}
},
// 判断用户、部门组件是否多选
isMultipleSelect(column){
let jsonStr = column.fieldExtendJson
if(jsonStr){
// online
let config = JSON.parse(jsonStr)
if(config && config['multiSelect']==false){
return false
}
}else if(column.multi==false){
// 实际开发
return false
}
return true;
}
},
@ -3280,3 +3458,19 @@
}
</style>
<style lang="less">
// 新增按钮配置气泡的样式
.j-add-btn-settings {
width: 240px;
.ant-form {
.ant-form-item {
margin-bottom: 0;
.ant-input-number {
width: 100%;
}
}
}
}
</style>

View File

@ -134,9 +134,17 @@
}else{
//update--begin--autor:wangshuai-----date:20200724------for富文本编辑器切换tab无法修改------
let tabLayout = getVmParentByName(this, 'TabLayout')
tabLayout.excuteCallback(()=>{
this.reload()
})
//update--begin--autor:liusq-----date:20210713------for处理特殊情况excuteCallback不能使用------
try {
tabLayout.excuteCallback(() => {
this.reload()
})
} catch (error) {
if (tabLayout) {
this.reload()
}
}
//update--end--autor:liusq-----date:20210713------for处理特殊情况excuteCallback不能使用------
//update--begin--autor:wangshuai-----date:20200724------for文本编辑器切换tab无法修改------
}
},

View File

@ -47,10 +47,10 @@
<script>
import { getClass, getStyle } from '@/utils/props-util'
import { triggerWindowResizeEvent } from '@/utils/util'
import { getClass, getStyle } from '@/utils/props-util'
import { triggerWindowResizeEvent } from '@/utils/util'
export default {
export default {
name: 'JModal',
props: {
title: String,
@ -169,6 +169,7 @@
</script>
<style lang="less">
.j-modal-box {
&.fullscreen {
top: 0;

View File

@ -0,0 +1,124 @@
<template>
<j-modal :visible="visible" :confirmLoading="loading" :after-close="afterClose" v-bind="modalProps" @ok="onOk" @cancel="onCancel">
<a-spin :spinning="loading">
<div v-html="content"></div>
<a-form-model ref="form" :model="model" :rules="rules">
<a-form-model-item prop="input">
<a-input ref="input" v-model="model.input" v-bind="inputProps" @pressEnter="onInputPressEnter"/>
</a-form-model-item>
</a-form-model>
</a-spin>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
export default {
name: 'JPrompt',
data() {
return {
visible: false,
loading: false,
content: '',
// 弹窗参数
modalProps: {
title: '',
},
inputProps: {
placeholder: '',
},
// form model
model: {
input: '',
},
// 校验
rule: [],
// 回调函数
callback: {},
}
},
computed: {
rules() {
return {
input: this.rule
}
},
},
methods: {
show(options) {
this.content = options.content
if (Array.isArray(options.rule)) {
this.rule = options.rule
}
if (options.defaultValue != null) {
this.model.input = options.defaultValue
}
// 取出常用的弹窗参数
let pickModalProps = pick(options, 'title', 'centered', 'cancelText', 'closable', 'mask', 'maskClosable', 'okText', 'okType', 'okButtonProps', 'cancelButtonProps', 'width', 'wrapClassName', 'zIndex', 'dialogStyle', 'dialogClass')
this.modalProps = Object.assign({}, pickModalProps, options.modalProps)
// 取出常用的input参数
let pickInputProps = pick(options, 'placeholder', 'allowClear')
this.inputProps = Object.assign({}, pickInputProps, options.inputProps)
// 回调函数
this.callback = pick(options, 'onOk', 'onOkAsync', 'onCancel')
this.visible = true
this.$nextTick(() => this.$refs.input.focus())
},
onOk() {
this.$refs.form.validate((ok, err) => {
if (ok) {
let event = {value: this.model.input, target: this}
// 异步方法优先级高于同步方法
if (typeof this.callback.onOkAsync === 'function') {
this.callback.onOkAsync(event)
} else if (typeof this.callback.onOk === 'function') {
this.callback.onOk(event)
this.close()
} else {
this.close()
}
}
})
},
onCancel() {
if (typeof this.callback.onCancel === 'function') {
this.callback.onCancel(this.model.input)
}
this.close()
},
onInputPressEnter() {
this.onOk()
},
close() {
this.visible = this.loading ? this.visible : false
},
forceClose() {
this.visible = false
},
showLoading() {
this.loading = true
},
hideLoading() {
this.loading = false
},
afterClose(e) {
if (typeof this.modalProps.afterClose === 'function') {
this.modalProps.afterClose(e)
}
this.$emit('after-close', e)
},
},
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,18 @@
import JModal from './JModal'
import JPrompt from './JPrompt'
export default {
install(Vue) {
Vue.component(JModal.name, JModal)
const JPromptExtend = Vue.extend(JPrompt)
Vue.prototype.$JPrompt = function (options = {}) {
// 创建prompt实例
const vm = new JPromptExtend().$mount()
vm.show(options)
// 关闭后销毁
vm.$on('after-close', () => vm.$destroy())
return vm
}
},
}

View File

@ -143,6 +143,10 @@
}
},
handleEmpty() {
// 禁用时,不允许清空内容
if (this.disabled) {
return
}
this.showText = ''
let destFieldsArr = this.destFields.split(',')
if (destFieldsArr.length === 0) {
@ -173,9 +177,11 @@
let tempDestArr = []
for(let rw of rows){
let val = rw[orgFieldsArr[i]]
if(!val){
// update--begin--autor:liusq-----date:20210713------for处理val等于0的情况issues/I3ZL4T------
if(typeof val=='undefined'|| val==null || val.toString()==""){
val = ""
}
// update--end--autor:liusq-----date:20210713------for处理val等于0的情况issues/I3ZL4T------
tempDestArr.push(val)
}
res[destFieldsArr[i]] = tempDestArr.join(",")

View File

@ -1,7 +1,7 @@
<template>
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder">
<a-select :value="arrayValue" @change="onChange" mode="multiple" :placeholder="placeholder" allowClear>
<a-select-option
v-for="(item,index) in options"
v-for="(item,index) in selectOptions"
:key="index"
:getPopupContainer="getParentContainer"
:value="item.value">
@ -12,6 +12,8 @@
<script>
//option {label:,value:}
import { getAction } from '@api/manage'
export default {
name: 'JSelectMultiple',
props: {
@ -31,7 +33,8 @@
},
options:{
type: Array,
required: true
default:()=>[],
required: false
},
triggerChange:{
type: Boolean,
@ -48,12 +51,22 @@
default:'',
required:false
},
dictCode:{
type:String,
required:false
},
},
data(){
return {
arrayValue:!this.value?[]:this.value.split(this.spliter)
arrayValue:!this.value?[]:this.value.split(this.spliter),
dictOptions: [],
}
},
computed:{
selectOptions(){
return this.dictOptions.length > 0 ? this.dictOptions : this.options
},
},
watch:{
value (val) {
if(!val){
@ -63,6 +76,11 @@
}
}
},
mounted(){
if (this.dictCode) {
this.loadDictOptions()
}
},
methods:{
onChange (selectedValue) {
if(this.triggerChange){
@ -77,7 +95,18 @@
}else{
return document.querySelector(this.popContainer)
}
}
},
// 根据字典code查询字典项
loadDictOptions(){
getAction(`/sys/dict/getDictItems/${this.dictCode}`,{}).then(res=>{
if (res.success) {
this.dictOptions = res.result.map(item => ({value: item.value, label: item.text}))
} else {
console.error('getDictItems error: : ', res)
this.dictOptions = []
}
})
},
},
}

View File

@ -103,6 +103,8 @@
<a-col :md="8" :xs="24" style="margin-bottom: 12px;">
<!-- 下拉搜索 -->
<j-search-select-tag v-if="item.type==='sel_search'" v-model="item.val" :dict="getDictInfo(item)" placeholder="请选择"/>
<!-- 下拉框 -->
<j-search-select-tag v-else-if="item.type==='list' && item.dictTable" v-model="item.val" :dict="getDictInfo(item)" placeholder="请选择"/>
<!-- 下拉多选 -->
<template v-else-if="item.type==='list_multi'">
<j-multi-select-tag v-if="item.options" v-model="item.val" :options="item.options" placeholder="请选择"/>
@ -136,14 +138,14 @@
v-else-if="item.type === 'select-user' || item.type === 'sel_user'"
v-model="item.val"
:buttons="false"
:multiple="false"
:multiple="allowMultiple(item)"
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"
:multi="allowMultiple(item)"
placeholder="请选择部门"
:customReturnField="item.customReturnField || 'id'"
/>
@ -331,7 +333,12 @@
let child = { ...item2 }
child.label = child.label || child.text
child.label = data.label + '-' + child.label
child.value = data.value + ',' + child.value
// update--begin--author:sunjianlei-----date:20220121------for【JTC-1167】【表单设计器】高级查询一对一字段查询不好使
// 是否仅包含字段名,不需要拼接子表表名
if (!data.onlyFieldName) {
child.value = data.value + ',' + child.value
}
// update--end--author:sunjianlei-----date:20220121------for【JTC-1167】【表单设计器】高级查询一对一字段查询不好使
child.val = ''
return child
})
@ -379,7 +386,7 @@
this.$message.warn("不能查询空条件")
}
},
emitCallback(event = {}) {
emitCallback(event = {}, loadStatus = true) {
let { params = [], matchType = this.matchType } = event
this.superQueryFlag = (params && params.length > 0)
for (let param of params) {
@ -388,7 +395,7 @@
}
}
console.debug('---高级查询参数--->', { params, matchType })
this.$emit(this.callback, params, matchType)
this.$emit(this.callback, params, matchType, loadStatus)
},
handleCancel() {
this.close()
@ -412,8 +419,9 @@
this.queryParamsModel.splice(index, 1)
},
handleSelected(node, item) {
let { type, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef
let { type, dbType, options, dictCode, dictTable, dictText, customReturnField, popup } = node.dataRef
item['type'] = type
item['dbType'] = dbType
item['options'] = options
item['dictCode'] = dictCode
item['dictTable'] = dictTable
@ -427,9 +435,13 @@
handleOpen() {
this.show()
},
handleOutReset(loadStaus=true) {
this.resetLine()
this.emitCallback({}, loadStaus)
},
handleReset() {
this.resetLine()
this.emitCallback()
this.emitCallback({}, true)
},
handleSave() {
let queryParams = this.removeEmptyObject(this.queryParamsModel)

View File

@ -17,7 +17,7 @@
:headers="headers"
:data="{'biz':bizPath}"
:fileList="fileList"
:beforeUpload="beforeUpload"
:beforeUpload="doBeforeUpload"
@change="handleChange"
:disabled="disabled"
:returnUrl="returnUrl"
@ -139,6 +139,9 @@
type: Boolean,
default: true
},
beforeUpload: {
type: Function
},
},
watch:{
value:{
@ -242,7 +245,7 @@
}
this.$emit('change', path);
},
beforeUpload(file){
doBeforeUpload(file){
this.uploadGoOn=true
var fileType = file.type;
if(this.fileType===FILE_TYPE_IMG){
@ -252,7 +255,10 @@
return false;
}
}
//TODO 扩展功能验证文件大小
// 扩展 beforeUpload 验证
if (typeof this.beforeUpload === 'function') {
return this.beforeUpload(file)
}
return true
},
handleChange(info) {

View File

@ -98,6 +98,8 @@ export default {
// 是否一直显示组件如果为false则只有点击的时候才出现组件
// 注:该参数不能动态修改;如果行、列字段多的情况下,会根据机器性能造成不同程度的卡顿。
alwaysEdit: PropTypes.bool.def(false),
// 联动配置,数组,详情配置见文档
linkageConfig: PropTypes.array.def(() => []),
},
data() {
return {
@ -151,7 +153,10 @@ export default {
// 允许执行刷新特效的行ID
reloadEffectRowKeysMap: {},
//配置了但是没有授权的按钮和列 集合
excludeCode:[]
excludeCode:[],
// 联动下拉选项(用于隔离不同的下拉选项)
// 内部联动配置map
_innerLinkageConfig: null,
}
},
computed: {
@ -178,6 +183,18 @@ export default {
renderOptions.target = this
}
}
// 处理联动列,联动列只能作用于 select 组件
if (column.$type === JVXETypes.select && this._innerLinkageConfig != null) {
// 判断当前列是否是联动列
if (this._innerLinkageConfig.has(column.key)) {
renderOptions.linkage = {
config: this._innerLinkageConfig.get(column.key),
getLinkageOptionsSibling: this.getLinkageOptionsSibling,
getLinkageOptionsAsync: this.getLinkageOptionsAsync,
linkageSelectChange: this.linkageSelectChange,
}
}
}
if (column.editRender) {
Object.assign(column.editRender, renderOptions)
}
@ -278,15 +295,21 @@ export default {
immediate: true,
async handler() {
let vxe = await getRefPromise(this, 'vxe')
// 阻断vue监听大数据提高性能
// 开启了排序就自动计算排序值
if (this.dragSort) {
this.dataSource.forEach((data, idx) => {
this.dataSource.forEach((data, idx) => {
// 开启了排序就自动计算排序值
if (this.dragSort) {
this.$set(data, this.dragSortKey, idx + 1)
})
}
}
// 处理联动回显数据
if (this._innerLinkageConfig != null) {
for (let configItem of this._innerLinkageConfig.values()) {
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
}
}
})
// 阻断vue监听大数据提高性能
vxe.loadData(this.dataSource)
// TODO 解析disabledRows
@ -469,7 +492,40 @@ export default {
this._innerColumns = _innerColumns
this._innerEditRules = _innerEditRules
}
}
},
// watch linkageConfig
// 整理多级联动配置
linkageConfig: {
immediate: true,
handler() {
if (Array.isArray(this.linkageConfig) && this.linkageConfig.length > 0) {
// 获取联动的key顺序
let getLcKeys = (key, arr) => {
let col = this._innerColumns.find(col => col.key === key)
if (col) {
arr.push(col.key)
if (col.linkageKey) {
return getLcKeys(col.linkageKey, arr)
}
}
return arr
}
let configMap = new Map()
this.linkageConfig.forEach(lc => {
let keys = getLcKeys(lc.key, [])
// 多个key共享一个引用地址
let configItem = {
...lc, keys,
optionsMap: new Map()
}
keys.forEach(k => configMap.set(k, configItem))
})
this._innerLinkageConfig = configMap
} else {
this._innerLinkageConfig = null
}
}
},
},
created() {
},
@ -668,7 +724,24 @@ export default {
async loadNewData(dataSource) {
if (Array.isArray(dataSource)) {
let {xTable} = this.$refs.vxe.$refs
return await xTable.loadData(dataSource)
// issues/2784
// 先清空所有数据
xTable.loadData([])
dataSource.forEach((data, idx) => {
// 开启了排序就自动计算排序值
if (this.dragSort) {
this.$set(data, this.dragSortKey, idx + 1)
}
// 处理联动回显数据
if (this._innerLinkageConfig != null) {
for (let configItem of this._innerLinkageConfig.values()) {
this.autoSetLinkageOptionsByData(data, '', configItem, 0)
}
}
})
// 再新增
return xTable.insertAt(dataSource)
}
return []
},
@ -793,6 +866,7 @@ export default {
* 添加一行或多行
*
* @param rows
* @param isOnlJs 是否是onlineJS增强触发的
* @return
*/
async addRows(rows = {}, isOnlJs) {
@ -892,6 +966,89 @@ export default {
this.$emit(name, event)
},
/** 【多级联动】获取同级联动下拉选项 */
getLinkageOptionsSibling(row, col, config, request) {
// 如果当前列不是顶级列
let key = ''
if (col.key !== config.key) {
// 就找出联动上级列
let idx = config.keys.findIndex(k => col.key === k)
let parentKey = config.keys[idx - 1]
key = row[parentKey]
// 如果联动上级列没有选择数据,就直接返回空数组
if (key === '' || key == null) {
return []
}
} else {
key = 'root'
}
let options = config.optionsMap.get(key)
if (!Array.isArray(options)) {
if (request) {
let parent = key === 'root' ? '' : key
return this.getLinkageOptionsAsync(config, parent)
} else {
options = []
}
}
return options
},
/** 【多级联动】获取联动下拉选项(异步) */
getLinkageOptionsAsync(config, parent) {
return new Promise(resolve => {
let key = parent ? parent : 'root'
let options
if (config.optionsMap.has(key)) {
options = config.optionsMap.get(key)
if (options instanceof Promise) {
options.then(opt => {
config.optionsMap.set(key, opt)
resolve(opt)
})
} else {
resolve(options)
}
} else if (typeof config.requestData === 'function') {
// 调用requestData方法通过传入parent来获取子级
let promise = config.requestData(parent)
config.optionsMap.set(key, promise)
promise.then(opt => {
config.optionsMap.set(key, opt)
resolve(opt)
})
} else {
resolve([])
}
})
},
// 【多级联动】 用于回显数据,自动填充 optionsMap
autoSetLinkageOptionsByData(data, parent, config, level) {
if (level === 0) {
this.getLinkageOptionsAsync(config, '')
} else {
this.getLinkageOptionsAsync(config, parent)
}
if (config.keys.length - 1 > level) {
let value = data[config.keys[level]]
if (value) {
this.autoSetLinkageOptionsByData(data, value, config, level + 1)
}
}
},
// 【多级联动】联动组件change时清空下级组件
linkageSelectChange(row, col, config, value) {
if (col.linkageKey) {
this.getLinkageOptionsAsync(config, value)
let idx = config.keys.findIndex(k => k === col.key)
let values = {}
for (let i = idx; i < config.keys.length; i++) {
values[config.keys[i]] = ''
}
// 清空后几列的数据
this.setValues([{rowKey: row.id, values}])
}
},
/** 加载数据字典并合并到 options */
_loadDictConcatToOptions(column) {
initDictOptions(column.dictCode).then((res) => {
@ -1084,6 +1241,15 @@ export default {
let createValue = getEnhancedMixins(col.$type || col.type, 'createValue')
record[col.key] = createValue({row: record, column, $table: xTable})
}
// update-begin--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
if (col.$type === JVXETypes.select && this._innerLinkageConfig != null) {
// 判断当前列是否是联动列
if (this._innerLinkageConfig.has(col.key)) {
let configItem = this._innerLinkageConfig.get(col.key)
this.getLinkageOptionsAsync(configItem, '')
}
}
// update-end--author:sunjianlei---date:20210819------for: 处理联动列,联动列只能作用于 select 组件
})
return record
},
@ -1236,7 +1402,7 @@ const fooPatterns = [
{title: '网址', value: 'url', pattern: /^(?:([A-Za-z]+):)?(\/{0,3})([0-9.\-A-Za-z]+)(?::(\d+))?(?:\/([^?#]*))?(?:\?([^#]*))?(?:#(.*))?$/},
{title: '电子邮件', value: 'e', pattern: /^([\w]+\.*)([\w]+)@[\w]+\.\w{3}(\.\w{2}|)$/},
{title: '手机号码', value: 'm', pattern: /^1[3456789]\d{9}$/},
{title: '邮政编码', value: 'p', pattern: /^[1-9]\d{5}$/},
{title: '邮政编码', value: 'p', pattern: /^[0-9]{6}$/},
{title: '字母', value: 's', pattern: /^[A-Z|a-z]+$/},
{title: '数字', value: 'n', pattern: /^-?\d+(\.?\d+|\d?)$/},
{title: '整数', value: 'z', pattern: /^-?\d+$/},

View File

@ -10,6 +10,7 @@
<a-popconfirm
v-if="showRemove"
:title="`确定要删除这 ${selectedRowIds.length} 项吗?`"
:disabled="disabled"
@confirm="trigger('remove')"
>
<a-button icon="minus" :disabled="disabled">删除</a-button>

View File

@ -16,11 +16,13 @@
:multi="multi"
:rootOpened="rootOpened"
:depart-id="departIds"
:store="storeField()"
:text="textField()"
@ok="handleOK"
@initComp="initComp"/>
<span style="display: inline-block;height:100%;padding-left:14px" v-if="departIds" >
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ departNames }}</span>
<a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
<a-icon v-if="!componentDisabled" style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
</span>
</div>
</template>
@ -68,7 +70,7 @@
}
},
multi(){
if(this.cellProps.multi==false){
if(this.cellProps.multi==false || this.originColumn.multi===false){
return false
}else{
return true
@ -96,20 +98,26 @@
},
methods: {
openSelect(){
// disabled 不弹窗
if (this.componentDisabled) {
return
}
this.$refs.innerDepartSelectModal.show()
},
handleEmpty(){
this.handleOK('')
},
handleOK(rows, idstr) {
handleOK(rows) {
let value = ''
if (!rows && rows.length <= 0) {
this.departNames = ''
this.departIds = ''
} else {
value = rows.map(row => row[this.customReturnField]).join(',')
this.departNames = rows.map(row => row['departName']).join(',')
this.departIds = idstr
let storeField = this.storeField();
let textField = this.textField();
value = rows.map(row => row[storeField]).join(',')
this.departNames = rows.map(row => row[textField]).join(',')
this.departIds = value
}
this.handleChangeCommon(this.departIds)
},
@ -118,6 +126,34 @@
},
handleChange(value) {
this.handleChangeCommon(value)
},
storeField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.store){
return json.store
}
}else if(this.originColumn.store){
return this.originColumn.store
}
}
return 'id'
},
textField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.text){
return json.text
}
}else if(this.originColumn.text){
return this.originColumn.text
}
}
return 'departName'
}
},
enhanced: {

View File

@ -7,11 +7,16 @@
v-bind="selectProps"
style="width: 100%;"
@blur="handleBlur"
@change="handleChangeCommon"
@change="handleChange"
@search="handleSearchSelect"
>
<template v-for="option of originColumn.options">
<div v-if="loading" slot="notFoundContent">
<a-icon type="loading" />
<span>&nbsp;加载中</span>
</div>
<template v-for="option of selectOptions">
<a-select-option :key="option.value" :value="option.value" :disabled="option.disabled">
<span>{{option.text || option.label || option.title|| option.value}}</span>
</a-select-option>
@ -23,10 +28,18 @@
<script>
import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
import { JVXETypes } from '@comp/jeecg/JVxeTable/index'
import { filterDictText } from '@comp/dict/JDictSelectUtil'
export default {
name: 'JVxeSelectCell',
mixins: [JVxeCellMixins],
data(){
return {
loading: false,
// 异步加载的options用于多级联动
asyncOptions: null,
}
},
computed: {
selectProps() {
let props = {...this.cellProps}
@ -37,6 +50,32 @@
}
return props
},
// 下拉选项
selectOptions() {
if (this.asyncOptions) {
return this.asyncOptions
}
let {linkage} = this.renderOptions
if (linkage) {
let {getLinkageOptionsSibling, config} = linkage
let res = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
// 当返回Promise时说明是多级联动
if (res instanceof Promise) {
this.loading = true
res.then(opt => {
this.asyncOptions = opt
this.loading = false
}).catch(e => {
console.error(e)
this.loading = false
})
} else {
this.asyncOptions = null
return res
}
}
return this.originColumn.options
},
},
created() {
let multiple = [JVXETypes.selectMultiple, JVXETypes.list_multi]
@ -54,6 +93,15 @@
},
methods: {
handleChange(value) {
// 处理下级联动
let linkage = this.renderOptions.linkage
if (linkage) {
linkage.linkageSelectChange(this.row, this.originColumn, linkage.config, value)
}
this.handleChangeCommon(value)
},
/** 处理blur失去焦点事件 */
handleBlur(value) {
let {allowInput, options} = this.originColumn
@ -120,7 +168,28 @@
dispatchEvent.call(this, event, 'ant-select')
},
},
translate: {enabled: true},
translate: {
enabled: true,
async handler(value,) {
let options
let {linkage} = this.renderOptions
// 判断是否是多级联动,如果是就通过接口异步翻译
if (linkage) {
let {getLinkageOptionsSibling, config} = linkage
options = getLinkageOptionsSibling(this.row, this.originColumn, config, true)
if (options instanceof Promise) {
return new Promise(resolve => {
options.then(opt => {
resolve(filterDictText(opt, value))
})
})
}
} else {
options = this.column.own.options
}
return filterDictText(options, value)
},
},
getValue(value) {
if (Array.isArray(value)) {
return value.join(',')

View File

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

View File

@ -14,7 +14,7 @@
<a-tooltip v-else-if="file.status === 'done'" title="上传完成">
<a-icon type="check-circle" style="color:#00DB00;"/>
</a-tooltip>
<a-tooltip v-else title="上传失败">
<a-tooltip v-else :title="file.message||'上传失败'">
<a-icon type="exclamation-circle" style="color:red;"/>
</a-tooltip>
</template>
@ -118,8 +118,17 @@
value['responseName'] = file.response[col.responseName]
}
if (file.status === 'done') {
value['path'] = file.response[col.responseName]
this.handleChangeCommon(value)
if (typeof file.response.success === 'boolean') {
if (file.response.success) {
value['path'] = file.response[col.responseName]
} else {
value['status'] = 'error'
value['message'] = file.response.message || '未知错误'
}
} else {
// 考虑到如果设置action上传路径为非jeecg-boot后台可能不会返回 success 属性的情况,就默认为成功
value['path'] = file.response[col.responseName]
}
} else if (file.status === 'error') {
value['message'] = file.response.message || '未知错误'
}

View File

@ -16,10 +16,12 @@
:multi="multi"
:user-ids="userIds"
@ok="selectOK"
:store="storeField"
:text="textField"
@initComp="initComp"/>
<span style="display: inline-block;height:100%;padding-left:14px" v-if="userIds" >
<span @click="openSelect" style="display: inline-block;vertical-align: middle">{{ userNames }}</span>
<a-icon style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
<a-icon v-if="!componentDisabled" style="margin-left:5px;vertical-align: middle" type="close-circle" @click="handleEmpty" title="清空"/>
</span>
</div>
@ -77,6 +79,30 @@
}else{
return true
}
},
storeField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.store){
return json.store
}
}
}
return 'username'
},
textField(){
if(this.originColumn){
const str = this.originColumn.fieldExtendJson
if(str){
let json = JSON.parse(str)
if(json && json.text){
return json.text
}
}
}
return 'realname'
}
},
watch: {
@ -93,6 +119,10 @@
},
methods: {
openSelect() {
// disabled 不弹窗
if (this.componentDisabled) {
return
}
this.$refs.selectModal.showModal()
},
selectOK(rows, idstr) {
@ -102,12 +132,8 @@
this.userNames = ''
this.userIds = ''
} else {
let temp = ''
for (let item of rows) {
temp += ',' + item.realname
}
this.userNames = temp.substring(1)
this.userIds = idstr
this.userIds = rows.map(row => row[this.storeField]).join(',')
this.userNames = rows.map(row => row[this.textField]).join(',')
}
this.handleChangeCommon(this.userIds)
},

View File

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

View File

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

View File

@ -73,6 +73,16 @@ export default {
props['disabled'] = true
}
// update-begin-author:taoyan date:20211011 for: online表单附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
let jsonStr = col['fieldExtendJson']
if(jsonStr){
let fieldExtendJson = JSON.parse(jsonStr)
if(fieldExtendJson && fieldExtendJson['multiSelect']==false){
props['multi'] = false
}
}
// update-end-author:taoyan date:20211011 for: online表单附表用户选择器{"multiSelect":false}不生效,单表可以生效 #3036
return props
},
},
@ -102,7 +112,13 @@ export default {
// 判断是否启用翻译
if (this.renderType === JVXERenderType.spaner && this.enhanced.translate.enabled) {
this.innerValue = this.enhanced.translate.handler.call(this, value)
let res = this.enhanced.translate.handler.call(this, value)
// 异步翻译,目前仅【多级联动】使用
if (res instanceof Promise) {
res.then(value => this.innerValue = value)
} else {
this.innerValue = res
}
}
},
},

View File

@ -26,19 +26,24 @@ import JSlider from './JSlider.vue'
import JSwitch from './JSwitch.vue'
import JTime from './JTime.vue'
import JTreeTable from './JTreeTable.vue'
import JEasyCron from "@/components/jeecg/JEasyCron";
import JEasyCron from '@/components/jeecg/JEasyCron'
//jeecgbiz
import JSelectDepart from '../jeecgbiz/JSelectDepart.vue'
import JSelectMultiUser from '../jeecgbiz/JSelectMultiUser.vue'
import JSelectPosition from '../jeecgbiz/JSelectPosition.vue'
import JSelectRole from '../jeecgbiz/JSelectRole.vue'
import JSelectUserByDep from '../jeecgbiz/JSelectUserByDep.vue'
//引入需要全局注册的js函数和变量
import { Modal, notification,message } from 'ant-design-vue'
import lodash_object from 'lodash'
import debounce from 'lodash/debounce'
import pick from 'lodash.pick'
import data from 'china-area-data'
export default {
install(Vue) {
Vue.use(JModal)
Vue.component('JMarkdownEditor', JMarkdownEditor)
Vue.component(JModal.name, JModal)
Vue.component('JPopupOnlReport', JPopupOnlReport)
Vue.component('JFilePop', JFilePop)
Vue.component('JInputPop', JInputPop)
@ -73,5 +78,14 @@ export default {
Vue.component('JSelectRole', JSelectRole)
Vue.component('JSelectUserByDep', JSelectUserByDep)
Vue.component(JEasyCron.name, JEasyCron)
//注册全局js函数和变量
Vue.prototype.$Jnotification = notification
Vue.prototype.$Jmodal = Modal
Vue.prototype.$Jmessage = message
Vue.prototype.$Jlodash = lodash_object
Vue.prototype.$Jdebounce= debounce
Vue.prototype.$Jpick = pick
Vue.prototype.$Jpcaa = data
}
}

View File

@ -7,7 +7,7 @@
<a-icon type="close" @click="visible=false"/>
</span>
</div>
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange">
<a-input :value="inputContent" :disabled="disabled" @change="handleInputChange" :placeholder="placeholder">
<a-icon slot="suffix" type="fullscreen" @click.stop="pop" />
</a-input>
<div slot="content">
@ -53,6 +53,10 @@
type: Boolean,
default: false,
},
placeholder:{
type:String,
required:false
}
},
data(){
@ -83,6 +87,10 @@
this.$emit('change',this.inputContent)
},
pop(){
// disabled 不弹窗
if (this.disabled) {
return
}
this.visible=true
this.$nextTick(() => {
this.$refs.textarea.focus()

View File

@ -58,7 +58,7 @@
:dataSource="table.dataSource"
:pagination="table.pagination"
:loading="table.loading"
:rowSelection="{fixed:true,selectedRowKeys: table.selectedRowKeys, onChange: handleChangeInTableSelect}"
:rowSelection="{type:rowSelectionType,fixed:true,selectedRowKeys: table.selectedRowKeys, onChange: handleChangeInTableSelect}"
@change="handleChangeInTable"
style="min-height: 300px"
:scroll="tableScroll"
@ -74,6 +74,7 @@
import {filterObj} from '@/utils/util'
import { filterMultiDictText } from '@/components/dict/JDictSelectUtil'
import { httpGroupRequest } from '@/api/GroupRequest.js'
import md5 from 'md5'
const MODAL_WIDTH = 1200;
export default {
@ -137,8 +138,12 @@
param:{
deep:true,
handler(){
this.dynamicParamHandler()
this.loadData();
// update--begin--autor:liusq-----date:20210706------forJPopup组件在modal中使用报错#2729------
if(this.visible){
this.dynamicParamHandler()
this.loadData();
}
// update--begin--autor:liusq-----date:20210706------forJPopup组件在modal中使用报错#2729------
},
},
sorter: {
@ -166,7 +171,11 @@
computed:{
showSearchFlag(){
return this.queryInfo && this.queryInfo.length>0
}
},
// 行选择框类型,根据是否多选来控制显示为单选框还是多选框
rowSelectionType() {
return this.multi ? 'checkbox' : 'radio'
},
},
methods:{
loadColumnsInfo(){
@ -196,6 +205,12 @@
}
this.table.columns = [...currColumns]
this.initQueryInfo()
} else {
this.$error({
title: '出错了',
content: (<p>Popup初始化失败请检查你的配置或稍后重试<br/>错误信息如下:{res.message}</p>),
onOk: () => this.close(),
})
}
})
},
@ -386,9 +401,12 @@
}
//update-end---author:liusq Date:20210203 forpop选择器列主键问题 issues/I29P9Q------------
})
if(res.length>50){
// update-begin---author:taoyan Date:20211025 forjpopup 表格key重复BUG /issues/3121
res = md5(res)
/*if(res.length>50){
res = res.substring(0,50)
}
}*/
// update-end---author:taoyan Date:20211025 forjpopup 表格key重复BUG /issues/3121
return res
},
@ -413,6 +431,11 @@
this.table.selectionRows.splice(rowKey_index,1);
}
}
// 判断是否允许多选,如果不允许多选,就只存储最后一个选中的行
if (!this.multi && this.table.selectedRowKeys.length > 1) {
this.table.selectionRows = [this.table.selectionRows.pop()]
this.table.selectedRowKeys = [this.table.selectedRowKeys.pop()]
}
}
}
}

View File

@ -62,8 +62,9 @@
import { getAction } from '@/api/manage'
import Ellipsis from '@/components/Ellipsis'
import { JeecgListMixin } from '@/mixins/JeecgListMixin'
import { cloneObject, pushIfNotExist } from '@/utils/util'
import { pushIfNotExist } from '@/utils/util'
import JSelectBizQueryItem from './JSelectBizQueryItem'
import {cloneDeep} from 'lodash'
export default {
name: 'JSelectBizComponentModal',
@ -177,11 +178,24 @@
computed: {
// 表头
innerColumns() {
let columns = cloneObject(this.columns)
let columns = cloneDeep(this.columns)
columns.forEach(column => {
// 给所有的列加上过长裁剪
if (this.ellipsisLength !== -1) {
column.customRender = (text) => this.renderEllipsis(text)
// JSelectBizComponent columns 建议开放customRender等方法类配置
// https://github.com/jeecgboot/jeecg-boot/issues/3203
let myCustomRender = column.customRender
column.customRender = (text, record, index) => {
let value = text
if (typeof myCustomRender === 'function') {
// noinspection JSVoidFunctionReturnValueUsed
value = myCustomRender(text, record, index)
}
if (typeof value === 'string') {
return this.renderEllipsis(value)
}
return value
}
}
})
return columns
@ -192,7 +206,7 @@
deep: true,
immediate: true,
handler(val) {
this.innerValue = cloneObject(val)
this.innerValue = cloneDeep(val)
this.selectedRowKeys = []
this.valueWatchHandler(val)
this.queryOptionsByValue(val)

View File

@ -1,9 +1,9 @@
<template>
<div class="components-input-demo-presuffix">
<!---->
<a-input @click="openModal" placeholder="请点击选择部门" v-model="departNames" readOnly :disabled="disabled">
<a-input @click="openModal" placeholder="请点击选择部门" v-model="textVals" readOnly :disabled="disabled">
<a-icon slot="prefix" type="cluster" title="部门选择控件"/>
<a-icon v-if="departIds" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
<a-icon v-if="storeVals" slot="suffix" type="close-circle" @click="handleEmpty" title="清空"/>
</a-input>
<j-select-depart-modal
@ -11,7 +11,10 @@
:modal-width="modalWidth"
:multi="multi"
:rootOpened="rootOpened"
:depart-id="departIds"
:depart-id="value"
:store="storeField"
:text="textField"
:treeOpera="treeOpera"
@ok="handleOK"
@initComp="initComp"/>
</div>
@ -19,6 +22,7 @@
<script>
import JSelectDepartModal from './modal/JSelectDepartModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default {
name: 'JSelectDepart',
components:{
@ -52,56 +56,71 @@
// 自定义返回字段,默认返回 id
customReturnField: {
type: String,
default: 'id'
default: ''
},
backDepart: {
type: Boolean,
default: false,
required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'id',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'departName',
required: false
},
treeOpera: {
type: Boolean,
default: false,
required: false
}
},
data(){
return {
visible:false,
confirmLoading:false,
departNames:"",
departIds:''
storeVals: '', //[key values]
textVals: '' //[label values]
}
},
computed:{
storeField(){
let field = this.customReturnField
if(!field){
field = this.store;
}
return underLinetoHump(field)
},
textField(){
return underLinetoHump(this.text)
}
},
mounted(){
this.departIds = this.value
this.storeVals = this.value
},
watch:{
value(val){
//update-begin-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2
// if (this.customReturnField === 'id') {
this.departIds = val
// }
//update-end-author:wangshuai date:20201124 for:组件 JSelectDepart.vue不是默认id时新内容编辑问题 gitee I247X2
this.storeVals = val
}
},
methods:{
initComp(departNames){
this.departNames = departNames
//update-begin-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
//TODO 当返回字段为部门名称时会有问题,因为部门名称不唯一
//返回字段不为id时根据返回字段获取id
if(this.customReturnField !== 'id' && this.value){
const dataList = this.$refs.innerDepartSelectModal.dataList;
console.log('this.value',this.value)
this.departIds = this.value.split(',').map(item => {
const data = dataList.filter(d=>d[this.customReturnField] === item)
return data.length > 0 ? data[0].id : ''
}).join(',')
}
//update-end-author:lvdandan date:20200513 for:TESTA-438 部门选择组件自定义返回值,数据无法回填
initComp(textVals){
this.textVals = textVals
},
//返回选中的部门信息
backDeparInfo(){
if(this.backDepart===true){
if(this.departIds && this.departIds.length>0){
let arr1 = this.departIds.split(',')
let arr2 = this.departNames.split(',')
//LOWCOD-2147 【用户管理】选择部门和上级以后,负责部门没有数据可选 (陶炎改造自定义返回字段导致)
if(this.storeVals && this.storeVals.length>0){
let arr1 = this.storeVals.split(',')
let arr2 = this.textVals.split(',')
let info = []
for(let i=0;i<arr1.length;i++){
info.push({
@ -116,17 +135,21 @@
openModal(){
this.$refs.innerDepartSelectModal.show()
},
handleOK(rows, idstr) {
let value = ''
handleOK(rows) {
if (!rows && rows.length <= 0) {
this.departNames = ''
this.departIds = ''
this.textVals = ''
this.storeVals = ''
} else {
value = rows.map(row => row[this.customReturnField]).join(',')
this.departNames = rows.map(row => row['departName']).join(',')
this.departIds = idstr
let arr1 = []
let arr2 = []
for(let dep of rows){
arr1.push(dep[this.storeField])
arr2.push(dep[this.textField])
}
this.storeVals = arr1.join(',')
this.textVals = arr2.join(',')
}
this.$emit("change", value)
this.$emit("change", this.storeVals)
this.backDeparInfo()
},
getDepartNames(){

View File

@ -1,19 +1,28 @@
<template>
<div>
<a-input-search
v-model="userNames"
v-model="textVals"
placeholder="请先选择用户"
readOnly
unselectable="on"
@search="onSearchDepUser">
<a-button slot="enterButton" :disabled="disabled">选择用户</a-button>
</a-input-search>
<j-select-user-by-dep-modal ref="selectModal" :modal-width="modalWidth" :multi="multi" @ok="selectOK" :user-ids="value" @initComp="initComp"/>
<j-select-user-by-dep-modal
ref="selectModal"
:modal-width="modalWidth"
:multi="multi"
@ok="selectOK"
:user-ids="value"
:store="storeField"
:text="textField"
@initComp="initComp"/>
</div>
</template>
<script>
import JSelectUserByDepModal from './modal/JSelectUserByDepModal'
import { underLinetoHump } from '@/components/_util/StringUtil'
export default {
name: 'JSelectUserByDep',
@ -42,20 +51,44 @@
type: Boolean,
default: false,
required: false
},
// 存储字段 [key field]
store: {
type: String,
default: 'username',
required: false
},
// 显示字段 [label field]
text: {
type: String,
default: 'realname',
required: false
}
},
data() {
return {
userIds: "",
userNames: ""
storeVals: '', //[key values]
textVals: '' //[label values]
}
},
computed:{
storeField(){
let field = this.customReturnField
if(!field){
field = this.store;
}
return underLinetoHump(field)
},
textField(){
return underLinetoHump(this.text)
}
},
mounted() {
this.userIds = this.value
this.storeVals = this.value
},
watch: {
value(val) {
this.userIds = val
this.storeVals = val
}
},
model: {
@ -63,15 +96,15 @@
event: 'change'
},
methods: {
initComp(userNames) {
this.userNames = userNames
initComp(textVals) {
this.textVals = textVals
},
//返回选中的用户信息
backDeparInfo(){
if(this.backUser===true){
if(this.userIds && this.userIds.length>0){
let arr1 = this.userIds.split(',')
let arr2 = this.userNames.split(',')
if(this.storeVals && this.storeVals.length>0){
let arr1 = this.storeVals.split(',')
let arr2 = this.textVals.split(',')
let info = []
for(let i=0;i<arr1.length;i++){
info.push({
@ -86,21 +119,22 @@
onSearchDepUser() {
this.$refs.selectModal.showModal()
},
selectOK(rows, idstr) {
selectOK(rows) {
console.log("当前选中用户", rows)
console.log("当前选中用户ID", idstr)
if (!rows) {
this.userNames = ''
this.userIds = ''
this.storeVals = ''
this.textVals = ''
} else {
let temp = ''
let temp1 = []
let temp2 = []
for (let item of rows) {
temp += ',' + item.realname
temp1.push(item[this.storeField])
temp2.push(item[this.textField])
}
this.userNames = temp.substring(1)
this.userIds = idstr
this.storeVals = temp1.join(',')
this.textVals = temp2.join(',')
}
this.$emit("change", this.userIds)
this.$emit("change", this.storeVals)
}
}
}

View File

@ -6,16 +6,19 @@
:confirmLoading="confirmLoading"
@ok="handleSubmit"
@cancel="handleCancel"
@update:fullscreen="isFullscreen"
wrapClassName="j-depart-select-modal"
switchFullscreen
cancelText="关闭">
<a-spin tip="Loading..." :spinning="false">
<a-input-search style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" @search="onSearch" />
<a-input-search v-model="searchValue" style="margin-bottom: 1px" placeholder="请输入部门名称按回车进行搜索" />
<a-empty v-if="filterTreeData.length===0"></a-empty>
<a-tree
v-else
checkable
class="my-dept-select-tree"
:treeData="treeData"
:checkStrictly="true"
:class="treeScreenClass"
:treeData="filterTreeData"
:checkStrictly="checkStrictly"
@check="onCheck"
@select="onSelect"
@expand="onExpand"
@ -23,17 +26,24 @@
:expandedKeys="expandedKeys"
:checkedKeys="checkedKeys">
<template slot="title" slot-scope="{title}">
<span v-if="title.indexOf(searchValue) > -1">
{{title.substr(0, title.indexOf(searchValue))}}
<span style="color: #f50">{{searchValue}}</span>
{{title.substr(title.indexOf(searchValue) + searchValue.length)}}
</span>
<span v-else>{{title}}</span>
</template>
</a-tree>
</a-spin>
<!--底部父子关联操作和确认取消按钮-->
<template slot="footer" v-if="treeOpera && multi">
<div class="drawer-bootom-button">
<a-dropdown style="float: left" :trigger="['click']" placement="topCenter">
<a-menu slot="overlay">
<a-menu-item key="1" @click="switchCheckStrictly(1)">父子关联</a-menu-item>
<a-menu-item key="2" @click="switchCheckStrictly(2)">取消关联</a-menu-item>
</a-menu>
<a-button>
树操作 <a-icon type="up" />
</a-button>
</a-dropdown>
<a-button @click="handleCancel" type="primary" style="margin-right: 0.8rem">关闭</a-button>
<a-button @click="handleSubmit" type="primary" >确认</a-button>
</div>
</template>
</j-modal>
</template>
@ -41,7 +51,7 @@
import { queryDepartTreeList } from '@/api/api'
export default {
name: 'JSelectDepartModal',
props:['modalWidth','multi','rootOpened','departId'],
props:['modalWidth','multi','rootOpened','departId', 'store', 'text','treeOpera'],
data(){
return {
visible:false,
@ -52,7 +62,9 @@
dataList:[],
checkedKeys:[],
checkedRows:[],
searchValue:""
searchValue:"",
checkStrictly: true,
fullscreen:false
}
},
created(){
@ -64,15 +76,30 @@
},
visible: {
handler() {
if (this.departId) {
this.checkedKeys = this.departId.split(",");
// console.log('this.departId', this.departId)
} else {
this.checkedKeys = [];
}
this.initDepartComponent(true)
}
}
},
computed:{
treeScreenClass() {
return {
'my-dept-select-tree': true,
'fullscreen': this.fullscreen,
}
},
filterTreeData(){
if(!this.searchValue){
return this.treeData
}
let filter = []
this.dataList.forEach((item) => {
if (item.title.includes(this.searchValue)) {
filter.push(Object.assign({}, item, {children: null, isLeaf: true}))
}
})
return filter
},
},
methods:{
show(){
this.visible=true
@ -80,6 +107,7 @@
this.checkedKeys=[]
},
loadDepart(){
// 这个方法是找到所有的部门信息
queryDepartTreeList().then(res=>{
if(res.success){
let arr = [...res.result]
@ -92,20 +120,23 @@
}
})
},
initDepartComponent(){
let names = ''
initDepartComponent(flag){
let arr = []
//该方法两个地方用 1.visible改变事件重新设置选中项 2.组件编辑页面回显
let fieldName = flag==true?'key':this.text
if(this.departId){
let currDepartId = this.departId
let arr2 = this.departId.split(',')
for(let item of this.dataList){
if(currDepartId.indexOf(item.key)>=0){
names+=","+item.title
if(arr2.indexOf(item[this.store])>=0){
arr.push(item[fieldName])
}
}
if(names){
names = names.substring(1)
}
}
this.$emit("initComp",names)
if(flag==true){
this.checkedKeys = [...arr]
}else{
this.$emit("initComp", arr.join(','))
}
},
reWriterWithSlot(arr){
for(let item of arr){
@ -129,8 +160,11 @@
}
}
this.expandedKeys=[...keys]
//全部keys
//this.allTreeKeys = [...keys]
}else{
this.expandedKeys=[]
//this.allTreeKeys = []
}
},
onCheck (checkedKeys,info) {
@ -139,25 +173,32 @@
this.checkedKeys = [...arr]
this.checkedRows = (this.checkedKeys.length === 0) ? [] : [info.node.dataRef]
}else{
this.checkedKeys = checkedKeys.checked
if(this.checkStrictly){
this.checkedKeys = checkedKeys.checked
}else{
this.checkedKeys = checkedKeys
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
},
onSelect(selectedKeys,info) {
let keys = []
keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
this.checkedKeys = [...keys]
this.checkedRows=[info.node.dataRef]
}else{
let currKey = info.node.dataRef.key
if(this.checkedKeys.indexOf(currKey)>=0){
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
//取消关联的情况下才走onSelect的逻辑
if(this.checkStrictly){
let keys = []
keys.push(selectedKeys[0])
if(!this.checkedKeys || this.checkedKeys.length===0 || !this.multi){
this.checkedKeys = [...keys]
this.checkedRows=[info.node.dataRef]
}else{
this.checkedKeys.push(...keys)
let currKey = info.node.dataRef.key
if(this.checkedKeys.indexOf(currKey)>=0){
this.checkedKeys = this.checkedKeys.filter(item=> item !==currKey)
}else{
this.checkedKeys.push(...keys)
}
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
}
this.checkedRows = this.getCheckedRows(this.checkedKeys)
},
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
@ -193,22 +234,6 @@
}
}
return parentKey
},
onSearch(value){
const expandedKeys = this.dataList.map((item) => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key,this.treeData)
}
return null
}).filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true,
})
},
// 根据 checkedKeys 获取 rows
getCheckedRows(checkedKeys) {
@ -235,6 +260,16 @@
}
}
return rows
},
switchCheckStrictly (v) {
if(v==1){
this.checkStrictly = false
}else if(v==2){
this.checkStrictly = true
}
},
isFullscreen(val){
this.fullscreen=val
}
}
}
@ -244,8 +279,22 @@
<style lang="less" scoped>
// 限制部门选择树高度,避免部门太多时点击确定不便
.my-dept-select-tree{
height: 350px;
height:350px;
&.fullscreen{
height: calc(100vh - 250px);
}
overflow-y: scroll;
}
.drawer-bootom-button {
position: absolute;
bottom: 0;
width: 100%;
border-top: 1px solid #e8e8e8;
padding: 10px 16px;
text-align: right;
left: 0;
background: #fff;
border-radius: 0 0 2px 2px;
}
</style>

View File

@ -21,21 +21,30 @@
:dropdownStyle="{maxHeight:'200px',overflow:'auto'}"
:treeData="departTree"
:expandAction="false"
:expandedKeys.sync="expandedKeys"
@select="onDepSelect"
:load-data="onLoadDepartment"
/>
</a-card>
</a-col>
<a-col :md="18" :sm="24">
<a-card :bordered="false">
用户账号:
<a-input-search
:style="{width:'150px',marginBottom:'15px'}"
placeholder="请输入账号"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
<a-button @click="searchReset(1)" style="margin-left: 20px" icon="redo">重置</a-button>
<a-form-model>
<a-form-model-item label="用户账号" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row type="flex" :gutter="8">
<a-col :span="18">
<a-input-search
:style="{width:'100%'}"
placeholder="请输入账号"
v-model="queryParam.username"
@search="onSearch"
></a-input-search>
</a-col>
<a-col :span="6">
<a-button @click="searchReset(1)" icon="redo">重置</a-button>
</a-col>
</a-row>
</a-form-model-item>
</a-form-model>
<!--用户列表-->
<a-table
ref="table"
@ -57,13 +66,13 @@
<script>
import { pushIfNotExist, filterObj } from '@/utils/util'
import {queryDepartTreeList, getUserList, queryUserByDepId} from '@/api/api'
import {queryDepartTreeList, getUserList, queryUserByDepId, queryDepartTreeSync} from '@/api/api'
import { getAction } from '@/api/manage'
export default {
name: 'JSelectUserByDepModal',
components: {},
props: ['modalWidth', 'multi', 'userIds'],
props: ['modalWidth', 'multi', 'userIds', 'store', 'text'],
data() {
return {
queryParam: {
@ -133,6 +142,14 @@
form: this.$form.createForm(this),
loading: false,
expandedKeys: [],
labelCol: {
xs: { span: 24 },
sm: { span: 4 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 10 },
},
}
},
computed: {
@ -159,27 +176,27 @@
if (this.userIds) {
// 这里最后加一个 , 的原因是因为无论如何都要使用 in 查询,防止后台进行了模糊匹配,导致查询结果不准确
let values = this.userIds.split(',') + ','
getUserList({
username: values,
pageNo: 1,
pageSize: values.length
}).then((res) => {
if (res.success) {
this.selectionRows = []
let selectedRowKeys = []
let realNames = []
res.result.records.forEach(user => {
realNames.push(user['realname'])
let param = {[this.store]: values}
getAction('/sys/user/getMultiUser', param).then((list)=>{
this.selectionRows = []
let selectedRowKeys = []
let textArray = []
if(list && list.length>0){
for(let user of list){
textArray.push(user[this.text])
selectedRowKeys.push(user['id'])
this.selectionRows.push(user)
})
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', realNames.join(','))
}
}
this.selectedRowKeys = selectedRowKeys
this.$emit('initComp', textArray.join(','))
})
} else {
// JSelectUserByDep组件bug issues/I16634
this.$emit('initComp', '')
// 前端用户选择单选无法置空的问题 #2610
this.selectedRowKeys = []
}
},
async loadData(arg) {
@ -254,7 +271,7 @@
handleSubmit() {
let that = this;
this.getSelectUserRows();
that.$emit('ok', that.selectUserRows, that.selectUserIds);
that.$emit('ok', that.selectUserRows);
that.searchReset(0)
that.close();
},
@ -297,14 +314,35 @@
})
},
queryDepartTree() {
queryDepartTreeList().then((res) => {
//update-begin-author:taoyan date:20211202 for: 异步加载部门树 https://github.com/jeecgboot/jeecg-boot/issues/3196
this.expandedKeys = []
this.departTree = []
queryDepartTreeSync().then((res) => {
if (res.success) {
this.departTree = res.result;
// 默认展开父节点
this.expandedKeys = this.departTree.map(item => item.id)
for (let i = 0; i < res.result.length; i++) {
let temp = res.result[i]
this.departTree.push(temp)
}
}
})
},
onLoadDepartment(treeNode){
return new Promise(resolve => {
queryDepartTreeSync({pid:treeNode.dataRef.id}).then((res) => {
if (res.success) {
//判断chidlren是否为空并修改isLeaf属性值
if(res.result.length == 0){
treeNode.dataRef['isLeaf']=true
return;
}else{
treeNode.dataRef['children']= res.result;
}
}
})
resolve();
});
},
//update-end-author:taoyan date:20211202 for: 异步加载部门树 https://github.com/jeecgboot/jeecg-boot/issues/3196
modalFormOk() {
this.loadData();
}

View File

@ -28,6 +28,9 @@
</template>
<!-- update-end-author:taoyan date:20201221 for:此处删掉transition标签 不知道为什么加上后 页面路由切换的时候即1及菜单切到2及菜单的时候 两个菜单页面会同时出现300-500秒左右 -->
</div>
<!-- update-begin-author:zyf date:20211129 for:qiankun 挂载子应用盒子 -->
<div id="content" class="app-view-box"></div>
<!-- update-end-author:zyf date:20211129 for: qiankun 挂载子应用盒子-->
</global-layout>
</template>
@ -38,6 +41,7 @@
import { triggerWindowResizeEvent } from '@/utils/util'
import Vue from 'vue'
import { CACHE_INCLUDED_ROUTES } from '@/store/mutation-types'
import registerApps from "@/qiankun";
const indexKey = '/dashboard/analysis'
@ -92,6 +96,14 @@
this.activePage = currentRoute.fullPath
},
mounted() {
if (process.env.VUE_APP_QIANKUN == 'true') {
//update-begin-author:zyf date:20211129 for:qiankun 注册子应用
if (!window.qiankunStarted) {
window.qiankunStarted = true;
registerApps();
}
//update-end-author:zyf date:20211129 for:qiankun 注册子应用
}
},
watch: {
'$route': function(newRoute) {
@ -219,10 +231,20 @@
cacheRouterArray.splice(cacheRouterArray.findIndex(item => item === componentName), 1)
Vue.ls.set(CACHE_INCLUDED_ROUTES, cacheRouterArray)
}
this.emitPageClosed(removeRoute[0])
}
//update-end--Author:scott Date:20201015 for路由缓存问题关闭了tab页时再打开就不刷新 #842
},
// 触发 page-closed (页面关闭)全局事件
emitPageClosed(closedRoute) {
this.$root.$emit('page-closed', {
closedRoute,
pageList: this.pageList,
linkList: this.linkList,
activePage: this.activePage
})
},
onContextmenu(e) {
const pagekey = this.getPageKey(e.target)
if (pagekey !== null) {

View File

@ -149,7 +149,8 @@
<!-- update_begin author:sunjianlei date:20190530 for: 选中首页的时候不显示背景颜色 -->
<style lang="less">
.ant-menu.ant-menu-root {
// 选中首页的时候不显示背景颜色,只应用于左侧菜单
.sider .ant-menu.ant-menu-root {
& > .ant-menu-item:first-child {
background-color: transparent;

View File

@ -150,10 +150,10 @@
this.topMenuStyle.headerIndexRight = {}
this.topMenuStyle.headerIndexLeft = {}
} else {
let rightWidth = '360px'
let rightWidth = '400px'
this.topMenuStyle.topNavHeader = { 'min-width': '165px' }
this.topMenuStyle.topSmenuStyle = { 'width': 'calc(100% - 165px)' }
this.topMenuStyle.headerIndexRight = { 'min-width': rightWidth }
this.topMenuStyle.headerIndexRight = { 'min-width': rightWidth, 'white-space': 'nowrap' }
this.topMenuStyle.headerIndexLeft = { 'width': `calc(100% - ${rightWidth})` }
}
}

View File

@ -220,7 +220,9 @@
overflow-y: auto;
}
.ant-table-body {
min-width: 800px;
// update-begin---author:sunjianlei Date:20220104 for 【JTC-480】移动端不支持左右拖动需要注释掉此段代码 ------------
//min-width: 800px;
// update-end---author:sunjianlei Date:20220104 for 【JTC-480】移动端不支持左右拖动需要注释掉此段代码 ------------
}
}
.sidemenu {

View File

@ -1,6 +1,6 @@
<template>
<div class="logo">
<router-link :to="{name:'dashboard'}">
<router-link :to="routerLinkTo">
<!-- update-begin- author:sunjianlei --- date:20190814 --- for: logo颜色根据主题颜色变化 -->
<img v-if="navTheme === 'dark'" src="~@/assets/logo-white.png" alt="logo">
@ -28,7 +28,12 @@
type: Boolean,
default: true,
required: false
}
},
// 点击Logo跳转地址
routerLinkTo: {
type: Object,
default: () => ({name: 'dashboard'}),
},
}
}
</script>

View File

@ -23,6 +23,7 @@
</template>
<script>
import {getUserList} from '@/api/api'
export default {
name: "SysAnnouncementModal",
components: {
@ -59,6 +60,15 @@
},
methods: {
detail (record) {
//update-begin---author:wangshuai ---date:20220107 for将其它页面传递过来的用户名改成用户真实姓名
if(record.sender){
getUserList({"username":record.sender}).then((res) =>{
if(res.success && res.result.records.length>0){
record.sender = res.result.records[0].realname
}
})
}
//update-end---author:wangshuai ---date:20220107 for将其它页面传递过来的用户名改成用户真实姓名
this.visible = true;
this.record = record;
},

View File

@ -183,10 +183,10 @@
content: '真的要注销登录吗 ?',
onOk() {
return that.Logout({}).then(() => {
// update-begin author:wangshuai date:20200601 for: 退出登录跳转登录页面
that.$router.push({ path: '/user/login' });
// update-begin author:scott date:20211223 for:【JTC-198】退出登录体验不好
//that.$router.push({ path: '/user/login' });
window.location.reload()
// update-end author:wangshuai date:20200601 for: 退出登录跳转登录页面
// update-end author:scott date:20211223 for:【JTC-198】退出登录体验不好
}).catch(err => {
that.$message.error({
title: '错误',
@ -225,17 +225,13 @@
// update_begin author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题
searchMethods(value) {
let route = this.searchMenuOptions.filter(item => item.id === value)[0]
//update-begin-author:taoyan date:20210528 for: 【菜单问题】配置一个iframe地址的菜单内部打开在搜索菜单上打开却新开了一个窗口
if (route.meta.internalOrExternal === true) {
window.open(route.meta.url, '_blank')
} else {
if(route.component.includes('layouts/IframePageView')){
this.$router.push(route)
}else{
this.$router.push({ path: route.path })
}
//update-begin-author:sunjianlei date:20220111 for: 【JTC-702】【菜单搜索】菜单搜索里点击跳转的菜单无法将Token信息传递过去
if(route.component.includes('layouts/IframePageView')){
this.$router.push(route)
}else{
this.$router.push({ path: route.path })
}
//update-end-author:taoyan date:20210528 for: 【菜单问题】配置一个iframe地址的菜单内部打开在搜索菜单上打开却新开了一个窗口
//update-end-author:sunjianlei date:20220111 for: 【JTC-702】【菜单搜索】菜单搜索里点击跳转的菜单无法将Token信息传递过去
this.searchMenuVisible = false
},
// update_end author:sunjianlei date:20191230 for: 解决外部链接打开失败的问题

View File

@ -1,10 +1,10 @@
/** init domain config */
import Vue from 'vue'
//设置全局API_BASE_URL
Vue.prototype.API_BASE_URL = process.env.VUE_APP_API_BASE_URL
Vue.prototype.API_BASE_URL = window._CONFIG.VUE_APP_API_BASE_URL?window._CONFIG.VUE_APP_API_BASE_URL:process.env.VUE_APP_API_BASE_URL
window._CONFIG['domianURL'] = Vue.prototype.API_BASE_URL
//单点登录地址
window._CONFIG['casPrefixUrl'] = process.env.VUE_APP_CAS_BASE_URL
window._CONFIG['onlinePreviewDomainURL'] = process.env.VUE_APP_ONLINE_BASE_URL
window._CONFIG['casPrefixUrl'] = window._CONFIG.VUE_APP_CAS_BASE_URL?window._CONFIG.VUE_APP_CAS_BASE_URL:process.env.VUE_APP_CAS_BASE_URL
window._CONFIG['onlinePreviewDomainURL'] = window._CONFIG.VUE_APP_ONLINE_BASE_URL?window._CONFIG.VUE_APP_ONLINE_BASE_URL:process.env.VUE_APP_ONLINE_BASE_URL
window._CONFIG['staticDomainURL'] = Vue.prototype.API_BASE_URL + '/sys/common/static'
window._CONFIG['pdfDomainURL'] = Vue.prototype.API_BASE_URL+ '/sys/common/pdf/pdfPreviewIframe'

View File

@ -347,6 +347,21 @@ export const constantRouterMap = [
// ]
// },
{
// OAuth2 APP页面路由
path: '/oauth2-app',
component: BlankLayout,
redirect: '/oauth2-app/login',
children: [
{
// OAuth2 登录路由
path: 'login',
name: 'oauth2-app-login',
component: () => import(/* webpackChunkName: "oauth2-app.login" */ '@/views/user/oauth2/OAuth2Login')
},
]
},
{
path: '/test',
component: BlankLayout,

View File

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

View File

@ -53,6 +53,7 @@ export const JEditableTableMixin = {
this.tableReset();
resolve();
}).then(() => {
if (typeof this.addBefore === 'function') this.addBefore()
// 默认新增空数据
let rowNum = this.addDefaultRowNum
if (typeof rowNum !== 'number') {

View File

@ -51,6 +51,7 @@ export const JEditableTableModelMixin = {
this.tableReset();
resolve();
}).then(() => {
if (typeof this.addBefore === 'function') this.addBefore()
// 默认新增空数据
let rowNum = this.addDefaultRowNum
if (typeof rowNum !== 'number') {

View File

@ -8,7 +8,6 @@ import { deleteAction, getAction,downFile,getFileAccessHttpUrl } from '@/api/man
import Vue from 'vue'
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
import store from '@/store'
import {Modal} from 'ant-design-vue'
export const JeecgListMixin = {
data(){
@ -94,11 +93,11 @@ export const JeecgListMixin = {
this.ipagination.total = 0;
}
//update-end---author:zhangyafei Date:20201118 for适配不分页的数据列表------------
}
if(res.code===510){
}else{
this.$message.warning(res.message)
}
this.loading = false;
}).finally(() => {
this.loading = false
})
},
initDictConfig(){
@ -148,6 +147,10 @@ export const JeecgListMixin = {
},
searchQuery() {
this.loadData(1);
// 点击查询清空列表选中行
// https://gitee.com/jeecg/jeecg-boot/issues/I4KTU1
this.selectedRowKeys = []
this.selectionRows = []
},
superQuery() {
this.$refs.superQueryModal.show();
@ -296,10 +299,12 @@ export const JeecgListMixin = {
},
/* 导入 */
handleImportExcel(info){
this.loading = true;
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
if (info.file.status === 'done') {
this.loading = false;
if (info.file.response.success) {
// this.$message.success(`${info.file.name} 文件上传成功`);
if (info.file.response.code === 201) {
@ -321,11 +326,12 @@ export const JeecgListMixin = {
this.$message.error(`${info.file.name} ${info.file.response.message}.`);
}
} else if (info.file.status === 'error') {
this.loading = false;
if (info.file.response.status === 500) {
let data = info.file.response
const token = Vue.ls.get(ACCESS_TOKEN)
if (token && data.message.includes("Token失效")) {
Modal.error({
this.$error({
title: '登录已过期',
content: '很抱歉登录已过期请重新登录',
okText: '重新登录',

View File

@ -1,10 +1,12 @@
import { formatDate } from '@/utils/util'
import Area from '@/components/_util/Area'
import { postAction } from '@/api/manage'
const onlUtil = {
data(){
return {
mixin_pca:''
mixin_pca:'',
flowCodePre: 'onl_'
}
},
created(){

View File

@ -4,19 +4,20 @@ import store from './store'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import notification from 'ant-design-vue/es/notification'
import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH } from '@/store/mutation-types'
import { generateIndexRouter } from "@/utils/util"
import { ACCESS_TOKEN,INDEX_MAIN_PAGE_PATH, OAUTH2_LOGIN_PAGE_PATH } from '@/store/mutation-types'
import { generateIndexRouter, isOAuth2AppEnv } from '@/utils/util'
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/user/login', '/user/register', '/user/register-result','/user/alteration'] // no redirect whitelist
whiteList.push(OAUTH2_LOGIN_PAGE_PATH)
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (Vue.ls.get(ACCESS_TOKEN)) {
/* has token */
if (to.path === '/user/login') {
if (to.path === '/user/login' || to.path === OAUTH2_LOGIN_PAGE_PATH) {
next({ path: INDEX_MAIN_PAGE_PATH })
NProgress.done()
} else {
@ -59,10 +60,18 @@ router.beforeEach((to, from, next) => {
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
// 在免登录白名单,直接进入
next()
// 在免登录白名单,如果进入的页面是login页面并且当前是OAuth2app环境就进入OAuth2登录页面
if (to.path === '/user/login' && isOAuth2AppEnv()) {
next({path: OAUTH2_LOGIN_PAGE_PATH})
} else {
// 在免登录白名单,直接进入
next()
}
NProgress.done()
} else {
next({ path: '/user/login', query: { redirect: to.fullPath } })
// 如果当前是在OAuth2APP环境就跳转到OAuth2登录页面
let path = isOAuth2AppEnv() ? OAUTH2_LOGIN_PAGE_PATH : '/user/login'
next({ path: path, query: { redirect: to.fullPath } })
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}

View File

@ -0,0 +1,22 @@
/**
*微应用apps
* @name: 微应用名称 - 具有唯一性
* @entry: 微应用入口.必选 - 通过该地址加载微应用,
* @container: 微应用挂载节点 - 微应用加载完成后将挂载在该节点上
* @activeRule: 微应用触发的路由规则 - 触发路由规则后将加载该微应用
*/
//子应用列表
const _apps = [];
for (const key in process.env) {
if (key.includes('VUE_APP_SUB_')) {
const name = key.split('VUE_APP_SUB_')[1];
const obj = {
name,
entry: process.env[key],
container: '#content',
activeRule: name,
};
_apps.push(obj)
}
}
export const apps = _apps;

View File

@ -0,0 +1,68 @@
/**
* qiankun配置
*/
import {registerMicroApps, setDefaultMountApp, start, runAfterFirstMounted, addGlobalUncaughtErrorHandler} from 'qiankun';
import {apps} from './apps';
import {getProps, initGlState} from './state';
/**
* 重构apps
*/
function filterApps() {
apps.forEach((item) => {
//主应用需要传递给微应用的数据。
item.props = getProps();
//微应用触发的路由规则
item.activeRule = genActiveRule('/' + item.activeRule);
});
return apps;
}
/**
* 路由监听
* @param {*} routerPrefix 前缀
*/
function genActiveRule(routerPrefix) {
return location => location.pathname.startsWith(routerPrefix);
}
/**
* 微应用注册
*/
function registerApps() {
const _apps = filterApps();
registerMicroApps(_apps,
{
beforeLoad: [
loadApp => {
console.log('before load', loadApp);
}
],
beforeMount: [
mountApp => {
console.log('before mount', mountApp);
}
],
afterMount: [
mountApp => {
console.log('before mount', mountApp);
}
],
afterUnmount: [
unloadApp => {
console.log('after unload', unloadApp);
}
]
});
// 设置默认子应用,与 genActiveRule中的参数保持一致
// setDefaultMountApp();
// 第一个微应用 mount 后需要调用的方法,比如开启一些监控或者埋点脚本。
runAfterFirstMounted(() => console.log('开启监控'));
// 添加全局的未捕获异常处理器。
addGlobalUncaughtErrorHandler(event => console.log(event));
// 定义全局状态
initGlState();
//启动qiankun
start({});
}
export default registerApps;

View File

@ -0,0 +1,41 @@
/**
*公共数据
*/
import {initGlobalState} from 'qiankun';
import store from '@/store';
import router from '@/router';
import Vue from 'vue';
import {ACCESS_TOKEN} from "@/store/mutation-types"
//定义传入子应用的数据
export function getProps() {
return {
data: {
publicPath: process.env.BASE_URL,
token: Vue.ls.get(ACCESS_TOKEN),
store,
router
}
}
}
/**
* 定义全局状态,并返回通信方法,在主应用使用,微应用通过 props 获取通信方法。
* @param state 主应用穿的公共数据
*/
export function initGlState(info = {userName: 'admin'}) {
// 初始化state
const actions = initGlobalState(info);
// 设置新的值
actions.setGlobalState(info);
// 注册 观察者 函数 - 响应 globalState 变化,在 globalState 发生改变时触发该 观察者 函数。
actions.onGlobalStateChange((newState, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.info("newState", newState)
for (const key in newState) {
console.info("onGlobalStateChange", key)
}
});
// 将action对象绑到Vue原型上为了项目中其他地方使用方便
Vue.prototype.$actions = actions;
}

View File

@ -6,8 +6,8 @@ const getters = {
color: state => state.app.color,
token: state => state.user.token,
avatar: state => {state.user.avatar = Vue.ls.get(USER_INFO).avatar; return state.user.avatar},
username: state => state.user.username,
nickname: state => {state.user.realname = Vue.ls.get(USER_INFO).realname; return state.user.realname},
username: state => state.user.info.username,
nickname: state => {state.user.info.realname = Vue.ls.get(USER_INFO).realname; return state.user.info.realname},
welcome: state => state.user.welcome,
permissionList: state => state.user.permissionList,
userInfo: state => {state.user.info = Vue.ls.get(USER_INFO); return state.user.info},
@ -16,7 +16,8 @@ const getters = {
enhanceJs:(state) => (code) => {
state.enhance.enhanceJs[code] = Vue.ls.get(ENHANCE_PRE+code);
return state.enhance.enhanceJs[code]
}
},
sysSafeMode: state => state.user.sysSafeMode,
}

View File

@ -16,7 +16,7 @@ export default new Vuex.Store({
user,
permission,
enhance,
online
online,
},
state: {

View File

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

View File

@ -17,6 +17,7 @@ export const ENCRYPTED_STRING = 'ENCRYPTED_STRING'
export const ENHANCE_PRE = 'enhance_'
export const UI_CACHE_DB_DICT_DATA = 'UI_CACHE_DB_DICT_DATA'
export const INDEX_MAIN_PAGE_PATH = '/dashboard/analysis'
export const OAUTH2_LOGIN_PAGE_PATH = '/oauth2-app/login'
export const TENANT_ID = 'TENANT_ID'
export const ONL_AUTH_FIELDS = 'ONL_AUTH_FIELDS'
//路由缓存问题关闭了tab页时再打开就不刷新 #842

View File

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

View File

@ -20,3 +20,11 @@ export function demoFieldDefVal_getAddress(arg) {
}
return `北京市 ${arg}`
}
/** 自定义JS函数示例 */
export function sayHi(name) {
if (!name) {
name = '张三'
}
return `您好我叫 ${name}`
}

View File

@ -49,12 +49,14 @@ export default class signMd5Utils {
result = {};
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
//【这边条件没有encode】带条件参数例子/sys/dict/getDictItems/sys_user,realname,id,username!='admin'%20order%20by%20create_time
let lastpathVariable = url.substring(url.lastIndexOf('/') + 1);
if(lastpathVariable.includes(",")){
if(lastpathVariable.includes("?")){
lastpathVariable = lastpathVariable.substring(0, lastpathVariable.indexOf("?"));
}
result["x-path-variable"] = lastpathVariable;
//解决Sign 签名校验失败 #2728
result["x-path-variable"] = decodeURIComponent(lastpathVariable);
}
if (urlArray && urlArray[1]) {
let paramString = urlArray[1], paramResult;

View File

@ -2,7 +2,7 @@ import Vue from 'vue'
import axios from 'axios'
import store from '@/store'
import { VueAxios } from './axios'
import {Modal, notification} from 'ant-design-vue'
import router from '@/router/index'
import { ACCESS_TOKEN, TENANT_ID } from "@/store/mutation-types"
/**
@ -28,7 +28,7 @@ const err = (error) => {
console.log("------异常响应------",error.response.status)
switch (error.response.status) {
case 403:
notification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '拒绝访问',duration: 4})
break
case 500:
console.log("------error.response------",error.response)
@ -39,40 +39,43 @@ const err = (error) => {
break;
}
// update-end- --- author:liusq ------ date:20200910 ---- for:处理Blob情况----
//notification.error({ message: '系统提示', description:'Token失效请重新登录!',duration: 4})
if(token && data.message.includes("Token失效")){
// update-begin- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转----
Modal.error({
title: '登录已过期',
content: '很抱歉登录已过期请重新登录',
okText: '重新登录',
mask: false,
onOk: () => {
store.dispatch('Logout').then(() => {
Vue.ls.remove(ACCESS_TOKEN)
try {
let path = window.document.location.pathname
console.log("location pathname -> "+path)
if(path!="/" && path.indexOf('/user/login')==-1){
if (/wxwork|dingtalk/i.test(navigator.userAgent)) {
Vue.prototype.$Jmessage.loading('登录已过期正在重新登陆', 0)
} else {
Vue.prototype.$Jmodal.error({
title: '登录已过期',
content: '很抱歉登录已过期请重新登录',
okText: '重新登录',
mask: false,
onOk: () => {
store.dispatch('Logout').then(() => {
Vue.ls.remove(ACCESS_TOKEN)
try {
let path = window.document.location.pathname
console.log('location pathname -> ' + path)
if (path != '/' && path.indexOf('/user/login') == -1) {
window.location.reload()
}
} catch (e) {
window.location.reload()
}
}catch (e) {
window.location.reload()
}
})
}
})
})
}
})
}
// update-end- --- author:scott ------ date:20190225 ---- for:Token失效采用弹框模式不直接跳转----
}
break
case 404:
notification.error({ message: '系统提示', description:'很抱歉资源未找到!',duration: 4})
Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉资源未找到!',duration: 4})
break
case 504:
notification.error({ message: '系统提示', description: '网络超时'})
Vue.prototype.$Jnotification.error({ message: '系统提示', description: '网络超时'})
break
case 401:
notification.error({ message: '系统提示', description:'未授权请重新登录',duration: 4})
Vue.prototype.$Jnotification.error({ message: '系统提示', description:'很抱歉登录已过期请重新登录',duration: 4})
if (token) {
store.dispatch('Logout').then(() => {
setTimeout(() => {
@ -82,13 +85,19 @@ const err = (error) => {
}
break
default:
notification.error({
Vue.prototype.$Jnotification.error({
message: '系统提示',
description: data.message,
duration: 4
})
break
}
} else if (error.message) {
if (error.message.includes('timeout')) {
Vue.prototype.$Jnotification.error({message: '系统提示', description: '网络超时'})
} else {
Vue.prototype.$Jnotification.error({message: '系统提示', description: error.message})
}
}
return Promise.reject(error)
};
@ -99,6 +108,14 @@ service.interceptors.request.use(config => {
if (token) {
config.headers[ 'X-Access-Token' ] = token // 让每个请求携带自定义 token 请根据实际情况自行修改
}
// update-begin--author:sunjianlei---date:20200723---for 如果当前在low-app环境并且携带了appId就向Header里传递appId
const $route = router.currentRoute
if ($route && $route.name && $route.name.startsWith('low-app') && $route.params.appId) {
config.headers['X-Low-App-ID'] = $route.params.appId
}
// update-end--author:sunjianlei---date:20200723---for 如果当前在low-app环境并且携带了appId就向Header里传递appId
//update-begin-author:taoyan date:2020707 for:多租户
let tenantid = Vue.ls.get(TENANT_ID)
if (!tenantid) {

View File

@ -1,5 +1,7 @@
import Vue from 'vue'
import * as api from '@/api/api'
import { isURL } from '@/utils/validate'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import onlineCommons from '@jeecg/antd-online-mini'
export function timeFix() {
@ -81,19 +83,19 @@ export function formatDate(value, fmt) {
// 生成首页路由
export function generateIndexRouter(data) {
let indexRouter = [{
path: '/',
name: 'dashboard',
//component: () => import('@/components/layouts/BasicLayout'),
component: resolve => require(['@/components/layouts/TabLayout'], resolve),
meta: { title: '首页' },
redirect: '/dashboard/analysis',
children: [
...generateChildRouters(data)
]
},{
"path": "*", "redirect": "/404", "hidden": true
}]
let indexRouter = [{
path: '/',
name: 'dashboard',
//component: () => import('@/components/layouts/BasicLayout'),
component: resolve => require(['@/components/layouts/TabLayout'], resolve),
meta: { title: '首页' },
redirect: '/dashboard/analysis',
children: [
...generateChildRouters(data)
]
},{
"path": "*", "redirect": "/404", "hidden": true
}]
return indexRouter;
}
@ -104,9 +106,9 @@ function generateChildRouters (data) {
for (let item of data) {
let component = "";
if(item.component.indexOf("layouts")>=0){
component = "components/"+item.component;
component = "components/"+item.component;
}else{
component = "views/"+item.component;
component = "views/"+item.component;
}
// eslint-disable-next-line
@ -145,6 +147,7 @@ function generateChildRouters (data) {
component: componentPath,
//component: resolve => require(['@/' + component+'.vue'], resolve),
hidden:item.hidden,
//component:()=> import(`@/views/${item.component}.vue`),
meta: {
title:item.meta.title ,
icon: item.meta.icon,
@ -203,7 +206,7 @@ export function randomNumber() {
}
if (arguments.length === 1) {
let [length] = arguments
// 生成指定长度的随机数字,首位一定不是 0
// 生成指定长度的随机数字,首位一定不是 0
let nums = [...Array(length).keys()].map((i) => (i > 0 ? random(0, 9) : random(1, 9)))
return parseInt(nums.join(''))
} else if (arguments.length >= 2) {
@ -560,3 +563,67 @@ export function removeArrayElement(array, prod, value) {
array.splice(index, 1);
}
}
/** 判断是否是OAuth2APP环境 */
export function isOAuth2AppEnv() {
return /wxwork|dingtalk/i.test(navigator.userAgent)
}
/**
* 获取积木报表打印地址
* @param url
* @param id
* @param open 是否自动打开
* @returns {*}
*/
export function getReportPrintUrl(url, id, open) {
// URL支持{{ window.xxx }}占位符变量
url = url.replace(/{{([^}]+)?}}/g, (s1, s2) => eval(s2))
if (url.includes('?')) {
url += '&'
} else {
url += '?'
}
url += `id=${id}`
url += `&token=${Vue.ls.get(ACCESS_TOKEN)}`
if (open) {
window.open(url)
}
return url
}
/**
* JS实现AOP切面
*
* @param obj 包含函数的对象
* @param funcName 要切面的函数名
* @param callback 执行方法前的回调用于切面callback的返回值就是funcName最终的返回值
*/
export function aspectAroundFunction(obj, funcName, callback) {
if (typeof callback !== 'function' || !obj) {
console.warn('aspectAroundFunctionobj或callback格式不正确')
return
}
// 保存原来的函数
let func = obj[funcName]
if (typeof func !== 'function') {
console.warn('aspectAroundFunction' + funcName + '不是一个方法')
return
}
// 赋值新方法
// 实现当外部调用 funcName 时,首先调用我定义的新方法
// 然后调用传入的callback方法以决定是否执行 funcName以及更改参数、返回值
obj[funcName] = function (...args) {
return callback({
args,
// 只有执行 proceed 才会真正执行给定的 funcName 方法
proceed() {
try {
return func.apply(obj, args)
} catch (e) {
console.error(e)
}
},
})
}
}

View File

@ -1,5 +1,5 @@
<template>
<a-modal :visible="visible" title="修改头像" :maskClosable="false" :confirmLoading="confirmLoading" :width="800">
<a-modal :visible="visible" title="修改头像" :maskClosable="false" :confirmLoading="confirmLoading" :width="800" @cancel="cancelHandel">
<a-row>
<a-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper

View File

@ -32,6 +32,7 @@
size="default"
:columns="columns"
:data="loadData"
:scroll="{}"
>
<div
slot="expandedRowRender"

View File

@ -31,6 +31,7 @@
size="default"
:columns="columns"
:data="loadData"
:scroll="{}"
>
<div
slot="expandedRowRender"

View File

@ -8,123 +8,155 @@
:height="484"
:dataSource="dataSource"
:columns="columns"
@valueChange="handleValueChange"
:linkage-config="linkageConfig"
/>
</template>
<script>
import moment from 'moment'
import { randomNumber, randomUUID } from '@/utils/util'
import { JVXETypes } from '@/components/jeecg/JVxeTable'
import { JVXETypes } from '@/components/jeecg/JVxeTable'
import { getAction } from '@api/manage'
export default {
name: 'JVxeDemo2',
data() {
return {
columns: [
{
title: '/直辖市/自治区',
key: 's1',
type: JVXETypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
},
{
title: '市',
key: 's2',
type: JVXETypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
},
{
title: '/',
key: 's3',
type: JVXETypes.select,
width: '240px',
options: [],
placeholder: '请选择${title}'
}
],
dataSource: [],
mockData: [
{ text: '北京市', value: '110000', parent: null },
{ text: '天津市', value: '120000', parent: null },
{ text: '河北省', value: '130000', parent: null },
{ text: '上海市', value: '310000', parent: null },
{ text: '北京市', value: '110100', parent: '110000' },
{ text: '天津市市', value: '120100', parent: '120000' },
{ text: '石家庄市', value: '130100', parent: '130000' },
{ text: '唐山市', value: '130200', parent: '130000' },
{ text: '秦皇岛市', value: '130300', parent: '130000' },
{ text: '上海市', value: '310100', parent: '310000' },
{ text: '东城区', value: '110101', parent: '110100' },
{ text: '西城区', value: '110102', parent: '110100' },
{ text: '朝阳区', value: '110105', parent: '110100' },
{ text: '和平区', value: '120101', parent: '120100' },
{ text: '河东区', value: '120102', parent: '120100' },
{ text: '河西区', value: '120103', parent: '120100' },
{ text: '黄浦区', value: '310101', parent: '310100' },
{ text: '徐汇区', value: '310104', parent: '310100' },
{ text: '长宁区', value: '310105', parent: '310100' },
{ text: '长安区', value: '130102', parent: '130100' },
{ text: '桥西区', value: '130104', parent: '130100' },
{ text: '新华区', value: '130105', parent: '130100' },
{ text: '路南区', value: '130202', parent: '130200' },
{ text: '路北区', value: '130203', parent: '130200' },
{ text: '古冶区', value: '130204', parent: '130200' },
{ text: '海港区', value: '130302', parent: '130300' },
{ text: '山海关区', value: '130303', parent: '130300' },
{ text: '北戴河区', value: '130304', parent: '130300' },
]
}
},
created() {
// 初始化数据
this.columns[0].options = this.request(null)
},
methods: {
request(parentId) {
return this.mockData.filter(i => i.parent === parentId)
},
/** 当选项被改变时,联动其他组件 */
handleValueChange(event) {
const { type, row, column, value, target } = event
console.log("event",event)
if (type === JVXETypes.select) {
// 第一列
if (column.key === 's1') {
// 设置第二列的 options
console.log('this.request(value)::',this.request(value))
target.$refs.vxe.columns[3].options = this.request(value)
// 清空后两列的数据
target.setValues([{
rowKey: row.id,
values: { s2: '', s3: '' }
}])
target.$refs.vxe.columns[4].options = []
} else
// 第二列
if (column.key === 's2') {
target.$refs.vxe.columns[4].options = this.request(value)
target.setValues([{
rowKey: row.id,
values: { s3: '' }
}])
}
export default {
name: 'JVxeDemo2',
data() {
return {
// 联动配置
linkageConfig: [
{requestData: this.requestData, key: 's1'},
// 可配置多个联动
{requestData: this.loadMenu, key: 'menu1',},
],
columns: [
{
title: '性别',
key: 'sex',
type: JVXETypes.select,
dictCode: 'sex',
width: '180px',
placeholder: '请选择${title}',
},
{
title: '/直辖市/自治区',
key: 's1',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 's2',
},
{
title: '市',
key: 's2',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 's3',
},
{
title: '/',
key: 's3',
type: JVXETypes.select,
width: '180px',
options: [],
placeholder: '请选择${title}',
},
{
title: '一级菜单',
key: 'menu1',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'menu2',
},
{
title: '二级菜单',
key: 'menu2',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
// 联动字段(即下一级的字段)
linkageKey: 'menu3',
},
{
title: '三级菜单',
key: 'menu3',
type: JVXETypes.select,
width: '180px',
placeholder: '请选择${title}',
}
],
dataSource: [
{sex: '1', s1: '110000', s2: '110100', s3: '110101'},
{sex: '2', s1: '130000', s2: '130300', s3: '130303'},
],
// 模拟数据
mockData: [
{text: '北京市', value: '110000', parent: ''},
{text: '天津市', value: '120000', parent: ''},
{text: '河北省', value: '130000', parent: ''},
{text: '上海市', value: '310000', parent: ''},
}
{text: '北京市', value: '110100', parent: '110000'},
{text: '天津市市', value: '120100', parent: '120000'},
{text: '石家庄市', value: '130100', parent: '130000'},
{text: '唐山市', value: '130200', parent: '130000'},
{text: '秦皇岛市', value: '130300', parent: '130000'},
{text: '上海市', value: '310100', parent: '310000'},
{text: '东城区', value: '110101', parent: '110100'},
{text: '西城区', value: '110102', parent: '110100'},
{text: '朝阳区', value: '110105', parent: '110100'},
{text: '和平区', value: '120101', parent: '120100'},
{text: '河东区', value: '120102', parent: '120100'},
{text: '河西区', value: '120103', parent: '120100'},
{text: '黄浦区', value: '310101', parent: '310100'},
{text: '徐汇区', value: '310104', parent: '310100'},
{text: '长宁区', value: '310105', parent: '310100'},
{text: '长安区', value: '130102', parent: '130100'},
{text: '桥西区', value: '130104', parent: '130100'},
{text: '新华区', value: '130105', parent: '130100'},
{text: '路南区', value: '130202', parent: '130200'},
{text: '路北区', value: '130203', parent: '130200'},
{text: '古冶区', value: '130204', parent: '130200'},
{text: '海港区', value: '130302', parent: '130300'},
{text: '山海关区', value: '130303', parent: '130300'},
{text: '北戴河区', value: '130304', parent: '130300'},
],
}
},
methods: {
/**
* 模拟从后台查询数据
*/
requestData(parent) {
return new Promise((resolve, reject) => {
let data = this.mockData.filter(i => i.parent === parent)
setTimeout(() => {
resolve(data)
}, 500)
})
},
async loadMenu(parent) {
let res
// 如果parent为空则查询第一级菜单
if (parent === '') {
res = await getAction('/sys/permission/getSystemMenuList')
} else {
res = await getAction('/sys/permission/getSystemSubmenu', {parentId: parent})
}
if (res.success) {
// 返回的数据里必须包含 value 和 text 字段
return res.result.map(item => ({value: item.id, text: item.name}))
}
this.$message.warning('loadMenu失败' + res.message)
return []
},
}
}
</script>
<style scoped>

View File

@ -254,7 +254,7 @@
<a-row :gutter="24">
<a-col :span="12">
<a-form-model-item label="树字典" prop="treeDict">
<j-tree-dict v-model="formData.treeDict" placeholder="请选择树字典" parentCode="A01" />
<j-tree-dict v-model="formData.treeDict" placeholder="请选择树字典" parentCode="B01" />
</a-form-model-item>
</a-col>
<a-col :span="12">选中的值(v-model){{ formData.treeDict }}</a-col>
@ -268,6 +268,7 @@
placeholder="请选择菜单"
dict="sys_permission,name,id"
pidField="parent_id"
hasChildField="is_leaf"
pidValue=""
/>
</a-form-model-item>
@ -283,6 +284,7 @@
placeholder="请选择菜单"
dict="sys_permission,name,id"
pidField="parent_id"
hasChildField="is_leaf"
pidValue=""
multiple
/>
@ -305,7 +307,7 @@
<a-row :gutter="24">
<a-col :span="12">
<a-form-model-item label="cron表达式" prop="cronExpression">
<j-cron v-model="formData.cronExpression"></j-cron>
<j-easy-cron v-model="formData.cronExpression"></j-easy-cron>
</a-form-model-item>
</a-col>
</a-row>
@ -408,14 +410,11 @@
<!-- 省市县级联 -->
<a-row :gutter="24">
<a-col :span="12">
<a-form-model-item label="省市县级联" prop="areaLinkage2">
<j-area-linkage v-model="formData.areaLinkage2" type="select"/>
</a-form-model-item>
</a-col>
<a-col :span="12">输入的值(v-model){{ formData.areaLinkage2 }}</a-col>
<a-form-model-item label="省市县级联" prop="areaLinkage2">
<j-area-linkage v-model="formData.areaLinkage2" type="select" style="float: left"/>
<span style="margin-left: 25px">输入的值(v-model){{ formData.areaLinkage2 }}</span>
</a-form-model-item>
</a-row>
<!-- 功能示例:关闭当前页面 -->
<a-row :gutter="24">
<a-col :span="12">
@ -457,6 +456,7 @@
import JSelectMultiple from '@/components/jeecg/JSelectMultiple'
import JTreeDict from "../../components/jeecg/JTreeDict.vue";
import JCron from "@/components/jeecg/JCron.vue";
import JEasyCron from "@/components/jeecg/JEasyCron";
import JTreeSelect from '@/components/jeecg/JTreeSelect'
import JSuperQuery from '@/components/jeecg/JSuperQuery'
import JUpload from '@/components/jeecg/JUpload'
@ -489,7 +489,7 @@
JCheckbox,
JCodeEditor,
JDate, JEditor, JEllipsis, JSlider, JSelectMultiple,
JCron, JTreeSelect, JSuperQuery, JMultiSelectTag,
JCron, JEasyCron,JTreeSelect, JSuperQuery, JMultiSelectTag,
JSearchSelectTag
},
data() {

View File

@ -0,0 +1,231 @@
<template>
<a-card :bordered="false">
<a-row>
<a-col>
<a-switch v-bind="pageSwitchProps" v-model="pageSwitch"/>
</a-col>
<a-col>
<a-table
v-bind="tableProps"
@change="handleTableChange"
style="margin-top: 20px"
>
</a-table>
</a-col>
</a-row>
</a-card>
</template>
<script>
export default {
name: 'TableTotal',
data() {
return {
columns: [
{
title: '#',
width: '180px',
align: 'center',
dataIndex: 'rowIndex',
customRender: function (text, r, index) {
return (text !== '总计') ? (parseInt(index) + 1) : text
}
},
{
title: '姓名',
dataIndex: 'name',
width: 180,
},
{
title: '贡献点',
dataIndex: 'point',
width: 180,
},
{
title: '等级',
dataIndex: 'level',
width: 180,
},
{
title: '更新时间',
dataIndex: 'updateTime',
width: 180,
},
],
/* 分页参数 */
ipagination:{
current: 1,
pageSize: 10,
},
dataSource: [
{ id:"1",name: '张三', point: 23, level: 3, updateTime: '2019-8-14' },
{ name: '小王', point: 6, level: 1, updateTime: '2019-8-13' },
{ name: '李四', point: 53, level: 8, updateTime: '2019-8-12' },
{ name: '小红', point: 44, level: 5, updateTime: '2019-8-11' },
{ name: '王五', point: 97, level: 10, updateTime: '2019-8-10' },
{ name: '小明', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小张', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小六', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小五', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小赵', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '李华', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小康', point: 33, level: 2, updateTime: '2019-8-10' },
{ name: '小鹿', point: 33, level: 2, updateTime: '2019-8-10' },
],
newArr:[],
newDataSource:[],
footerDataSource: [],
pageSwitch:true
}
},
computed:{
// 数据表格的固定属性
tableProps(){
let tableProps = {
size: 'middle',
rowKey:'rowIndex',
columns: this.columns,
scroll: {x: true},
}
let renderFooter = this.footerDataSource.length === 0 ? null : () => this.renderTableFooter(tableProps)
return {
...tableProps,
ref: 'table',
class: 'chart-data-list',
pagination:this.pageSwitch?this.ipagination:false,
columns: this.columns,
dataSource: this.dataSource,
footer: renderFooter,
}
},
pageSwitchProps() {
return {
checkedChildren: '分页',
unCheckedChildren: '分页',
style: {
position: 'absolute',
right: '0px',
top: '-10px'
}
}
},
},
mounted() {
// this.tableAddTotalRow(this.columns, this.dataSource)
/*新增分页合计方法*/
this.newDataSource=this.dataSource
this.dataHandling(1,this.ipagination.pageSize)
},
watch:{
//update-begin---author:wangshuai ---date:20220209 for[JTC-494]常用示例->表格合计写法改成新的写法------------
'pageSwitch':function(val){
if(!val){
this.dataHandling('-1',0)
}else{
this.dataHandling(1,this.ipagination.pageSize)
}
},
'ipagination.current':function(val) {
this.dataHandling(val,this.ipagination.pageSize)
},
//当合计行变化时,绑定滚动条
'footerDataSource': {
async handler(dataSource) {
// 当底部合计行有值,并且显示出来时,再同步滚动条
if (dataSource && dataSource.length > 0) {
await this.$nextTick()
// 同步表与footer滚动
let dom = this.$refs.table.$el.querySelectorAll('.ant-table-body')[0]
let footerDom = this.$refs.footerTable.$el.querySelectorAll('.ant-table-body')[0]
dom.addEventListener(
'scroll',
() => {
footerDom.scrollLeft = dom.scrollLeft
},
true,
)
}
},
//update-end---author:wangshuai ---date:20220209 for[JTC-494]常用示例->表格合计写法改成新的写法------------
}
},
methods: {
/** 表格增加合计行 */
tableAddTotalRow(columns, dataSource) {
let numKey = 'rowIndex'
let totalRow = { [numKey]: '合计' }
columns.forEach(column => {
let { key, dataIndex } = column
if (![key, dataIndex].includes(numKey)) {
let total = 0
dataSource.forEach(data => {
total += /^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN
console.log(data[dataIndex], ':', (/^\d+\.?\d?$/.test(data[dataIndex]) ? Number.parseInt(data[dataIndex]) : Number.NaN))
})
if (Number.isNaN(total)) {
total = '-'
}
totalRow[dataIndex] = total
}
})
dataSource.push(totalRow)
},
handleTableChange(pagination, filters, sorter) {
this.ipagination = pagination;
},
//update-begin---author:wangshuai ---date:20220209 for[JTC-494]常用示例->表格合计写法改成新的写法------------
/*如果分页走这个方法*/
dataHandling(pageNo,pageSize) {
//根据当前页数和每页显示条数分割数组
let arrs = [];
//如果pageNo不是-1不分页,那么需要对数据进行分页计算
if(pageNo!=-1){
arrs = this.newDataSource.slice((pageNo-1)*pageSize,pageNo*pageSize)
}else{
arrs = this.newDataSource
}
let newDataSource=[];
let newArr= { };
newArr.rowIndex="总计"
let level=0;
let point=0;
//每一项的数值相加
for (let j=0;j<arrs.length;j++){
level+=arrs[j].level;
point+=arrs[j].point;
}
newArr.level=level;
newArr.point=point;
newDataSource.push(newArr);
//给foot底部数组赋值
this.footerDataSource = newDataSource;
},
// 渲染表格底部合计行
renderTableFooter(tableProps) {
let h = this.$createElement
return h('a-table', {
ref: 'footerTable',
props: {
...tableProps,
pagination: false,
dataSource: this.footerDataSource,
showHeader: false,
},
})
//update-end---author:wangshuai ---date:20220209 for[JTC-494]常用示例->表格合计写法改成新的写法------------
}
}
}
</script>
<style scoped lang="less">
/deep/ .chart-data-list .ant-table-footer .ant-table-body{
overflow: hidden !important;
}
/deep/ .ant-table-footer{
padding:0;
}
</style>

View File

@ -251,8 +251,6 @@
},
delRowCustom (index) {
console.log(index)
let all = this.orderMainModel
all['jeecgOrderCustomerList'].splice(index,1);
this.orderMainModel.jeecgOrderCustomerList.splice(index,1);
this.$forceUpdate();
},
@ -263,8 +261,6 @@
},
delRowTicket (index) {
console.log(index)
let all = this.orderMainModel
all['jeecgOrderTicketList'].splice(index,1);
this.orderMainModel.jeecgOrderTicketList.splice(index,1);
this.$forceUpdate();
},

View File

@ -37,7 +37,9 @@
visible: false,
bodyStyle:{
padding: "0",
height:(window.innerHeight-150)+"px"
//update-begin---author:wangshuai ---date:20220104 for[JTC-411]火狐 分屏 图片大时,与按钮重叠,样式不好------------
height:(window.innerHeight-140)+"px"
//update-begin---author:wangshuai ---date:20220104 for[JTC-411]火狐 分屏 图片大时,与按钮重叠,样式不好------------
},
modalWidth:800,
}

View File

@ -1,6 +1,7 @@
<template>
<a-card title="磁盘监控">
<a-row>
<a-skeleton v-if="loading" active/>
<a-row v-else>
<template v-if="diskInfo && diskInfo.length>0">
<a-col :span="8" v-for="(item,index) in diskInfo" :key=" 'diskInfo'+index ">
<dash-chart-demo :title="item.name" :datasource="item.restPPT"></dash-chart-demo>
@ -23,6 +24,7 @@
},
data() {
return {
loading: true,
description: '磁盘监控',
//数据集
diskInfo:[],
@ -32,6 +34,7 @@
}
},
created() {
this.loading = true
getAction(this.url.queryDiskInfo).then((res)=>{
if(res.success){
for(var i=0;i<res.result.length;i++){
@ -39,7 +42,7 @@
}
this.diskInfo = res.result;
}
})
}).finally(() => this.loading = false)
}
}
</script>

View File

@ -1,7 +1,7 @@
<template>
<a-skeleton active :loading="loading" :paragraph="{rows: 17}">
<a-card>
<!-- Radis 信息实时监控 -->
<!-- Redis 信息实时监控 -->
<a-row :gutter="8">
<a-col :sm="24" :xl="12">
<area-chart-ty v-bind="memory"/>
@ -41,7 +41,7 @@
millisec: 3000,
// Key 实时数量
key: {
title: 'Radis Key 实时数量',
title: 'Redis Key 实时数量',
dataSource: [],
y: '数量',
height: 340,
@ -53,7 +53,7 @@
},
// 内存实时占用情况
memory: {
title: 'Radis 内存实时占用情况KB',
title: 'Redis 内存实时占用情况KB',
dataSource: [],
y: '内存KB',
min: 0,

View File

@ -23,12 +23,13 @@
</a-alert>
<a-input-search @search="onSearch" style="width:100%;margin-top: 10px" placeholder="请输入部门名称"/>
<!-- 树-->
<a-col :md="10" :sm="24">
<template>
<div>
<a-empty v-if="departTree.length===0" description="暂无部门" style="margin-top: 8px;"/>
<template v-else>
<a-dropdown :trigger="[this.dropTrigger]" @visibleChange="dropStatus">
<span style="user-select: none">
<a-tree
v-if="loading"
v-if="treeLoading"
checkable
multiple
@select="onSelect"
@ -49,7 +50,7 @@
</a-menu>
</a-dropdown>
</template>
</a-col>
</div>
</div>
</a-card>
<!---- author:os_chengtgen -- date:20190827 -- for:切换父子勾选模式 =======------>
@ -195,7 +196,7 @@
data() {
return {
iExpandedKeys: [],
loading: true,
treeLoading: true,
autoExpandParent: false,
currFlowId: '',
currFlowName: '',
@ -271,8 +272,10 @@
that.allIds = []
that.iExpandedKeys = []
that.loading = false
//update-begin---author:wangshuai ---date:20220105 for[JTC-364]sqlserver 部门导入导入失败,部门树数据丢失------------
//部门树v-if用到了loading,和上传loading冲突了换一个名称
that.treeLoading = false
//update-end---author:wangshuai ---date:20220105 for[JTC-364]sqlserver 部门导入导入失败,部门树数据丢失------------
queryDepartTreeSync().then((res) => {
if (res.success) {
this.allTreeKeys = [];
@ -288,7 +291,8 @@
}
}
that.$nextTick(()=>{
that.loading = true
//部门树v-if用到了loading,和上传loading冲突了换一个名称
that.treeLoading = true
})
}
})
@ -320,7 +324,8 @@
that.departTreeAll=that.departTree
},
refresh() {
this.loading = true
//部门树v-if用到了loading,和上传loading冲突了换一个名称
this.treeLoading = true
this.loadTree()
},
// 右键操作方法

View File

@ -59,10 +59,13 @@
:loading="loading"
@change="handleTableChange">
<div v-show="queryParam.logType==2" slot="expandedRowRender" slot-scope="record" style="margin: 0">
<div style="margin-bottom: 5px"><a-badge status="success" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求方法:{{ record.method }}</span></div>
<div><a-badge status="processing" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求参数:{{ record.requestParam }}</span></div>
</div>
<template v-if="queryParam.logType==='2'" #expandedRowRender="record">
<div style="margin: 0">
<div style="margin-bottom: 5px"><a-badge status="success" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求方法:{{ record.method }}</span></div>
<div><a-badge status="processing" style="vertical-align: middle;"/><span style="vertical-align: middle;">请求参数:{{ record.requestParam }}</span></div>
</div>
</template>
<!-- 字符串超长截取省略号显示-->
<span slot="logContent" slot-scope="text, record">
<j-ellipsis :value="text" :length="40"/>
@ -187,6 +190,10 @@
param['superQueryParams'] = encodeURI(this.superQueryParams)
param['superQueryMatchType'] = this.superQueryMatchType
}
//登录日志没有操作类型
if (this.tabKey === '1') {
param.operateType = ''
}
return filterObj(param);
},
@ -213,7 +220,7 @@
let that=this;
that.queryParam.logType=key;
that.loadData();
that.loadData(1);
},
onDateChange: function (value, dateString) {
console.log(dateString[0],dateString[1]);

View File

@ -47,6 +47,9 @@
:dataSource="dataSource"
:loading="loading"
:rowClassName="getRowClassname">
<template slot="ruleValueText" slot-scope="text,record">
<j-ellipsis :value="text" :length="15"></j-ellipsis>
</template>
<span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">
<a-icon type="edit"/>编辑
@ -72,17 +75,21 @@
{
title: '规则名称',
dataIndex: 'ruleName',
key: 'ruleName'
key: 'ruleName',
width:150,
},
{
title: '规则字段',
dataIndex: 'ruleColumn',
key: 'ruleColumn'
key: 'ruleColumn',
width:150,
},
{
title: '规则值',
dataIndex: 'ruleValue',
key: 'ruleValue'
key: 'ruleValue',
width:150,
scopedSlots: {customRender: "ruleValueText"}
},
{
title: '操作',

View File

@ -64,6 +64,7 @@
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
:scroll="{x:true}"
@change="handleTableChange">
<!-- 字符串超长截取省略号显示-->
@ -83,7 +84,7 @@
<a-dropdown>
<a class="ant-dropdown-link">更多 <a-icon type="down" /></a>
<a-menu slot="overlay">
<a-menu-item><a @click="executeImmediately(record)">立即执行</a></a-menu-item>
<a-menu-item><a @click="executeImmediately(record)">执行一次</a></a-menu-item>
<a-menu-item><a @click="handleEdit(record)">编辑</a></a-menu-item>
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
@ -214,7 +215,12 @@
this.isorter.order = "ascend" == sorter.order ? "asc" : "desc"
}
//这种筛选方式只支持单选
this.filters.status = filters.status[0];
// update-begin-author:liusq date:20210624 for:前台定时任务无法翻页 #2666
if(filters && Object.keys(filters).length>0 && filters.status){
this.filters.status = filters.status[0];
}
// update-end-author:liusq date:20210624 for:前台定时任务无法翻页 #2666
this.ipagination = pagination;
this.loadData();
},

View File

@ -69,6 +69,7 @@
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical" />
<a-dropdown>
<a class="ant-dropdown-link">
更多 <a-icon type="down" />

View File

@ -63,6 +63,7 @@
<a @click="handleOpen(record)">用户</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">
更多 <a-icon type="down"/>
@ -537,6 +538,7 @@
}
</script>
<style scoped>
@import '~@assets/less/common.less';
/** Button按钮间距 */
.ant-btn {
margin-left: 8px

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