commit 24df7ea115d755b313a65e1e79fd0602a3a2e85e Author: liuhanqing Date: Thu Oct 16 14:45:48 2025 +0800 项目初始化 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..024ffd5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +# idea +/.idea/ +**/target/ + +/log/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8564f29 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2018 RuoYi + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..274d769 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +

+ logo +

+

RuoYi v3.8.9

+

基于SpringBoot+Vue前后端分离的Java快速开发框架

+

+ + + +

+ +## 平台简介 + +若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 + +* 前端采用Vue、Element UI。 +* 后端采用Spring Boot、Spring Security、Redis & Jwt。 +* 权限认证使用Jwt,支持多终端认证系统。 +* 支持加载动态权限菜单,多方式轻松权限控制。 +* 高效率开发,使用代码生成器可以一键生成前后端代码。 +* 提供了技术栈([Vue3](https://v3.cn.vuejs.org) [Element Plus](https://element-plus.org/zh-CN) [Vite](https://cn.vitejs.dev))版本[RuoYi-Vue3](https://gitcode.com/yangzongzhuan/RuoYi-Vue3),保持同步更新。 +* 提供了单应用版本[RuoYi-Vue-fast](https://gitcode.com/yangzongzhuan/RuoYi-Vue-fast),Oracle版本[RuoYi-Vue-Oracle](https://gitcode.com/yangzongzhuan/RuoYi-Vue-Oracle),保持同步更新。 +* 不分离版本,请移步[RuoYi](https://gitee.com/y_project/RuoYi),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud) +* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)   + +## 内置功能 + +1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 +2. 部门管理:配置系统组织机构(公司、部门、小组),树结构展现支持数据权限。 +3. 岗位管理:配置系统用户所属担任职务。 +4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 +5. 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 +6. 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 +7. 参数管理:对系统动态配置常用参数。 +8. 通知公告:系统通知公告信息发布维护。 +9. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 +10. 登录日志:系统登录日志记录查询包含登录异常。 +11. 在线用户:当前系统中活跃用户状态监控。 +12. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 +13. 代码生成:前后端代码的生成(java、html、xml、sql)支持CRUD下载 。 +14. 系统接口:根据业务代码自动生成相关的api接口文档。 +15. 服务监控:监视当前系统CPU、内存、磁盘、堆栈等相关信息。 +16. 缓存监控:对系统的缓存信息查询,命令统计等。 +17. 在线构建器:拖动表单元素生成相应的HTML代码。 +18. 连接池监视:监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 + +## 在线体验 + +- admin/admin123 +- 陆陆续续收到一些打赏,为了更好的体验已用于演示服务器升级。谢谢各位小伙伴。 + +演示地址:http://vue.ruoyi.vip +文档地址:http://doc.ruoyi.vip + +## 演示图 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +## 若依前后端分离交流群 + +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-937441-blue.svg)](https://jq.qq.com/?_wv=1027&k=5bVB1og) [![加入QQ群](https://img.shields.io/badge/已满-887144332-blue.svg)](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [![加入QQ群](https://img.shields.io/badge/已满-180251782-blue.svg)](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [![加入QQ群](https://img.shields.io/badge/已满-104180207-blue.svg)](https://jq.qq.com/?_wv=1027&k=51G72yr) [![加入QQ群](https://img.shields.io/badge/已满-186866453-blue.svg)](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [![加入QQ群](https://img.shields.io/badge/已满-201396349-blue.svg)](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [![加入QQ群](https://img.shields.io/badge/已满-101456076-blue.svg)](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [![加入QQ群](https://img.shields.io/badge/已满-101539465-blue.svg)](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [![加入QQ群](https://img.shields.io/badge/已满-264312783-blue.svg)](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [![加入QQ群](https://img.shields.io/badge/已满-167385320-blue.svg)](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [![加入QQ群](https://img.shields.io/badge/已满-104748341-blue.svg)](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [![加入QQ群](https://img.shields.io/badge/已满-160110482-blue.svg)](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [![加入QQ群](https://img.shields.io/badge/已满-170801498-blue.svg)](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [![加入QQ群](https://img.shields.io/badge/已满-108482800-blue.svg)](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [![加入QQ群](https://img.shields.io/badge/已满-101046199-blue.svg)](https://jq.qq.com/?_wv=1027&k=SpyH2875) [![加入QQ群](https://img.shields.io/badge/已满-136919097-blue.svg)](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [![加入QQ群](https://img.shields.io/badge/已满-143961921-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [![加入QQ群](https://img.shields.io/badge/已满-174951577-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [![加入QQ群](https://img.shields.io/badge/已满-161281055-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [![加入QQ群](https://img.shields.io/badge/已满-138988063-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [![加入QQ群](https://img.shields.io/badge/已满-151450850-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [![加入QQ群](https://img.shields.io/badge/已满-224622315-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [![加入QQ群](https://img.shields.io/badge/已满-287842588-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [![加入QQ群](https://img.shields.io/badge/已满-187944233-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [![加入QQ群](https://img.shields.io/badge/228578329-blue.svg)](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) 点击按钮入群。 \ No newline at end of file diff --git a/bin/clean.bat b/bin/clean.bat new file mode 100644 index 0000000..24c0974 --- /dev/null +++ b/bin/clean.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ÇåÀí¹¤³ÌtargetÉú³É·¾¶¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean + +pause \ No newline at end of file diff --git a/bin/package.bat b/bin/package.bat new file mode 100644 index 0000000..c693ec0 --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ´ò°üWeb¹¤³̣¬Éú³Éwar/jar°üÎļþ¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +call mvn clean package -Dmaven.test.skip=true + +pause \ No newline at end of file diff --git a/bin/run.bat b/bin/run.bat new file mode 100644 index 0000000..41efbd0 --- /dev/null +++ b/bin/run.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [ÐÅϢ] ʹÓÃJarÃüÁîÔËÐÐWeb¹¤³̡£ +echo. + +cd %~dp0 +cd ../ruoyi-admin/target + +set JAVA_OPTS=-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m + +java -jar %JAVA_OPTS% ruoyi-admin.jar + +cd bin +pause \ No newline at end of file diff --git a/doc/若依环境使用手册.docx b/doc/若依环境使用手册.docx new file mode 100644 index 0000000..9e4daef Binary files /dev/null and b/doc/若依环境使用手册.docx differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..589d24f --- /dev/null +++ b/pom.xml @@ -0,0 +1,300 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 3.8.9 + + ruoyi + http://www.ruoyi.vip + 若依管理系统 + + + 3.8.9 + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 2.5.15 + 1.2.23 + 1.21 + 3.0.0 + 2.3.3 + 1.4.7 + 2.0.53 + 6.6.5 + 2.13.0 + 4.1.2 + 2.3 + 0.9.1 + + 9.0.102 + 1.2.13 + 5.7.12 + 5.3.39 + 5.8.27 + 1.18.30 + 3.7.0 + 3.5.2 + + + + + + + + + + org.springframework + spring-framework-bom + ${spring-framework.version} + pom + import + + + + + org.springframework.security + spring-security-bom + ${spring-security.version} + pom + import + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + + ch.qos.logback + logback-core + ${logback.version} + + + + ch.qos.logback + logback-classic + ${logback.version} + + + + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} + + + + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + + + org.apache.velocity + velocity-engine-core + ${velocity.version} + + + + + com.alibaba.fastjson2 + fastjson2 + ${fastjson.version} + + + + + io.jsonwebtoken + jjwt + ${jwt.version} + + + + + pro.fessional + kaptcha + ${kaptcha.version} + + + + + com.ruoyi + ruoyi-quartz + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-generator + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-framework + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-system + ${ruoyi.version} + + + + + com.ruoyi + ruoyi-common + ${ruoyi.version} + + + + + cn.hutool + hutool-all + ${hutool.version} + + + + + com.github.dathlin + HslCommunication + ${HslCommunication.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + + + + ruoyi-admin + ruoyi-framework + ruoyi-system + ruoyi-quartz + ruoyi-generator + ruoyi-common + + pom + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + + + + + public + aliyun nexus + https://maven.aliyun.com/repository/public + + true + + + false + + + + + diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 0000000..ae294d5 --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,137 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + jar + ruoyi-admin + + + web服务入口 + + + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + io.springfox + springfox-boot-starter + + + + + io.swagger + swagger-models + 1.6.2 + + + + + mysql + mysql-connector-java + + + + com.microsoft.sqlserver + mssql-jdbc + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + ruoyi-quartz + + + + + com.ruoyi + ruoyi-generator + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + org.projectlombok + lombok + + + + org.slf4j + slf4j-api + + + + + org.apache.plc4x + plc4j-api + 0.9.0 + + + + org.apache.plc4x + plc4j-driver-s7 + 0.9.0 + + + + com.github.dathlin + HslCommunication + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.5.15 + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.1.0 + + false + ${project.artifactId} + + + + ${project.artifactId} + + + diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java new file mode 100644 index 0000000..e95a574 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,26 @@ +package com.ruoyi; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +@EnableScheduling +@EnableAsync +public class RuoYiApplication +{ + public static void main(String[] args) + { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 福来瑞达WCS系统启动成功 ლ(´ڡ`ლ)゙ "); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java new file mode 100644 index 0000000..6de67dc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiServletInitializer.java @@ -0,0 +1,18 @@ +package com.ruoyi; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +/** + * web容器中进行部署 + * + * @author ruoyi + */ +public class RuoYiServletInitializer extends SpringBootServletInitializer +{ + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) + { + return application.sources(RuoYiApplication.class); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/controller/AgvController.java b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/AgvController.java new file mode 100644 index 0000000..5c8f84c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/AgvController.java @@ -0,0 +1,53 @@ +package com.ruoyi.api.controller; + +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackRequest; +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackResponse; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * Agv操作请求 + */ +@RestController +@RequestMapping("/agv/api") +@Slf4j +public class AgvController extends BaseController { + + + @Autowired + private IApiService apiService; + + /** + * agv执行状态返回接口 + * @param request + * @return + */ + @PostMapping("/agvCallback") + public AgvCallbackResponse agvCallback(@RequestBody AgvCallbackRequest request) { + return apiService.agvCallback(request); + } + + + + /** + * agv搬运作业 + * @param param{ + * startPoint:起始点 + * endPoint:结束点 + * containerCode:容器编号 + * taskNo:任务号 + * } + * @return + */ + @PostMapping("/receiveAgvMoveJob") + public AjaxResult receiveAgvMoveJob(@RequestBody Map param) { + return apiService.receiveAgvMoveJob(param); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/controller/RequestLogController.java b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/RequestLogController.java new file mode 100644 index 0000000..9bcdd62 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/RequestLogController.java @@ -0,0 +1,120 @@ +package com.ruoyi.api.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.api.domain.RequestLog; +import com.ruoyi.api.service.IRequestLogService; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; +import com.ruoyi.device.DeviceCar.service.IDeviceCarService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 接口记录Controller + * + * @author ruoyi + * @date 2025-06-11 + */ +@RestController +@RequestMapping("/device/api") +public class RequestLogController extends BaseController +{ + @Autowired + private IRequestLogService requestLogService; + + @Autowired + private IDeviceCarService deviceCarService; + + /** + * 查询所有设备信息 + */ + @GetMapping("/devicecar/list") + public AjaxResult listAllDeviceCar() + { + List list = deviceCarService.selectDeviceCarList(new DeviceCar()); + return success(list); + } + + /** + * 查询接口记录列表 + */ + @PreAuthorize("@ss.hasPermi('device:api:list')") + @GetMapping("/list") + public TableDataInfo list(RequestLog requestLog) + { + startPage(); + List list = requestLogService.selectRequestLogList(requestLog); + return getDataTable(list); + } + + /** + * 导出接口记录列表 + */ + @PreAuthorize("@ss.hasPermi('device:api:export')") + @Log(title = "接口记录", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, RequestLog requestLog) + { + List list = requestLogService.selectRequestLogList(requestLog); + ExcelUtil util = new ExcelUtil(RequestLog.class); + util.exportExcel(response, list, "接口记录数据"); + } + + /** + * 获取接口记录详细信息 + */ + @PreAuthorize("@ss.hasPermi('device:api:query')") + @GetMapping(value = "/{requestId}") + public AjaxResult getInfo(@PathVariable("requestId") Long requestId) + { + return success(requestLogService.selectRequestLogByRequestId(requestId)); + } + + /** + * 新增接口记录 + */ + @PreAuthorize("@ss.hasPermi('device:api:add')") + @Log(title = "接口记录", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody RequestLog requestLog) + { + return toAjax(requestLogService.insertRequestLog(requestLog)); + } + + /** + * 修改接口记录 + */ + @PreAuthorize("@ss.hasPermi('device:api:edit')") + @Log(title = "接口记录", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody RequestLog requestLog) + { + return toAjax(requestLogService.updateRequestLog(requestLog)); + } + + /** + * 删除接口记录 + */ + @PreAuthorize("@ss.hasPermi('device:api:remove')") + @Log(title = "接口记录", businessType = BusinessType.DELETE) + @DeleteMapping("/{requestIds}") + public AjaxResult remove(@PathVariable Long[] requestIds) + { + return toAjax(requestLogService.deleteRequestLogByRequestIds(requestIds)); + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/controller/ScreenController.java b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/ScreenController.java new file mode 100644 index 0000000..83d8e10 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/ScreenController.java @@ -0,0 +1,124 @@ +package com.ruoyi.api.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import com.ruoyi.common.annotation.Anonymous; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.service.IDeviceSsjService; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; + +/** + * 大屏接口Controller + * + * @author ruoyi + */ +@Anonymous +@RestController +@RequestMapping("/api/screen") +public class ScreenController extends BaseController { + + @Autowired + private IDeviceSsjService deviceSsjService; + + @Autowired + private IMydoJobService mydoJobService; + + // 定义各区域输送机编号数组 + private static final Map> AREA_SSJ_CODES = new HashMap<>(); + + // 静态初始化输送机编号数组 + static { + // 1区域输送机编号 + AREA_SSJ_CODES.put(1, Arrays.asList( + "L2007","L2008","L2005","L2006","L2013" + )); + + // 2区域输送机编号 + AREA_SSJ_CODES.put(2, Arrays.asList( + "L2003","L2004","L2012","L2001","L2002" + )); + + // 3区域输送机编号 + AREA_SSJ_CODES.put(3, Arrays.asList( + "L1001","L1002","L1011" + )); + + // 4区域输送机编号 + AREA_SSJ_CODES.put(4, Arrays.asList( + "L1003","L1004","L1012" + )); + } + + /** + * 根据区域编号查询输送机列表 + * + * @param areaNum 区域编号(1-4) + * @return 输送机数据列表 + */ + @PostMapping("/ssj/{areaNum}") + public AjaxResult getSsjListByArea(@PathVariable("areaNum") Integer areaNum) { + if (areaNum < 1 || areaNum > 4) { + return AjaxResult.error("区域编号必须在1-4之间"); + } + + // 获取该区域的输送机编号数组 + List ssjCodes = AREA_SSJ_CODES.get(areaNum); + if (ssjCodes == null || ssjCodes.isEmpty()) { + return AjaxResult.error("未找到该区域的输送机编号配置"); + } + + // 查询所有输送机 + List allSsjList = deviceSsjService.selectDeviceSsjList(null); + + // 根据编号数组筛选输送机 + List filteredList = allSsjList.stream() + .filter(ssj -> ssj.getDeviceCode() != null && ssjCodes.contains(ssj.getDeviceCode().trim())) + .collect(Collectors.toList()); + + // 转换为前端需要的数据格式 + List> resultList = new ArrayList<>(); + // 创建作业信息列表 + List jobList = new ArrayList<>(); + + for (DeviceSsj ssj : filteredList) { + Map map = new HashMap<>(); + map.put("id", ssj.getDeviceCode()); + map.put("name", ssj.getName()); + map.put("kx", String.valueOf(ssj.getKx())); + map.put("dw", String.valueOf(ssj.getDw())); + map.put("sbzt", ssj.getException()); + // map.put("trayCode", ssj.getTrayCode()); + map.put("allowPut", String.valueOf(ssj.getAllowPut())); + map.put("traySize", String.valueOf(ssj.getTraySize())); + map.put("exception", String.valueOf(ssj.getException())); + map.put("taskNo", ssj.getTaskNo()); + resultList.add(map); + + // 如果taskNo大于0,查询对应的作业信息 + if (ssj.getTaskNo()> 0) { + MydoJob job = mydoJobService.selectMydoJobByTaskNo(Long.valueOf(ssj.getTaskNo())); + if (job != null) { + jobList.add(job); + } + } + } + + // 返回结果包含data和job两个字段 + Map result = new HashMap<>(); + result.put("ssj", resultList); + result.put("job", jobList); + + return AjaxResult.success(result); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/controller/Wcs2WmsController.java b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/Wcs2WmsController.java new file mode 100644 index 0000000..e7c86d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/controller/Wcs2WmsController.java @@ -0,0 +1,56 @@ +package com.ruoyi.api.controller; + +import com.ruoyi.api.domain.wcs2wms.JobParam; +import com.ruoyi.api.domain.wcs2wms.ReceiveJob; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/wcs2Wms/api") +public class Wcs2WmsController { + + @Autowired + private IApiService apiService; + + /** + * 接收出库任务 + * + * @param receiveOutJob + * @return + */ + @PostMapping("/receiveOutMydoJob") + public AjaxResult receiveOutMydoJob(@RequestBody ReceiveJob receiveOutJob) { + return apiService.receiveOutMydoJob(receiveOutJob); + } + + /** + * 接收入库任务 + * + * @param receiveInJob + * @return + */ + @PostMapping("/receiveInMydoJob") + public AjaxResult receiveInMydoJob(@RequestBody ReceiveJob receiveInJob) { + return apiService.receiveInMydoJob(receiveInJob); + } + + + + /** + * wms取消任务 + * @param jobParam + * @return + */ + @PostMapping("/cancelMydoJob") + public AjaxResult cancelMydoJob(@RequestBody JobParam jobParam) { + return apiService.cancelMydoJob(jobParam); + } + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/InterfaceReceive.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/InterfaceReceive.java new file mode 100644 index 0000000..e1dc98f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/InterfaceReceive.java @@ -0,0 +1,21 @@ +package com.ruoyi.api.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +/** + * 接口接收类 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +@ToString +public class InterfaceReceive { + + private Integer code; + private String msg; + private Object data; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/RequestLog.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/RequestLog.java new file mode 100644 index 0000000..fb0dbcc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/RequestLog.java @@ -0,0 +1,247 @@ +package com.ruoyi.api.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 接口记录对象 request_log + * + * @author ruoyi + * @date 2025-06-11 + */ +public class RequestLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 请求id */ + private Long requestId; + + /** 请求时间 */ + @Excel(name = "请求时间") + private String requestTime; + + /** 来源系统 */ + @Excel(name = "来源系统") + private String sourceSystem; + + /** 目标系统 */ + @Excel(name = "目标系统") + private String targetSystem; + + /** 接口名称 */ + @Excel(name = "接口名称") + private String endpoint; + + /** url路径 */ + @Excel(name = "url路径") + private String requestUrl; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 请求头信息 */ + @Excel(name = "请求头信息") + private String requestHeaders; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String requestBody; + + /** 响应时间 */ + @Excel(name = "响应时间") + private String responseTime; + + /** 响应主体 */ + @Excel(name = "响应主体") + private String responseBody; + + /** 响应状态 */ + @Excel(name = "响应状态") + private String responseStatus; + + /** 错误信息 */ + @Excel(name = "错误信息") + private String errorDetails; + + /** 耗时 */ + @Excel(name = "耗时") + private String duration; + + /** 是否成功 */ + @Excel(name = "是否成功",dictType = "interface_status") + private String isSuccessful; + + public void setRequestId(Long requestId) + { + this.requestId = requestId; + } + + public Long getRequestId() + { + return requestId; + } + + public void setRequestTime(String requestTime) + { + this.requestTime = requestTime; + } + + public String getRequestTime() + { + return requestTime; + } + + public void setSourceSystem(String sourceSystem) + { + this.sourceSystem = sourceSystem; + } + + public String getSourceSystem() + { + return sourceSystem; + } + + public void setTargetSystem(String targetSystem) + { + this.targetSystem = targetSystem; + } + + public String getTargetSystem() + { + return targetSystem; + } + + public void setEndpoint(String endpoint) + { + this.endpoint = endpoint; + } + + public String getEndpoint() + { + return endpoint; + } + + public void setRequestUrl(String requestUrl) + { + this.requestUrl = requestUrl; + } + + public String getRequestUrl() + { + return requestUrl; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestHeaders(String requestHeaders) + { + this.requestHeaders = requestHeaders; + } + + public String getRequestHeaders() + { + return requestHeaders; + } + + public void setRequestBody(String requestBody) + { + this.requestBody = requestBody; + } + + public String getRequestBody() + { + return requestBody; + } + + public void setResponseTime(String responseTime) + { + this.responseTime = responseTime; + } + + public String getResponseTime() + { + return responseTime; + } + + public void setResponseBody(String responseBody) + { + this.responseBody = responseBody; + } + + public String getResponseBody() + { + return responseBody; + } + + public void setResponseStatus(String responseStatus) + { + this.responseStatus = responseStatus; + } + + public String getResponseStatus() + { + return responseStatus; + } + + public void setErrorDetails(String errorDetails) + { + this.errorDetails = errorDetails; + } + + public String getErrorDetails() + { + return errorDetails; + } + + public void setDuration(String duration) + { + this.duration = duration; + } + + public String getDuration() + { + return duration; + } + + public void setIsSuccessful(String isSuccessful) + { + this.isSuccessful = isSuccessful; + } + + public String getIsSuccessful() + { + return isSuccessful; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("requestId", getRequestId()) + .append("requestTime", getRequestTime()) + .append("sourceSystem", getSourceSystem()) + .append("targetSystem", getTargetSystem()) + .append("endpoint", getEndpoint()) + .append("requestUrl", getRequestUrl()) + .append("requestMethod", getRequestMethod()) + .append("requestHeaders", getRequestHeaders()) + .append("requestBody", getRequestBody()) + .append("responseTime", getResponseTime()) + .append("responseBody", getResponseBody()) + .append("responseStatus", getResponseStatus()) + .append("errorDetails", getErrorDetails()) + .append("duration", getDuration()) + .append("isSuccessful", getIsSuccessful()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/ResponseBase.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/ResponseBase.java new file mode 100644 index 0000000..5ec49be --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/ResponseBase.java @@ -0,0 +1,19 @@ +package com.ruoyi.api.domain.agv; + +import lombok.Data; + +@Data +public class ResponseBase { + /** + * 返回码 + */ + private Integer code; + /** + * 返回消息 + */ + private String message; + /** + * 自定义返回 + */ + private Object data; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackRequest.java new file mode 100644 index 0000000..5de494e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackRequest.java @@ -0,0 +1,126 @@ +package com.ruoyi.api.domain.agv.agvCallback; + +import lombok.Data; + +@Data +public class AgvCallbackRequest { + + /** + * 请求编号,每个请求都要一个唯一编号,同一个请求重复提交,使用同一编号 + */ + private String reqCode; + + /** + * 请求时间戳,格式: “yyyy-MM-dd HH:mm:ss” + */ + private String reqTime; + + /** + * 地码 X 坐标(mm):任务完成时有值 + */ + private Number cooX; + + /** + * 地码 Y 坐标(mm):任务完成时有值 + */ + private Number cooY; + + /** + * 当前位置编号 + * 任务开始:该位置为任务起点 + * 走出储位:该位置为任务起点 + * 任务单取消:该位置为工作位编号 + * 任务结束:该位置为任务终点 + * 取放申请:取放料箱的点 + */ + private String currentPositionCode; + + /** + * 自定义字段,不超过2000个字符 + */ + private String data; + + /** + * 地图编号 + */ + private String mapCode; + + /** + * 地码编号:任务完成时有值 + */ + private String mapDataCode; + + /** + * 仓位编号:叉车与CTU任务时有值 + */ + private String stgBinCode; + + /** + * 方法名, 可使用任务类型做为方法名 + * 默认使用方式: + * start : 任务开始 + * outbin : 走出储位 + * end : 任务结束 + * cancel : 任务单取消 + * apply:CTU料箱取放申请 + */ + private String method; + + /** + * 货架编号:背货架时有值 + */ + private String podCode; + + /** + * 方向:“180”,”0”,”90”,”-90” 分别对应地图的”左”,”右”,”上”,”下”:任务完成时有值 + */ + private String podDir; + + /** + * 物料编号 + */ + private String materialLot; + + /** + * AGV编号(同 agvCode ) + */ + private String robotCode; + + /** + * 当前任务单号 + */ + private String taskCode; + + /** + * 工作位,与RCS-2000端配置的位置名称一致。任务完成时有值,与生成任务单接口中的wbCode一致。 + */ + private String wbCode; + + /** + * 容器编号 + */ + private String ctnrCode; + + /** + * 容器类型 + */ + private String ctnrType; + + /** + * 巷道编号 + */ + private String roadWayCode; + + /** + * 巷道内顺序号 + * 巷道尾是0,到巷道头依次递增1 + */ + private String seq; + + /** + * 设备编号,如梳齿式工作站、输送线等,一般使用于CTU场景。 + * 系统根据仓位定位到关联的设备编号。 + */ + private String eqpCode; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackResponse.java new file mode 100644 index 0000000..b4af955 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/agvCallback/AgvCallbackResponse.java @@ -0,0 +1,27 @@ +package com.ruoyi.api.domain.agv.agvCallback; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class AgvCallbackResponse { + + /** + * 响应码 + */ + private Integer code; + + /** + * 响应信息 + */ + private String message; + + /** + * 请求编号 + */ + private String reqCode; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskRequest.java new file mode 100644 index 0000000..340f2cd --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskRequest.java @@ -0,0 +1,68 @@ +package com.ruoyi.api.domain.agv.cancelTask; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +/** + * 【取消任务】请求接口 + */ +@Data +public class CancelTaskRequest { + + /** + * 请求编号 + */ + @NotBlank(message = "请求编号不能为空") + @Size(max = 32, message = "请求编号不能超过32位") + private String reqCode; + + /** + * 请求时间戳 + * 格式: “yyyy-MM-dd HH:mm:ss” + */ + @Size(max = 20, message = "请求时间格式应为 yyyy-MM-dd HH:mm:ss") + private String reqTime; + + /** + * 客户端编号 + */ + @Size(max = 16, message = "客户端编号不能超过16位") + private String clientCode; + + /** + * 令牌号, 由调度系统颁发,由RCS-2000告知上层系统 + */ + @Size(max = 64, message = "令牌号不能超过64位") + private String tokenCode; + + /** + * 取消类型 + * 0表示:取消后货架直接放地上 + * 1表示:AGV仍然背着货架, 根据回库区域执行回库指令, 只有潜伏车和CTU车支持。 + * 若不传值,默认的取消模式为0 + */ + @Size(max = 16, message = "取消类型不能超过16位") + private String forceCancel; + + /** + * forcecancel=1时有意义, + * 回库区域编号, + * 如果为空,采用货架配置的库区 + */ + @Size(max = 16, message = "回库区域编号不能超过16位") + private String matterArea; + + /** + * 取消该AGV正在执行的任务单 + */ + @Size(max = 5, message = "AGV编号不能超过5位") + private String agvCode; + + /** + * 任务单编号, 取消该任务单 + */ + @Size(max = 64, message = "任务单编号不能超过64位") + private String taskCode; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskResponse.java new file mode 100644 index 0000000..42bfc88 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/cancelTask/CancelTaskResponse.java @@ -0,0 +1,13 @@ +package com.ruoyi.api.domain.agv.cancelTask; + +import com.ruoyi.api.domain.agv.ResponseBase; +import lombok.Data; + +@Data +public class CancelTaskResponse extends ResponseBase { + + /** + * 请求编号 + */ + public String reqCode; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskRequest.java new file mode 100644 index 0000000..0bcc4de --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskRequest.java @@ -0,0 +1,70 @@ +package com.ruoyi.api.domain.agv.continueTask; + +import lombok.Data; + +/** + * 【继续执行任务】请求接口 + */ +@Data +public class ContinueTaskRequest { + + /** + * 请求编号 + * 每个请求都要一个唯一编号,同一个请求重复提交,使用同一编号。 + */ + private String reqCode; + + /** + * 请求时间截 + * 格式:"yyyy-MM-dd HH:mm:ss" + */ + private String reqTime; + + /** + * 客户端编号 + * 如PDA,HCWMS等。 + */ + private String clientCode; + + /** + * 令牌号 + * 由调度系统颁发。 + */ + private String tokenCode; + + /** + * 工作位 + * 与RCS-2000端配置的位置名称一致。 + */ + private String wbCode; + + /** + * 货架号 + * 采用货架号触发的方式。 + */ + private String podCode; + + /** + * AGV编号 + * 采用AGV编号触发的方式。 + */ + private String agvCode; + + /** + * 任务单号 + * 选填,不填系统自动生成,必须为64位UUID。 + */ + private String taskCode; + + /** + * 下一个子任务的序列 + * 指定第几个子任务开始执行,校验子任务执行是否正确。不填默认执行下一个子任务。 + */ + private String taskSeq; + + /** + * 下一个位置信息 + * 在任务类型中配置外部设置时需要传入,否则不需要设置。待现场地图部署、配置完成后可获取。 + */ + private NextPositionCodeData nextPositionCode; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskResponse.java new file mode 100644 index 0000000..57e7e48 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/ContinueTaskResponse.java @@ -0,0 +1,13 @@ +package com.ruoyi.api.domain.agv.continueTask; + +import com.ruoyi.api.domain.agv.ResponseBase; +import lombok.Data; + +@Data +public class ContinueTaskResponse extends ResponseBase { + + /** + * 请求编号 + */ + public String reqCode; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/NextPositionCodeData.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/NextPositionCodeData.java new file mode 100644 index 0000000..e5133bd --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/continueTask/NextPositionCodeData.java @@ -0,0 +1,17 @@ +package com.ruoyi.api.domain.agv.continueTask; + +public class NextPositionCodeData { + + /** + * 位置编号 + */ + private String positionCode; + + /** + * 对象类型定义 + * 00:代表nextPositionCode是一个位置 + * 02:代表nextPositionCode是一个策略 + */ + private String type; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskRequest.java new file mode 100644 index 0000000..46bea8d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskRequest.java @@ -0,0 +1,156 @@ +package com.ruoyi.api.domain.agv.genAgvSchedulingTask; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.UUID; + +/** + * 【生成AGV任务】请求接口 + */ +@Data +public class GenAgvSchedulingTaskRequest { + /** + * 请求编号 + * 同一个请求重复提交, 使用同一编号 + */ + @NotNull + private String reqCode = UUID.randomUUID().toString(); + /** + * 请求时间 + * 格式: “yyyy-MM-dd HH:mm:ss” + */ + private String reqTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + /** + * 客户端编号 + * 客户端编号,如PDA,HCWMS等 + */ + private String clientCode; + /** + * 令牌号 + * 由调度系统颁发 + */ + private String tokenCode; + /** + * 任务类型 + * 内置任务类型: + * 厂内货架搬运: F01 + * 厂内货架空满交换: F02 + * 辊筒搬运接驳:F03 + * 厂内货架出库AGV待命:F04 + * 旋转货架: F05 + * 厂内电梯任务: F06 + * 以下为叉车专用任务类型 + * 高位货架到工作台: F11 + * 工作台到高位货架: F12 + * 巷道到工作台: F13 + * 工作台到巷道: F14 + * 高位货架到工作台(接驳) : F15 + * 工作台到高位货架 (接驳) : F16 + * 巷道到工作台(接驳) : F17 + * 工作台到巷道(接驳) : F18 + * 叉车电梯主任务: F20 + */ + @NotNull + private String taskTyp; + /** + * 容器类型(叉车/CTU专用) 叉车项目必传 + */ + private String ctnrTyp; + /** + * 容器编号 (叉车/CTU专用) + */ + private String ctnrCode; // + /** + * 容器数量(叉车堆叠专用),默认值1,仅记录堆叠的数量不记录堆叠的每个容器号 + */ + private String ctnrNum; + /** + * 任务模式 + * 0-普通move 1-出库move 2-入库move 3-移库move + */ + private String taskMode; + /** + * 工作位 + * 与RCS-2000端配置的位置名称一致, 工作位名称为字母\数字\或组合 + */ + private String wbCode; + /** + * 位置路径 + * AGV关键路径位置集合,与任务类型中模板配置的位置路径一一对应。待现场地图部署、配置完成后可获取 + */ + private List positionCodePath; + /** + * 货架编号 + * 不指定货架可以为空 + */ + private String podCode; + /** + * 货架方向 + * “180”,”0”,”90”,”-90” 分别对应地图的”左”,”右”,”上”,”下” + * 不指定方向可以为空 + * 如果终点不是工作台或工作台未配置操作方向则货架拣货方向podDir作为终点方向 + * 如果工作台配有操作方向,则系统会根据货架拣货方向podDir和工作台操作方向计算货架终点方向 + */ + private String podDir; + /** + * 货架类型, 传空时表示随机找个货架 + * 找空货架传参方式如下: + * -1: 代表不关心货架类型, 找到空货架即可. + * -2: 代表从工作位获取关联货架类型, 如果未配置, 只找空货架. + * 货架类型编号: 只找该货架类型的空货架 + */ + private String podTyp; + /** + * 物料批次 + * 物料批次或货架上的物料唯一编码,生成任务单时,货架与物料直接绑定时使用. (通过同时传podCode和materialLot来绑定或通过 wbCode 找到位置上的货架和materialLot来绑定) + * 如果是区域/策略内查找货架,可以匹配对应物料批次的货架,支持以下值: + * 0:区域/策略内查找空货架 + * 1:区域/策略内查找满货架 + * 其他值:查找指定物料批次对应的货架 + * 巷道任务通过该字段传入特征值 + */ + private String materialLot; + /** + * 优先级 + * 从(1~127)级,最大优先级最高。为空时,采用任务模板的优先级 + */ + private String priority; + /** + * AGV编号 + * 填写表示指定某一编号的AGV执行该任务 + */ + private String agvCode; + /** + * 任务单号 + * 选填, 不填系统自动生成 + */ + private String taskCode; + /** + * 组编号 + * CTU场景下用于按组出库,同组任务优先拼车。 如业务需要任务组间或组内按顺序出库,则需调用料箱顺序出库(CTU)接口。 + * 潜伏式场景下,通过组号来管理顺序出库的顺序,组号小的优先出库。 + */ + private String groupId; + /** + * 设备类型 + */ + private String agvTyp; + /** + * 选择策略 + * 区域/策略中挑选货架以及根据物料批次挑选货架时的先进先出规则,支持以下4个值: + * 1:按照货架到达储位的时间顺序,先进先出 + * 2:按照货架到达储位的时间顺序,先进后出 + * 9:按照货架绑定物料批次的时间顺序,先进先出 + * 10:按照货架绑定物料批次的时间顺序,先进后出 + */ + private String positionSelStrategy; + /** + * 自定义数据 + */ + private String data; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskResponse.java new file mode 100644 index 0000000..d8b367b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/GenAgvSchedulingTaskResponse.java @@ -0,0 +1,14 @@ +package com.ruoyi.api.domain.agv.genAgvSchedulingTask; + +import com.ruoyi.api.domain.agv.ResponseBase; +import lombok.Data; + +@Data +public class GenAgvSchedulingTaskResponse extends ResponseBase { + + /** + * 请求编号 + */ + public String reqCode; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/PositionCodeData.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/PositionCodeData.java new file mode 100644 index 0000000..077c9d3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/genAgvSchedulingTask/PositionCodeData.java @@ -0,0 +1,35 @@ +package com.ruoyi.api.domain.agv.genAgvSchedulingTask; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PositionCodeData { + /** + * 位置编号 - 单个编号不超过64位 + */ + private String positionCode; + /** + * 类型 + * + * 位置类型说明: + * 00表示:位置编号 + * 01表示:物料批次号 + * 02表示:策略编号(含多个区域) 如:第一个区域放不下, 可以放第二个区域 + * 03表示:货架编号,通过货架编号找到货架所在位置 + * 04表示:区域编号,在区域中查找可用位置 + * 05表示:仓位编号(叉车/CTU专用) + * 06表示:巷道编号 + * 07表示:容器编号 + * 08表示:巷道策略 + * 09表示:巷道区域 + * 10表示:巷道仓位 + * 11表示:输送线(机台)编号 + * 12表示:CTU工作台(梳齿工作站)编号 + * 13表示:搬运巷道指定货架出库 + */ + private String type; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/AgvStatusData.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/AgvStatusData.java new file mode 100644 index 0000000..5d83fb1 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/AgvStatusData.java @@ -0,0 +1,82 @@ +package com.ruoyi.api.domain.agv.queryAgvStatus; + +import lombok.Data; + +import java.util.List; + +@Data +public class AgvStatusData { + + /** + * 机器人编号 + */ + private String robotCode; + + /** + * 机器人方向 (范围 -180~360度) + */ + private String robotDir; + + /** + * 机器人IP + */ + private String robotIp; + + /** + * 机器人电量, 范围: 0-100 + */ + private String battery; + + /** + * 机器人 x 坐标,单位:毫米 + */ + private String posX; + + /** + * 机器人 y 坐标,单位:毫米 + */ + private String posY; + + /** + * 机器人所在地图 + */ + private String mapCode; + + /** + * 机器人当前速度, 单位: mm/s + */ + private String speed; + + /** + * 机器人状态 + * AGV常见状态编号和描述见附件6.1 + */ + private String status; + + /** + * 是否已被排除,被排除后不接受新任务(1-排除,0-正常) + */ + private String exclType; + + /** + * 是否暂停 0-否 1-是 + */ + private String stop; + + /** + * 背货架的编号 + */ + private String podCode; + + /** + * 背货架的方向 + */ + private String podDir; + + /** + * 执行路径,单位是毫米, 格式[x,y,dir] + * 示例:["[x,y,dir]","[x,y,dir]"] + */ + private List path; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusRequest.java new file mode 100644 index 0000000..0a05e93 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusRequest.java @@ -0,0 +1,38 @@ +package com.ruoyi.api.domain.agv.queryAgvStatus; + +import lombok.Data; + +/** + * 通过请求参数, 查询 AGV 状态信息,包括电池电量。 + * 调用频次: 100车以下:5秒, 100~200车:10秒, 200 ~300车:15秒 + * 调用路径示例: http://IP:8182/rcms-dps/rest/queryAgvStatus + */ +@Data +public class QueryAgvStatusRequest { + + /** + * 请求编号,每个请求都要一个唯一编号,同一个请求重复提交,使用同一编号。 + */ + private String reqCode; + + /** + * 请求时间戳,格式: "yyyy-MM-dd HH:mm:ss" + */ + private String reqTime; + + /** + * 客户端编号,如PDA,HCWMS等。 + */ + private String clientCode; + + /** + * 令牌号,由调度系统颁发。 + */ + private String tokenCode; + + /** + * 地图编号 + */ + private String mapCode; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusResponse.java new file mode 100644 index 0000000..76a3c7d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryAgvStatus/QueryAgvStatusResponse.java @@ -0,0 +1,21 @@ +package com.ruoyi.api.domain.agv.queryAgvStatus; + +import com.ruoyi.api.domain.agv.ResponseBase; +import lombok.Data; + +import java.util.List; + +@Data +public class QueryAgvStatusResponse extends ResponseBase { + + /** + * 请求编号 + */ + public String reqCode; + + /** + * 数据列表 + */ + private List data; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusRequest.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusRequest.java new file mode 100644 index 0000000..85a8e02 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusRequest.java @@ -0,0 +1,44 @@ +package com.ruoyi.api.domain.agv.queryTaskStatus; + +import com.ruoyi.common.utils.uuid.UUID; +import lombok.Data; + +import java.util.List; + +/** + * 通过任务编号查询任务当前执行状态,支持批量查询。 + * 参数错误或者任务单未找到返回均为成功。 + */ +@Data +public class QueryTaskStatusRequest { + + /** + * 请求编号,每个请求都要一个唯一编号,同一个请求重复提交,使用同一编号。 + */ + private String reqCode = UUID.randomUUID().toString(); + + /** + * 请求时间戳,格式: "yyyy-MM-dd HH:mm:ss" + */ + private String reqTime; + + /** + * 客户端编号,如PDA,HCWMS等。 + */ + private String clientCode; + + /** + * 令牌号,由调度系统颁发。 + */ + private String tokenCode; + + /** + * AGV编号 + */ + private String agvCode; + + /** + * 任务单编号数组,任务单编号与AGV编号只能传其中之一 + */ + private List taskCodes; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusResponse.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusResponse.java new file mode 100644 index 0000000..f6198fb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/QueryTaskStatusResponse.java @@ -0,0 +1,20 @@ +package com.ruoyi.api.domain.agv.queryTaskStatus; + +import com.ruoyi.api.domain.agv.ResponseBase; +import lombok.Data; + +import java.util.List; + +@Data +public class QueryTaskStatusResponse extends ResponseBase { + + /** + * 请求编号 + */ + public String reqCode; + + /** + * 数据列表 + */ + private List data; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/TaskStatusData.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/TaskStatusData.java new file mode 100644 index 0000000..b984660 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/agv/queryTaskStatus/TaskStatusData.java @@ -0,0 +1,26 @@ +package com.ruoyi.api.domain.agv.queryTaskStatus; + +import lombok.Data; + +@Data +public class TaskStatusData { + /** + * 任务单编号 + */ + private String taskCode; + + /** + * 任务类型 + */ + private String taskTyp; + + /** + * 任务状态:1-已创建,2-正在执行,5-取消完成,9-已结束, 10-被打断 + */ + private String taskStatus; + + /** + * AGV编号, 任务分配车后有值 + */ + private String agvCode; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/JobParam.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/JobParam.java new file mode 100644 index 0000000..c396884 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/JobParam.java @@ -0,0 +1,14 @@ +package com.ruoyi.api.domain.wcs2wms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class JobParam { + private String jobId; + private String trayCode; + private String taskNo; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/ReceiveJob.java b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/ReceiveJob.java new file mode 100644 index 0000000..a8d968a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/domain/wcs2wms/ReceiveJob.java @@ -0,0 +1,20 @@ +package com.ruoyi.api.domain.wcs2wms; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ReceiveJob { + private String taskNo; + private String cargoSpaceId; + private String exitId; + private String trayCode; + private String entranceId; + private String inOrOut; + private String jobId; + private String roadwayId; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/mapper/RequestLogMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/api/mapper/RequestLogMapper.java new file mode 100644 index 0000000..7b2a552 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/mapper/RequestLogMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.api.mapper; + +import java.util.List; +import com.ruoyi.api.domain.RequestLog; + +/** + * 接口记录Mapper接口 + * + * @author ruoyi + * @date 2025-06-11 + */ +public interface RequestLogMapper +{ + /** + * 查询接口记录 + * + * @param requestId 接口记录主键 + * @return 接口记录 + */ + public RequestLog selectRequestLogByRequestId(Long requestId); + + /** + * 查询接口记录列表 + * + * @param requestLog 接口记录 + * @return 接口记录集合 + */ + public List selectRequestLogList(RequestLog requestLog); + + /** + * 新增接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + public int insertRequestLog(RequestLog requestLog); + + /** + * 修改接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + public int updateRequestLog(RequestLog requestLog); + + /** + * 删除接口记录 + * + * @param requestId 接口记录主键 + * @return 结果 + */ + public int deleteRequestLogByRequestId(Long requestId); + + /** + * 批量删除接口记录 + * + * @param requestIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteRequestLogByRequestIds(Long[] requestIds); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/IApiService.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IApiService.java new file mode 100644 index 0000000..8d42633 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IApiService.java @@ -0,0 +1,131 @@ +package com.ruoyi.api.service; + +import com.ruoyi.api.domain.RequestLog; +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackRequest; +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackResponse; +import com.ruoyi.api.domain.agv.cancelTask.CancelTaskRequest; +import com.ruoyi.api.domain.agv.cancelTask.CancelTaskResponse; +import com.ruoyi.api.domain.agv.continueTask.ContinueTaskRequest; +import com.ruoyi.api.domain.agv.continueTask.ContinueTaskResponse; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskRequest; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskResponse; +import com.ruoyi.api.domain.wcs2wms.JobParam; +import com.ruoyi.api.domain.wcs2wms.ReceiveJob; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.queue.agv.domain.AgvQueue; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 接口记录Service接口 + * + * @author ruoyi + * @date 2025-06-11 + */ +public interface IApiService +{ + + + /** + * 生成agv队列 + * @param code1 起点 + * @param code2 终点 + * @param wmsTrayCode 容器码 托盘料箱码/容器码 + * @param wmsTaskNo mydojob 任务 + * @param wmsTaskType 1-入库 2-出库 3-agv搬运 + * @return + */ + public AgvQueue createAgvQueue(String code1,String code2,String wmsTrayCode,String taskCode,String wmsTaskType,String traySize,String wmsTaskNo); + + /** + * 取消AGV任务 + * + * @param cancelTaskRequest + * @return + */ + public CancelTaskResponse cancelTask(CancelTaskRequest cancelTaskRequest); + + /** + * 继续AGV任务 + * @param agvQueue + * @return + */ + public AjaxResult continueTask(AgvQueue agvQueue); + + /** + * 生成AGV生成任务 + * + * @param genAgvSchedulingTaskRequest + * @return + */ + public GenAgvSchedulingTaskResponse genAgvSchedulingTask(GenAgvSchedulingTaskRequest genAgvSchedulingTaskRequest); + + /** + * agv任务状态推送 + * @param request + * @return + */ + AgvCallbackResponse agvCallback(AgvCallbackRequest request); + + /** + * agv搬运作业 + * + * @param param + * @return + */ + AjaxResult receiveAgvMoveJob(Map param); + + /** + * 任务到达拣选口 + * @param param + * @return + */ + RequestLog finishMydoJob(Map param); + + /** + * 下发agv任务 + * @param agvQueue + * @return + */ + public GenAgvSchedulingTaskResponse requestAgvQueue(AgvQueue agvQueue); + + + /** + * 取消ctu任务 + * @param jobParam + * @return + */ + AjaxResult cancelMydoJob(JobParam jobParam); + + + + /** + * 放货有货,重新分配货位 + * @param jobParam + * @return + */ + AjaxResult reAssign(JobParam jobParam); + + /** + * 取货无货,重新分配单据 + * @param jobParam + * @return + */ + AjaxResult reMaterial(JobParam jobParam); + + /** + * 入库请求生成作业 + */ + public AjaxResult createInMydoJob(Map param); + + public AjaxResult selectMydoJobByNoTrayCode(Map param); + + AjaxResult receiveOutMydoJob(ReceiveJob receiveOutJob); + + AjaxResult receiveInMydoJob(ReceiveJob receiveInJob); + + AjaxResult selectMydoJobByTrayCodeAndEntranceId(HashMap paramMap); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/IRequestLogService.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IRequestLogService.java new file mode 100644 index 0000000..25062d0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/IRequestLogService.java @@ -0,0 +1,78 @@ +package com.ruoyi.api.service; + +import java.util.List; +import com.ruoyi.api.domain.RequestLog; + +/** + * 接口记录Service接口 + * + * @author ruoyi + * @date 2025-06-11 + */ +public interface IRequestLogService +{ + /** + * 查询接口记录 + * + * @param requestId 接口记录主键 + * @return 接口记录 + */ + public RequestLog selectRequestLogByRequestId(Long requestId); + + /** + * 查询接口记录列表 + * + * @param requestLog 接口记录 + * @return 接口记录集合 + */ + public List selectRequestLogList(RequestLog requestLog); + + /** + * 新增接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + public int insertRequestLog(RequestLog requestLog); + + /** + * 修改接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + public int updateRequestLog(RequestLog requestLog); + + /** + * 批量删除接口记录 + * + * @param requestIds 需要删除的接口记录主键集合 + * @return 结果 + */ + public int deleteRequestLogByRequestIds(Long[] requestIds); + + /** + * 删除接口记录信息 + * + * @param requestId 接口记录主键 + * @return 结果 + */ + public int deleteRequestLogByRequestId(Long requestId); + + /** + * 发送请求,并插入日志 + * @param url + * @param param + * @param endPoint + * @return + */ + public RequestLog getRequestLog(String url, String param, String endPoint); + + /** + * 接收请求,并插入日志 + * @param url + * @param param + * @return + */ + public RequestLog getRequestLog(String url, String param); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/ApiServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/ApiServiceImpl.java new file mode 100644 index 0000000..9c31fe3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/ApiServiceImpl.java @@ -0,0 +1,394 @@ +package com.ruoyi.api.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.api.domain.InterfaceReceive; +import com.ruoyi.api.domain.RequestLog; +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackRequest; +import com.ruoyi.api.domain.agv.agvCallback.AgvCallbackResponse; +import com.ruoyi.api.domain.agv.cancelTask.CancelTaskRequest; +import com.ruoyi.api.domain.agv.cancelTask.CancelTaskResponse; +import com.ruoyi.api.domain.agv.continueTask.ContinueTaskRequest; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskRequest; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskResponse; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.PositionCodeData; +import com.ruoyi.api.domain.wcs2wms.JobParam; +import com.ruoyi.api.domain.wcs2wms.ReceiveJob; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.ruoyi.common.Constant.*; + +/** + * 接口记录Service业务层处理 + * + * @author ruoyi + * @date 2025-06-11 + */ +@Service +@Slf4j +public class ApiServiceImpl implements IApiService { + @Autowired + private RequestLogServiceImpl requestLogService; + @Autowired + private IAgvQueueService agvQueueService; + @Autowired + private IMydoJobService mydoJobService; + + + + /** + * 下发agv编码 + * + * @param genAgvSchedulingTaskRequest + * @return + */ + @Override + public GenAgvSchedulingTaskResponse genAgvSchedulingTask(GenAgvSchedulingTaskRequest genAgvSchedulingTaskRequest) { + RequestLog requestLog = requestLogService.getRequestLog(GEN_AGV_SCHEDULING_TASK, "生成AGV任务", JSONUtil.toJsonStr(genAgvSchedulingTaskRequest)); + if ("1".equals(requestLog.getIsSuccessful())) { + return BeanUtil.toBean(requestLog.getResponseBody(), GenAgvSchedulingTaskResponse.class); + } else { + //todo 需要调试 + GenAgvSchedulingTaskResponse response = new GenAgvSchedulingTaskResponse(); + response.setCode(500); + response.setMessage("请求失败"); + //todo 需要调试小车请求 + response.setCode(0); + response.setMessage("模拟测试"); + return response; + } + } + + /** + * agv执行状态返回接口 + * + * @param request + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public AgvCallbackResponse agvCallback(AgvCallbackRequest request) { + RequestLog requestLog = requestLogService.getRequestLog(JSONUtil.toJsonStr(request), "AGV任务执行通知"); + AgvCallbackResponse response = new AgvCallbackResponse(0, "成功", ""); + try { + agvCallbackInfo(request); + requestLog.setResponseBody(JSONUtil.toJsonStr(response)); + requestLog.setResponseStatus("1"); + requestLog.setIsSuccessful("1"); + return new AgvCallbackResponse(0, "成功", ""); + } catch (RuntimeException e) { + requestLog.setErrorDetails(e.getMessage()); + requestLog.setIsSuccessful("0"); + requestLog.setResponseBody(JSONUtil.toJsonStr(response)); + requestLog.setResponseStatus("0"); + return new AgvCallbackResponse(1, "失败", ""); + } finally { + requestLogService.insertRequestLog(requestLog); + } + } + + /** + * agv搬运作业 + * + * @return + */ + @Override + public AjaxResult receiveAgvMoveJob(Map param) { + String startPoint = param.get("startPoint"); + String endPoint = param.get("endPoint"); + String traySize = param.get("traySize"); + String wmsTaskNo = param.get("wmsTaskNo"); + + AgvQueue agvQueue = createAgvQueue(startPoint, endPoint, param.get("trayCode"), param.get("taskNo"), "1", traySize, wmsTaskNo); + GenAgvSchedulingTaskResponse genAgvSchedulingTaskResponse = requestAgvQueue(agvQueue); + if (genAgvSchedulingTaskResponse.getCode() == 0) { + return AjaxResult.success(); + } + return AjaxResult.error(genAgvSchedulingTaskResponse.getMessage()); + + } + + /** + * 出库到达拣选口 + * + * @param param + * @return + */ + @Override + public RequestLog finishMydoJob(Map param) { + return requestLogService.getRequestLog(JOB_FINISH_URL, "agv任务完成", JSONUtil.toJsonStr(param)); + } + + + /** + * 处理agv状态返回 + * + * @param request + */ + private void agvCallbackInfo(AgvCallbackRequest request) { + AgvQueue agvQueue = agvQueueService.selectAgvQueueByTaskCode(request.getTaskCode()); + if (agvQueue != null) { + String method = request.getMethod(); + switch (method) { + case "applyin": + agvQueue.setStatus("1"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("【AGV请求取货】任务号:{}", request.getTaskCode()); + break; + case "start": + agvQueue.setStatus("3"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("【AGV任务开始】任务号:{}", request.getTaskCode()); + break; + case "outbin": + agvQueue.setStatus("4"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("【AGV走出储位】任务号:{}", request.getTaskCode()); + break; + case "applyout": + agvQueue.setStatus("5"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("【AGV请求放货】任务号:{}", request.getTaskCode()); + break; + case "end": + agvQueue.setStatus("7"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("【AGV任务结束】任务号:{}", request.getTaskCode()); + break; + default: + log.info("未知方法"); + } + + } + + + } + + /** + * 取消ctu任务 + * + * @param jobParam + * @return + */ + @Override + public AjaxResult cancelMydoJob(JobParam jobParam) { + + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(jobParam.getTaskNo())); + if(mydoJob==null){ + return AjaxResult.error("任务不存在"); + } + if("1".equals(mydoJob.getStatus())){ + mydoJob.setStatus("5"); + mydoJobService.insertHistory(mydoJob.getJobId()); + mydoJobService.deleteMydoJobByJobId(mydoJob.getJobId()); + return AjaxResult.success(); + }else{ + return AjaxResult.error("任务已开始"); + } + } + + + + + @Override + public AjaxResult reAssign(JobParam jobParam) { + RequestLog requestLog = requestLogService.getRequestLog(RE_ASSIGN_URL, "重新分配货位", JSONUtil.toJsonStr(jobParam)); + if ("1".equals(requestLog.getIsSuccessful())){ + InterfaceReceive bean = JSONUtil.toBean(requestLog.getResponseBody(), InterfaceReceive.class); + MydoJob wmsMydoJob = BeanUtil.toBean(bean.getData(), MydoJob.class); + MydoJob mydoJob = mydoJobService.selectMydoJobByJobId(wmsMydoJob.getJobId()); + mydoJob.setCargoSpaceId(wmsMydoJob.getCargoSpaceId()); + mydoJobService.updateMydoJob(mydoJob); + return AjaxResult.success(); + } + return AjaxResult.error(); + } + + @Override + public AjaxResult reMaterial(JobParam jobParam) { + RequestLog requestLog = requestLogService.getRequestLog(RE_MATERIAL_URL, "重新分配物料", JSONUtil.toJsonStr(jobParam)); + if ("1".equals(requestLog.getIsSuccessful())){ + return AjaxResult.success(); + } + return AjaxResult.error(); + } + + + /** + * 生成agv队列 + * + * @param code1 起点 + * @param code2 终点 + * @param wmsTrayCode 容器码 托盘料箱码/容器码 + * @param wmsTaskNo mydojob 任务 + * @param wmsTaskType 1-入库 2-出库 3-agv搬运 + * @return + */ + @Override + public AgvQueue createAgvQueue(String code1, String code2, String wmsTrayCode, String taskCode, String wmsTaskType, String traySize, String wmsTaskNo) { + //生成ctu执行队列 + AgvQueue agvQueue = new AgvQueue(); + String reqCode = IdUtil.fastSimpleUUID(); +// String taskCode = IdUtil.fastSimpleUUID(); + agvQueue.setReqCode(reqCode); + agvQueue.setReqTime(DateUtil.formatDateTime(DateUtil.date())); + agvQueue.setAgvType("AGV"); + agvQueue.setPositionCode1(code1); + agvQueue.setPositionCode2(code2); + agvQueue.setTaskType("F01"); + agvQueue.setCtnrType(traySize); + agvQueue.setCtnrCode("0"); + agvQueue.setCtnrNum("0"); + agvQueue.setTaskMode("0"); + agvQueue.setPriority("10"); + agvQueue.setRequest("0"); + agvQueue.setWmsTraycode(wmsTrayCode); + agvQueue.setWmsTaskNo(wmsTaskNo); + agvQueue.setStatus("0"); + agvQueue.setTaskCode(taskCode); + agvQueue.setWmsTaskType(wmsTaskType); + agvQueueService.insertAgvQueue(agvQueue); + return agvQueue; + } + + /** + * 取消AGV任务 + * + * @param cancelTaskRequest + * @return + */ + @Override + public CancelTaskResponse cancelTask(CancelTaskRequest cancelTaskRequest) { + RequestLog requestLog = requestLogService.getRequestLog(CANCEL_TASK_URL, "取消任务", JSONUtil.toJsonStr(cancelTaskRequest)); + return BeanUtil.toBean(requestLog.getResponseBody(), CancelTaskResponse.class); + + } + + /** + * 继续agv任务 + * + * @param agvQueue + * @return + */ + @Override + public AjaxResult continueTask(AgvQueue agvQueue) { + ContinueTaskRequest continueTaskRequest = new ContinueTaskRequest(); + continueTaskRequest.setReqCode(IdUtil.fastSimpleUUID()); + continueTaskRequest.setReqTime(DateUtil.formatDateTime(DateUtil.date())); + continueTaskRequest.setTaskCode(agvQueue.getTaskCode()); + RequestLog requestLog = requestLogService.getRequestLog(CONTINUE_TASK_URL, "继续执行任务", JSONUtil.toJsonStr(continueTaskRequest)); + if ("1".equals(requestLog.getIsSuccessful())) { + return AjaxResult.success(); + } else { + return AjaxResult.error(); + } + + } + + + /** + * 请求海康生成agv任务,并修改wms任务状态 + * + * @param agvQueue + */ + @Override + public GenAgvSchedulingTaskResponse requestAgvQueue(AgvQueue agvQueue) { + //todo 请求ctu + GenAgvSchedulingTaskRequest genAgvSchedulingTaskRequest = new GenAgvSchedulingTaskRequest(); + genAgvSchedulingTaskRequest.setReqCode(agvQueue.getReqCode()); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setPriority("10"); + genAgvSchedulingTaskRequest.setTaskTyp("F01"); + genAgvSchedulingTaskRequest.setCtnrCode("0"); + genAgvSchedulingTaskRequest.setCtnrNum("0"); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setTaskCode(agvQueue.getTaskCode()); + genAgvSchedulingTaskRequest.setAgvTyp("AGV"); + genAgvSchedulingTaskRequest.setPositionSelStrategy("0"); + List positionCodePath = new ArrayList<>(); + positionCodePath.add(new PositionCodeData(agvQueue.getPositionCode1(), "0")); + positionCodePath.add(new PositionCodeData(agvQueue.getPositionCode2(), "0")); + genAgvSchedulingTaskRequest.setPositionCodePath(positionCodePath); + return genAgvSchedulingTask(genAgvSchedulingTaskRequest); + } + + @Override + public AjaxResult createInMydoJob(Map param) { + RequestLog requestLog = requestLogService.getRequestLog(CREATE_IN_MYDO_JOB_URL, "入库请求生成作业", JSONUtil.toJsonStr(param)); + if ("1".equals(requestLog.getIsSuccessful())) { + InterfaceReceive bean = JSONUtil.toBean(requestLog.getResponseBody(), InterfaceReceive.class); + return AjaxResult.success(bean.getData()); + } else { + return AjaxResult.error(); + } + } + + + @Override + public AjaxResult receiveOutMydoJob(ReceiveJob receiveOutJob) { + MydoJob mydoJob = new MydoJob(); + mydoJob.setJobId(receiveOutJob.getJobId()); + mydoJob.setTaskNo(receiveOutJob.getTaskNo()); + mydoJob.setCargoSpaceId(receiveOutJob.getCargoSpaceId()); + mydoJob.setExitId(receiveOutJob.getExitId()); + mydoJob.setInOrOut(receiveOutJob.getInOrOut()); + mydoJob.setStatus("1"); + mydoJob.setTrayCode(receiveOutJob.getTrayCode()); + mydoJob.setRoadwayId(receiveOutJob.getRoadwayId()); + mydoJob.setCreateTime(DateUtils.getNowDate()); + mydoJob.setExecutionPhase("0"); + int i = mydoJobService.insertMydoJob(mydoJob); + if (i > 0) { + return AjaxResult.success(); + }else{ + return AjaxResult.error(); + } + + } + + @Override + public AjaxResult receiveInMydoJob(ReceiveJob receiveInJob) { + return null; + } + + @Override + public AjaxResult selectMydoJobByTrayCodeAndEntranceId(HashMap paramMap) { + RequestLog requestLog = requestLogService.getRequestLog(REQUEST_ENTER_URL, "请求获取入库作业", JSONUtil.toJsonStr(paramMap)); + if ("1".equals(requestLog.getIsSuccessful())) { + InterfaceReceive bean = JSONUtil.toBean(requestLog.getResponseBody(), InterfaceReceive.class); + return AjaxResult.success(bean.getData()); + } else { + return AjaxResult.error(); + } + } + + + @Override + public AjaxResult selectMydoJobByNoTrayCode(Map param) { + RequestLog requestLog = requestLogService.getRequestLog(REQUEST_MYDO_JOB_URL, "入库请求补码作业", JSONUtil.toJsonStr(param)); + if ("1".equals(requestLog.getIsSuccessful())) { + InterfaceReceive bean = JSONUtil.toBean(requestLog.getResponseBody(), InterfaceReceive.class); + return AjaxResult.success(bean.getData()); + } else { + return AjaxResult.error(); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/RequestLogServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/RequestLogServiceImpl.java new file mode 100644 index 0000000..0e7a619 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/api/service/impl/RequestLogServiceImpl.java @@ -0,0 +1,174 @@ +package com.ruoyi.api.service.impl; + +import java.util.Date; +import java.util.List; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import cn.hutool.http.HttpUtil; +import cn.hutool.json.JSONUtil; +import com.ruoyi.api.domain.InterfaceReceive; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.api.mapper.RequestLogMapper; +import com.ruoyi.api.domain.RequestLog; +import com.ruoyi.api.service.IRequestLogService; + +/** + * 接口记录Service业务层处理 + * + * @author ruoyi + * @date 2025-06-11 + */ +@Service +@Slf4j +public class RequestLogServiceImpl implements IRequestLogService +{ + @Autowired + private RequestLogMapper requestLogMapper; + + /** + * 查询接口记录 + * + * @param requestId 接口记录主键 + * @return 接口记录 + */ + @Override + public RequestLog selectRequestLogByRequestId(Long requestId) + { + return requestLogMapper.selectRequestLogByRequestId(requestId); + } + + /** + * 查询接口记录列表 + * + * @param requestLog 接口记录 + * @return 接口记录 + */ + @Override + public List selectRequestLogList(RequestLog requestLog) + { + return requestLogMapper.selectRequestLogList(requestLog); + } + + /** + * 新增接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + @Override + public int insertRequestLog(RequestLog requestLog) + { + if ("1".equals(requestLog.getIsSuccessful())){ + return requestLogMapper.insertRequestLog(requestLog); + }else { + //log.error("请求失败:" + requestLog.getErrorDetails()); + return 0; + } + } + + /** + * 修改接口记录 + * + * @param requestLog 接口记录 + * @return 结果 + */ + @Override + public int updateRequestLog(RequestLog requestLog) + { + return requestLogMapper.updateRequestLog(requestLog); + } + + /** + * 批量删除接口记录 + * + * @param requestIds 需要删除的接口记录主键 + * @return 结果 + */ + @Override + public int deleteRequestLogByRequestIds(Long[] requestIds) + { + return requestLogMapper.deleteRequestLogByRequestIds(requestIds); + } + + /** + * 删除接口记录信息 + * + * @param requestId 接口记录主键 + * @return 结果 + */ + @Override + public int deleteRequestLogByRequestId(Long requestId) + { + return requestLogMapper.deleteRequestLogByRequestId(requestId); + } + + + /** + * 插入请求日志 + * + * @param url 请求url + * @param param 参数 + * @return requestLog + */ + @Override + public RequestLog getRequestLog(String url, String endPoint,String param) { + RequestLog requestLog = new RequestLog(); + Date requestTime = new Date(); + requestLog.setRequestTime(DateUtil.format(requestTime, "yyyy-MM-dd HH:mm:ss")); + requestLog.setSourceSystem("wcs"); + requestLog.setTargetSystem("wms"); + requestLog.setEndpoint(endPoint); + requestLog.setRequestMethod("post"); + requestLog.setRequestHeaders(null); + requestLog.setRequestBody(param); + requestLog.setRequestUrl(url); + try { + String resStr = HttpUtil.post(url, param); + requestLog.setResponseBody(resStr); + InterfaceReceive bean = JSONUtil.toBean(resStr, InterfaceReceive.class); + Date responseTime =new Date(); + if (bean.getCode() == 200) { + requestLog.setResponseTime(DateUtil.format(responseTime, "yyyy-MM-dd HH:mm:ss")); + requestLog.setResponseStatus("1"); + requestLog.setDuration(Convert.toStr(DateUtil.between(requestTime, responseTime, DateUnit.MS))); + requestLog.setIsSuccessful("1"); + }else { + requestLog.setResponseStatus("0"); + requestLog.setErrorDetails(bean.getMsg()); + requestLog.setIsSuccessful("0"); + } + + } catch (Exception e) { + requestLog.setErrorDetails(e.getMessage()); + requestLog.setIsSuccessful("0"); + requestLog.setResponseStatus("0"); + } finally { + insertRequestLog(requestLog); + } + return requestLog; + } + + /** + * 插入请求日志 + * + * @return requestLog + */ + @Override + public RequestLog getRequestLog(String param, String endPoint) { + RequestLog requestLog = new RequestLog(); + Date requestTime = new Date(); + requestLog.setRequestTime(DateUtil.format(requestTime, "yyyy-MM-dd HH:mm:ss")); + requestLog.setRequestBody(param); + requestLog.setSourceSystem("wms"); + requestLog.setTargetSystem("wcs"); + requestLog.setEndpoint(endPoint); + requestLog.setRequestMethod("post"); + requestLog.setResponseTime(DateUtil.format(requestTime, "yyyy-MM-dd HH:mm:ss")); + requestLog.setDuration(Convert.toStr(DateUtil.between(requestTime, requestTime, DateUnit.MS))); + return requestLog; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/controller/BasCargoSpaceController.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/controller/BasCargoSpaceController.java new file mode 100644 index 0000000..0b7e583 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/controller/BasCargoSpaceController.java @@ -0,0 +1,104 @@ +package com.ruoyi.bas.cargospace.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 库存信息Controller + * + * @author ruoyi + * @date 2025-09-23 + */ +@RestController +@RequestMapping("/bas/cargoSpace") +public class BasCargoSpaceController extends BaseController +{ + @Autowired + private IBasCargoSpaceService basCargoSpaceService; + + /** + * 查询库存信息列表 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:list')") + @GetMapping("/list") + public TableDataInfo list(BasCargoSpace basCargoSpace) + { + startPage(); + List list = basCargoSpaceService.selectBasCargoSpaceList(basCargoSpace); + return getDataTable(list); + } + + /** + * 导出库存信息列表 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:export')") + @Log(title = "库存信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BasCargoSpace basCargoSpace) + { + List list = basCargoSpaceService.selectBasCargoSpaceList(basCargoSpace); + ExcelUtil util = new ExcelUtil(BasCargoSpace.class); + util.exportExcel(response, list, "库存信息数据"); + } + + /** + * 获取库存信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:query')") + @GetMapping(value = "/{cargoSpaceId}") + public AjaxResult getInfo(@PathVariable("cargoSpaceId") String cargoSpaceId) + { + return success(basCargoSpaceService.selectBasCargoSpaceByCargoSpaceId(cargoSpaceId)); + } + + /** + * 新增库存信息 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:add')") + @Log(title = "库存信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody BasCargoSpace basCargoSpace) + { + return toAjax(basCargoSpaceService.insertBasCargoSpace(basCargoSpace)); + } + + /** + * 修改库存信息 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:edit')") + @Log(title = "库存信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody BasCargoSpace basCargoSpace) + { + return toAjax(basCargoSpaceService.updateBasCargoSpace(basCargoSpace)); + } + + /** + * 删除库存信息 + */ + @PreAuthorize("@ss.hasPermi('bas:cargoSpace:remove')") + @Log(title = "库存信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{cargoSpaceIds}") + public AjaxResult remove(@PathVariable String[] cargoSpaceIds) + { + return toAjax(basCargoSpaceService.deleteBasCargoSpaceByCargoSpaceIds(cargoSpaceIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/domain/BasCargoSpace.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/domain/BasCargoSpace.java new file mode 100644 index 0000000..552165b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/domain/BasCargoSpace.java @@ -0,0 +1,324 @@ +package com.ruoyi.bas.cargospace.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 库存信息对象 bas_cargo_space + * + * @author ruoyi + * @date 2025-09-23 + */ +public class BasCargoSpace extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 货位id 主键 */ + private String cargoSpaceId; + + /** 仓库id(不允许为空) */ + @Excel(name = "仓库id", readConverterExp = "不=允许为空") + private String warehouseId; + + /** 巷道id(允许为空) */ + @Excel(name = "巷道id", readConverterExp = "允=许为空") + private String roadwayId; + + /** 货区id(允许为空) */ + @Excel(name = "货区id", readConverterExp = "允=许为空") + private String cargoAreaId; + + /** 货位类型(允许为空) */ + @Excel(name = "货位类型", readConverterExp = "允=许为空") + private String theType; + + /** 货位类型名称 */ + @Excel(name = "货位类型名称") + private String theTypeName; + + /** 货位名称(不允许为空) */ + @Excel(name = "货位名称", readConverterExp = "不=允许为空") + private String cargoSpaceName; + + /** 排(不允许为空) */ + @Excel(name = "排", readConverterExp = "不=允许为空") + private Long thePlatoon; + + /** 列(不允许为空) */ + @Excel(name = "列", readConverterExp = "不=允许为空") + private Long theColumn; + + /** 层(不允许为空) */ + @Excel(name = "层", readConverterExp = "不=允许为空") + private Long theFloor; + + /** 货位状态1空2满 3入库分配 4出库分配 5入库盘点 6出库盘点 */ + @Excel(name = "货位状态1空2满 3入库分配 4出库分配 5入库盘点 6出库盘点") + private String status; + + /** 是否锁定(不允许为空) +Y锁、N开 */ + @Excel(name = "是否锁定", readConverterExp = "不=允许为空") + private String locked; + + /** 托盘条码(允许为空) */ + @Excel(name = "托盘条码", readConverterExp = "允=许为空") + private String trayCode; + + /** 货位高度 */ + @Excel(name = "货位高度") + private String cargoHight; + + /** 是否双伸 Y N */ + @Excel(name = "是否双伸 Y N ") + private String issDouble; + + /** 双货位分组编码(标识区分) */ + @Excel(name = "双货位分组编码", readConverterExp = "标=识区分") + private String groupCodingNo; + + /** 双货位分组编码1、2… */ + @Excel(name = "双货位分组编码1、2… ") + private String groupCoding; + + /** 双货位标识 0 外1内 */ + @Excel(name = "双货位标识 0 外1内") + private String cargoSpaceLogo; + + /** 本仓库区域 1--8 A +9--15 B */ + @Excel(name = "本仓库区域") + private String thisCargoArea; + + /** wms货位id */ + @Excel(name = "wms货位id") + private String wmsCargoSpaceId; + + public void setCargoSpaceId(String cargoSpaceId) + { + this.cargoSpaceId = cargoSpaceId; + } + + public String getCargoSpaceId() + { + return cargoSpaceId; + } + + public void setWarehouseId(String warehouseId) + { + this.warehouseId = warehouseId; + } + + public String getWarehouseId() + { + return warehouseId; + } + + public void setRoadwayId(String roadwayId) + { + this.roadwayId = roadwayId; + } + + public String getRoadwayId() + { + return roadwayId; + } + + public void setCargoAreaId(String cargoAreaId) + { + this.cargoAreaId = cargoAreaId; + } + + public String getCargoAreaId() + { + return cargoAreaId; + } + + public void setTheType(String theType) + { + this.theType = theType; + } + + public String getTheType() + { + return theType; + } + + public void setTheTypeName(String theTypeName) + { + this.theTypeName = theTypeName; + } + + public String getTheTypeName() + { + return theTypeName; + } + + public void setCargoSpaceName(String cargoSpaceName) + { + this.cargoSpaceName = cargoSpaceName; + } + + public String getCargoSpaceName() + { + return cargoSpaceName; + } + + public void setThePlatoon(Long thePlatoon) + { + this.thePlatoon = thePlatoon; + } + + public Long getThePlatoon() + { + return thePlatoon; + } + + public void setTheColumn(Long theColumn) + { + this.theColumn = theColumn; + } + + public Long getTheColumn() + { + return theColumn; + } + + public void setTheFloor(Long theFloor) + { + this.theFloor = theFloor; + } + + public Long getTheFloor() + { + return theFloor; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setLocked(String locked) + { + this.locked = locked; + } + + public String getLocked() + { + return locked; + } + + public void setTrayCode(String trayCode) + { + this.trayCode = trayCode; + } + + public String getTrayCode() + { + return trayCode; + } + + public void setCargoHight(String cargoHight) + { + this.cargoHight = cargoHight; + } + + public String getCargoHight() + { + return cargoHight; + } + + public void setIssDouble(String issDouble) + { + this.issDouble = issDouble; + } + + public String getIssDouble() + { + return issDouble; + } + + public void setGroupCodingNo(String groupCodingNo) + { + this.groupCodingNo = groupCodingNo; + } + + public String getGroupCodingNo() + { + return groupCodingNo; + } + + public void setGroupCoding(String groupCoding) + { + this.groupCoding = groupCoding; + } + + public String getGroupCoding() + { + return groupCoding; + } + + public void setCargoSpaceLogo(String cargoSpaceLogo) + { + this.cargoSpaceLogo = cargoSpaceLogo; + } + + public String getCargoSpaceLogo() + { + return cargoSpaceLogo; + } + + public void setThisCargoArea(String thisCargoArea) + { + this.thisCargoArea = thisCargoArea; + } + + public String getThisCargoArea() + { + return thisCargoArea; + } + + public void setWmsCargoSpaceId(String wmsCargoSpaceId) + { + this.wmsCargoSpaceId = wmsCargoSpaceId; + } + + public String getWmsCargoSpaceId() + { + return wmsCargoSpaceId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("cargoSpaceId", getCargoSpaceId()) + .append("warehouseId", getWarehouseId()) + .append("roadwayId", getRoadwayId()) + .append("cargoAreaId", getCargoAreaId()) + .append("theType", getTheType()) + .append("theTypeName", getTheTypeName()) + .append("cargoSpaceName", getCargoSpaceName()) + .append("thePlatoon", getThePlatoon()) + .append("theColumn", getTheColumn()) + .append("theFloor", getTheFloor()) + .append("status", getStatus()) + .append("locked", getLocked()) + .append("trayCode", getTrayCode()) + .append("cargoHight", getCargoHight()) + .append("issDouble", getIssDouble()) + .append("groupCodingNo", getGroupCodingNo()) + .append("groupCoding", getGroupCoding()) + .append("cargoSpaceLogo", getCargoSpaceLogo()) + .append("thisCargoArea", getThisCargoArea()) + .append("wmsCargoSpaceId", getWmsCargoSpaceId()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/mapper/BasCargoSpaceMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/mapper/BasCargoSpaceMapper.java new file mode 100644 index 0000000..0d7ee0c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/mapper/BasCargoSpaceMapper.java @@ -0,0 +1,63 @@ +package com.ruoyi.bas.cargospace.mapper; + +import java.util.List; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; + +/** + * 库存信息Mapper接口 + * + * @author ruoyi + * @date 2025-09-23 + */ +public interface BasCargoSpaceMapper +{ + /** + * 查询库存信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 库存信息 + */ + public BasCargoSpace selectBasCargoSpaceByCargoSpaceId(String cargoSpaceId); + + /** + * 查询库存信息列表 + * + * @param basCargoSpace 库存信息 + * @return 库存信息集合 + */ + public List selectBasCargoSpaceList(BasCargoSpace basCargoSpace); + + /** + * 新增库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + public int insertBasCargoSpace(BasCargoSpace basCargoSpace); + + /** + * 修改库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + public int updateBasCargoSpace(BasCargoSpace basCargoSpace); + + /** + * 删除库存信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 结果 + */ + public int deleteBasCargoSpaceByCargoSpaceId(String cargoSpaceId); + + /** + * 批量删除库存信息 + * + * @param cargoSpaceIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteBasCargoSpaceByCargoSpaceIds(String[] cargoSpaceIds); + + BasCargoSpace selectBasCargoSpaceByWmsCargoSpaceId(String cargoSpaceId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/IBasCargoSpaceService.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/IBasCargoSpaceService.java new file mode 100644 index 0000000..3f0e9c3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/IBasCargoSpaceService.java @@ -0,0 +1,63 @@ +package com.ruoyi.bas.cargospace.service; + +import java.util.List; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; + +/** + * 库存信息Service接口 + * + * @author ruoyi + * @date 2025-09-23 + */ +public interface IBasCargoSpaceService +{ + /** + * 查询库存信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 库存信息 + */ + public BasCargoSpace selectBasCargoSpaceByCargoSpaceId(String cargoSpaceId); + + /** + * 查询库存信息列表 + * + * @param basCargoSpace 库存信息 + * @return 库存信息集合 + */ + public List selectBasCargoSpaceList(BasCargoSpace basCargoSpace); + + /** + * 新增库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + public int insertBasCargoSpace(BasCargoSpace basCargoSpace); + + /** + * 修改库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + public int updateBasCargoSpace(BasCargoSpace basCargoSpace); + + /** + * 批量删除库存信息 + * + * @param cargoSpaceIds 需要删除的库存信息主键集合 + * @return 结果 + */ + public int deleteBasCargoSpaceByCargoSpaceIds(String[] cargoSpaceIds); + + /** + * 删除库存信息信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 结果 + */ + public int deleteBasCargoSpaceByCargoSpaceId(String cargoSpaceId); + + BasCargoSpace selectBasCargoSpaceByWmsCargoSpaceId(String cargoSpaceId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/impl/BasCargoSpaceServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/impl/BasCargoSpaceServiceImpl.java new file mode 100644 index 0000000..b1be140 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/cargospace/service/impl/BasCargoSpaceServiceImpl.java @@ -0,0 +1,98 @@ +package com.ruoyi.bas.cargospace.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.bas.cargospace.mapper.BasCargoSpaceMapper; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; + +/** + * 库存信息Service业务层处理 + * + * @author ruoyi + * @date 2025-09-23 + */ +@Service +public class BasCargoSpaceServiceImpl implements IBasCargoSpaceService +{ + @Autowired + private BasCargoSpaceMapper basCargoSpaceMapper; + + /** + * 查询库存信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 库存信息 + */ + @Override + public BasCargoSpace selectBasCargoSpaceByCargoSpaceId(String cargoSpaceId) + { + return basCargoSpaceMapper.selectBasCargoSpaceByCargoSpaceId(cargoSpaceId); + } + + /** + * 查询库存信息列表 + * + * @param basCargoSpace 库存信息 + * @return 库存信息 + */ + @Override + public List selectBasCargoSpaceList(BasCargoSpace basCargoSpace) + { + return basCargoSpaceMapper.selectBasCargoSpaceList(basCargoSpace); + } + + /** + * 新增库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + @Override + public int insertBasCargoSpace(BasCargoSpace basCargoSpace) + { + return basCargoSpaceMapper.insertBasCargoSpace(basCargoSpace); + } + + /** + * 修改库存信息 + * + * @param basCargoSpace 库存信息 + * @return 结果 + */ + @Override + public int updateBasCargoSpace(BasCargoSpace basCargoSpace) + { + return basCargoSpaceMapper.updateBasCargoSpace(basCargoSpace); + } + + /** + * 批量删除库存信息 + * + * @param cargoSpaceIds 需要删除的库存信息主键 + * @return 结果 + */ + @Override + public int deleteBasCargoSpaceByCargoSpaceIds(String[] cargoSpaceIds) + { + return basCargoSpaceMapper.deleteBasCargoSpaceByCargoSpaceIds(cargoSpaceIds); + } + + /** + * 删除库存信息信息 + * + * @param cargoSpaceId 库存信息主键 + * @return 结果 + */ + @Override + public int deleteBasCargoSpaceByCargoSpaceId(String cargoSpaceId) + { + return basCargoSpaceMapper.deleteBasCargoSpaceByCargoSpaceId(cargoSpaceId); + } + + @Override + public BasCargoSpace selectBasCargoSpaceByWmsCargoSpaceId(String cargoSpaceId) { + return basCargoSpaceMapper.selectBasCargoSpaceByWmsCargoSpaceId(cargoSpaceId); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/controller/BasFluentFrameController.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/controller/BasFluentFrameController.java new file mode 100644 index 0000000..a598b39 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/controller/BasFluentFrameController.java @@ -0,0 +1,104 @@ +package com.ruoyi.bas.fluent.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.bas.fluent.domain.BasFluentFrame; +import com.ruoyi.bas.fluent.service.IBasFluentFrameService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 流利架位置管理Controller + * + * @author ruoyi + * @date 2025-07-08 + */ +@RestController +@RequestMapping("/bas/fluent") +public class BasFluentFrameController extends BaseController +{ + @Autowired + private IBasFluentFrameService basFluentFrameService; + + /** + * 查询流利架位置管理列表 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:list')") + @GetMapping("/list") + public TableDataInfo list(BasFluentFrame basFluentFrame) + { + startPage(); + List list = basFluentFrameService.selectBasFluentFrameList(basFluentFrame); + return getDataTable(list); + } + + /** + * 导出流利架位置管理列表 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:export')") + @Log(title = "流利架位置管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, BasFluentFrame basFluentFrame) + { + List list = basFluentFrameService.selectBasFluentFrameList(basFluentFrame); + ExcelUtil util = new ExcelUtil(BasFluentFrame.class); + util.exportExcel(response, list, "流利架位置管理数据"); + } + + /** + * 获取流利架位置管理详细信息 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(basFluentFrameService.selectBasFluentFrameById(id)); + } + + /** + * 新增流利架位置管理 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:add')") + @Log(title = "流利架位置管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody BasFluentFrame basFluentFrame) + { + return toAjax(basFluentFrameService.insertBasFluentFrame(basFluentFrame)); + } + + /** + * 修改流利架位置管理 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:edit')") + @Log(title = "流利架位置管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody BasFluentFrame basFluentFrame) + { + return toAjax(basFluentFrameService.updateBasFluentFrame(basFluentFrame)); + } + + /** + * 删除流利架位置管理 + */ + @PreAuthorize("@ss.hasPermi('bas:fluent:remove')") + @Log(title = "流利架位置管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(basFluentFrameService.deleteBasFluentFrameByIds(ids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/domain/BasFluentFrame.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/domain/BasFluentFrame.java new file mode 100644 index 0000000..03ec5e9 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/domain/BasFluentFrame.java @@ -0,0 +1,142 @@ +package com.ruoyi.bas.fluent.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 流利架位置管理对象 bas_fluent_frame + * + * @author ruoyi + * @date 2025-07-08 + */ +public class BasFluentFrame extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** id */ + private Long id; + + /** 位置名称 */ + @Excel(name = "位置名称") + private String locationName; + + /** 货位类型 1-放货点 2-取货点 */ + @Excel(name = "货位类型 1-放货点 2-取货点") + private String type; + + /** 流利架id */ + @Excel(name = "流利架id") + private String frameId; + + /** 当前的箱子数量 */ + @Excel(name = "当前的箱子数量 ") + private Long boxNum; + + /** 当前位置的箱码 类型2专用 */ + @Excel(name = "当前位置的箱码 类型2专用") + private String trayCode; + + /** 组号 */ + @Excel(name = "组号") + private String groupId; + + /** 当前位置分配数量 */ + @Excel(name = "当前位置分配数量") + private Long allotNum; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setLocationName(String locationName) + { + this.locationName = locationName; + } + + public String getLocationName() + { + return locationName; + } + + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + + public void setFrameId(String frameId) + { + this.frameId = frameId; + } + + public String getFrameId() + { + return frameId; + } + + public void setBoxNum(Long boxNum) + { + this.boxNum = boxNum; + } + + public Long getBoxNum() + { + return boxNum; + } + + public void setTrayCode(String trayCode) + { + this.trayCode = trayCode; + } + + public String getTrayCode() + { + return trayCode; + } + + public void setGroupId(String groupId) + { + this.groupId = groupId; + } + + public String getGroupId() + { + return groupId; + } + + public void setAllotNum(Long allotNum) + { + this.allotNum = allotNum; + } + + public Long getAllotNum() + { + return allotNum; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("locationName", getLocationName()) + .append("type", getType()) + .append("frameId", getFrameId()) + .append("boxNum", getBoxNum()) + .append("trayCode", getTrayCode()) + .append("groupId", getGroupId()) + .append("allotNum", getAllotNum()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/mapper/BasFluentFrameMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/mapper/BasFluentFrameMapper.java new file mode 100644 index 0000000..4d4e281 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/mapper/BasFluentFrameMapper.java @@ -0,0 +1,63 @@ +package com.ruoyi.bas.fluent.mapper; + +import java.util.List; +import com.ruoyi.bas.fluent.domain.BasFluentFrame; + +/** + * 流利架位置管理Mapper接口 + * + * @author ruoyi + * @date 2025-07-08 + */ +public interface BasFluentFrameMapper +{ + /** + * 查询流利架位置管理 + * + * @param id 流利架位置管理主键 + * @return 流利架位置管理 + */ + public BasFluentFrame selectBasFluentFrameById(Long id); + + /** + * 查询流利架位置管理列表 + * + * @param basFluentFrame 流利架位置管理 + * @return 流利架位置管理集合 + */ + public List selectBasFluentFrameList(BasFluentFrame basFluentFrame); + + /** + * 新增流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + public int insertBasFluentFrame(BasFluentFrame basFluentFrame); + + /** + * 修改流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + public int updateBasFluentFrame(BasFluentFrame basFluentFrame); + + /** + * 删除流利架位置管理 + * + * @param id 流利架位置管理主键 + * @return 结果 + */ + public int deleteBasFluentFrameById(Long id); + + /** + * 批量删除流利架位置管理 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteBasFluentFrameByIds(Long[] ids); + + BasFluentFrame selectBasFluentFrameOrderByAllotNum(String exitId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/IBasFluentFrameService.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/IBasFluentFrameService.java new file mode 100644 index 0000000..8bb6385 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/IBasFluentFrameService.java @@ -0,0 +1,68 @@ +package com.ruoyi.bas.fluent.service; + +import java.util.List; +import com.ruoyi.bas.fluent.domain.BasFluentFrame; + +/** + * 流利架位置管理Service接口 + * + * @author ruoyi + * @date 2025-07-08 + */ +public interface IBasFluentFrameService +{ + /** + * 查询流利架位置管理 + * + * @param id 流利架位置管理主键 + * @return 流利架位置管理 + */ + public BasFluentFrame selectBasFluentFrameById(Long id); + + /** + * 查询流利架位置管理列表 + * + * @param basFluentFrame 流利架位置管理 + * @return 流利架位置管理集合 + */ + public List selectBasFluentFrameList(BasFluentFrame basFluentFrame); + + /** + * 新增流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + public int insertBasFluentFrame(BasFluentFrame basFluentFrame); + + /** + * 修改流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + public int updateBasFluentFrame(BasFluentFrame basFluentFrame); + + /** + * 批量删除流利架位置管理 + * + * @param ids 需要删除的流利架位置管理主键集合 + * @return 结果 + */ + public int deleteBasFluentFrameByIds(Long[] ids); + + /** + * 删除流利架位置管理信息 + * + * @param id 流利架位置管理主键 + * @return 结果 + */ + public int deleteBasFluentFrameById(Long id); + + /** + * 获取分配数量最少得流利架货位 + * @param exitId + * @return + */ + BasFluentFrame selectBasFluentFrameOrderByAllotNum(String exitId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/impl/BasFluentFrameServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/impl/BasFluentFrameServiceImpl.java new file mode 100644 index 0000000..b85d733 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/fluent/service/impl/BasFluentFrameServiceImpl.java @@ -0,0 +1,98 @@ +package com.ruoyi.bas.fluent.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.bas.fluent.mapper.BasFluentFrameMapper; +import com.ruoyi.bas.fluent.domain.BasFluentFrame; +import com.ruoyi.bas.fluent.service.IBasFluentFrameService; + +/** + * 流利架位置管理Service业务层处理 + * + * @author ruoyi + * @date 2025-07-08 + */ +@Service +public class BasFluentFrameServiceImpl implements IBasFluentFrameService +{ + @Autowired + private BasFluentFrameMapper basFluentFrameMapper; + + /** + * 查询流利架位置管理 + * + * @param id 流利架位置管理主键 + * @return 流利架位置管理 + */ + @Override + public BasFluentFrame selectBasFluentFrameById(Long id) + { + return basFluentFrameMapper.selectBasFluentFrameById(id); + } + + /** + * 查询流利架位置管理列表 + * + * @param basFluentFrame 流利架位置管理 + * @return 流利架位置管理 + */ + @Override + public List selectBasFluentFrameList(BasFluentFrame basFluentFrame) + { + return basFluentFrameMapper.selectBasFluentFrameList(basFluentFrame); + } + + /** + * 新增流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + @Override + public int insertBasFluentFrame(BasFluentFrame basFluentFrame) + { + return basFluentFrameMapper.insertBasFluentFrame(basFluentFrame); + } + + /** + * 修改流利架位置管理 + * + * @param basFluentFrame 流利架位置管理 + * @return 结果 + */ + @Override + public int updateBasFluentFrame(BasFluentFrame basFluentFrame) + { + return basFluentFrameMapper.updateBasFluentFrame(basFluentFrame); + } + + /** + * 批量删除流利架位置管理 + * + * @param ids 需要删除的流利架位置管理主键 + * @return 结果 + */ + @Override + public int deleteBasFluentFrameByIds(Long[] ids) + { + return basFluentFrameMapper.deleteBasFluentFrameByIds(ids); + } + + /** + * 删除流利架位置管理信息 + * + * @param id 流利架位置管理主键 + * @return 结果 + */ + @Override + public int deleteBasFluentFrameById(Long id) + { + return basFluentFrameMapper.deleteBasFluentFrameById(id); + } + + @Override + public BasFluentFrame selectBasFluentFrameOrderByAllotNum(String exitId) { + return basFluentFrameMapper.selectBasFluentFrameOrderByAllotNum(exitId); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/port/controller/WcsPortController.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/controller/WcsPortController.java new file mode 100644 index 0000000..4caf1ed --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/controller/WcsPortController.java @@ -0,0 +1,104 @@ +package com.ruoyi.bas.port.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.bas.port.domain.WcsPort; +import com.ruoyi.bas.port.service.IWcsPortService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 点位管理Controller + * + * @author ruoyi + * @date 2025-06-11 + */ +@RestController +@RequestMapping("/bas/port") +public class WcsPortController extends BaseController +{ + @Autowired + private IWcsPortService wcsPortService; + + /** + * 查询点位管理列表 + */ + @PreAuthorize("@ss.hasPermi('bas:port:list')") + @GetMapping("/list") + public TableDataInfo list(WcsPort wcsPort) + { + startPage(); + List list = wcsPortService.selectWcsPortList(wcsPort); + return getDataTable(list); + } + + /** + * 导出点位管理列表 + */ + @PreAuthorize("@ss.hasPermi('bas:port:export')") + @Log(title = "点位管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, WcsPort wcsPort) + { + List list = wcsPortService.selectWcsPortList(wcsPort); + ExcelUtil util = new ExcelUtil(WcsPort.class); + util.exportExcel(response, list, "点位管理数据"); + } + + /** + * 获取点位管理详细信息 + */ + @PreAuthorize("@ss.hasPermi('bas:port:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(wcsPortService.selectWcsPortById(id)); + } + + /** + * 新增点位管理 + */ + @PreAuthorize("@ss.hasPermi('bas:port:add')") + @Log(title = "点位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody WcsPort wcsPort) + { + return toAjax(wcsPortService.insertWcsPort(wcsPort)); + } + + /** + * 修改点位管理 + */ + @PreAuthorize("@ss.hasPermi('bas:port:edit')") + @Log(title = "点位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody WcsPort wcsPort) + { + return toAjax(wcsPortService.updateWcsPort(wcsPort)); + } + + /** + * 删除点位管理 + */ + @PreAuthorize("@ss.hasPermi('bas:port:remove')") + @Log(title = "点位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(wcsPortService.deleteWcsPortByIds(ids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/port/domain/WcsPort.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/domain/WcsPort.java new file mode 100644 index 0000000..fac2a52 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/domain/WcsPort.java @@ -0,0 +1,157 @@ +package com.ruoyi.bas.port.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 点位管理对象 wcs_port + * + * @author ruoyi + * @date 2025-06-11 + */ +public class WcsPort extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 点位编码 */ + @Excel(name = "点位编码") + private String portCode; + + /** 点位名称 */ + @Excel(name = "点位名称") + private String portName; + + /** 点位类型 */ + @Excel(name = "点位类型") + private String portType; + + /** 点位区域 */ + @Excel(name = "点位区域") + private String portRegion; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare1; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare2; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare3; + + /** 状态 */ + @Excel(name = "状态") + private String status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setPortCode(String portCode) + { + this.portCode = portCode; + } + + public String getPortCode() + { + return portCode; + } + + public void setPortName(String portName) + { + this.portName = portName; + } + + public String getPortName() + { + return portName; + } + + public void setPortType(String portType) + { + this.portType = portType; + } + + public String getPortType() + { + return portType; + } + + public void setPortRegion(String portRegion) + { + this.portRegion = portRegion; + } + + public String getPortRegion() + { + return portRegion; + } + + public void setSpare1(String spare1) + { + this.spare1 = spare1; + } + + public String getSpare1() + { + return spare1; + } + + public void setSpare2(String spare2) + { + this.spare2 = spare2; + } + + public String getSpare2() + { + return spare2; + } + + public void setSpare3(String spare3) + { + this.spare3 = spare3; + } + + public String getSpare3() + { + return spare3; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("portCode", getPortCode()) + .append("portName", getPortName()) + .append("portType", getPortType()) + .append("portRegion", getPortRegion()) + .append("spare1", getSpare1()) + .append("spare2", getSpare2()) + .append("spare3", getSpare3()) + .append("status", getStatus()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/port/mapper/WcsPortMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/mapper/WcsPortMapper.java new file mode 100644 index 0000000..e45c838 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/mapper/WcsPortMapper.java @@ -0,0 +1,68 @@ +package com.ruoyi.bas.port.mapper; + +import java.util.List; +import com.ruoyi.bas.port.domain.WcsPort; + +/** + * 点位管理Mapper接口 + * + * @author ruoyi + * @date 2025-06-11 + */ +public interface WcsPortMapper +{ + /** + * 查询点位管理 + * + * @param id 点位管理主键 + * @return 点位管理 + */ + public WcsPort selectWcsPortById(Long id); + + /** + * 查询点位管理列表 + * + * @param wcsPort 点位管理 + * @return 点位管理集合 + */ + public List selectWcsPortList(WcsPort wcsPort); + + /** + * 新增点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + public int insertWcsPort(WcsPort wcsPort); + + /** + * 修改点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + public int updateWcsPort(WcsPort wcsPort); + + /** + * 删除点位管理 + * + * @param id 点位管理主键 + * @return 结果 + */ + public int deleteWcsPortById(Long id); + + /** + * 批量删除点位管理 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteWcsPortByIds(Long[] ids); + + /** + * 根据code查询点位 + * @param point + * @return + */ + WcsPort selectWcsPortByPortCode(String point); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/IWcsPortService.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/IWcsPortService.java new file mode 100644 index 0000000..d20fb18 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/IWcsPortService.java @@ -0,0 +1,68 @@ +package com.ruoyi.bas.port.service; + +import java.util.List; +import com.ruoyi.bas.port.domain.WcsPort; + +/** + * 点位管理Service接口 + * + * @author ruoyi + * @date 2025-06-11 + */ +public interface IWcsPortService +{ + /** + * 查询点位管理 + * + * @param id 点位管理主键 + * @return 点位管理 + */ + public WcsPort selectWcsPortById(Long id); + + /** + * 查询点位管理列表 + * + * @param wcsPort 点位管理 + * @return 点位管理集合 + */ + public List selectWcsPortList(WcsPort wcsPort); + + /** + * 新增点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + public int insertWcsPort(WcsPort wcsPort); + + /** + * 修改点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + public int updateWcsPort(WcsPort wcsPort); + + /** + * 批量删除点位管理 + * + * @param ids 需要删除的点位管理主键集合 + * @return 结果 + */ + public int deleteWcsPortByIds(Long[] ids); + + /** + * 删除点位管理信息 + * + * @param id 点位管理主键 + * @return 结果 + */ + public int deleteWcsPortById(Long id); + + /** + * 根据点位代码查询点位名称 + * @param startPoint + * @return + */ + WcsPort selectWcsPortByPortCode(String startPoint); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/impl/WcsPortServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/impl/WcsPortServiceImpl.java new file mode 100644 index 0000000..83c851e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/bas/port/service/impl/WcsPortServiceImpl.java @@ -0,0 +1,98 @@ +package com.ruoyi.bas.port.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.bas.port.mapper.WcsPortMapper; +import com.ruoyi.bas.port.domain.WcsPort; +import com.ruoyi.bas.port.service.IWcsPortService; + +/** + * 点位管理Service业务层处理 + * + * @author ruoyi + * @date 2025-06-11 + */ +@Service +public class WcsPortServiceImpl implements IWcsPortService +{ + @Autowired + private WcsPortMapper wcsPortMapper; + + /** + * 查询点位管理 + * + * @param id 点位管理主键 + * @return 点位管理 + */ + @Override + public WcsPort selectWcsPortById(Long id) + { + return wcsPortMapper.selectWcsPortById(id); + } + + /** + * 查询点位管理列表 + * + * @param wcsPort 点位管理 + * @return 点位管理 + */ + @Override + public List selectWcsPortList(WcsPort wcsPort) + { + return wcsPortMapper.selectWcsPortList(wcsPort); + } + + /** + * 新增点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + @Override + public int insertWcsPort(WcsPort wcsPort) + { + return wcsPortMapper.insertWcsPort(wcsPort); + } + + /** + * 修改点位管理 + * + * @param wcsPort 点位管理 + * @return 结果 + */ + @Override + public int updateWcsPort(WcsPort wcsPort) + { + return wcsPortMapper.updateWcsPort(wcsPort); + } + + /** + * 批量删除点位管理 + * + * @param ids 需要删除的点位管理主键 + * @return 结果 + */ + @Override + public int deleteWcsPortByIds(Long[] ids) + { + return wcsPortMapper.deleteWcsPortByIds(ids); + } + + /** + * 删除点位管理信息 + * + * @param id 点位管理主键 + * @return 结果 + */ + @Override + public int deleteWcsPortById(Long id) + { + return wcsPortMapper.deleteWcsPortById(id); + } + + @Override + public WcsPort selectWcsPortByPortCode(String point) { + return wcsPortMapper.selectWcsPortByPortCode(point); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/Constant.java b/ruoyi-admin/src/main/java/com/ruoyi/common/Constant.java new file mode 100644 index 0000000..1e89b77 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/Constant.java @@ -0,0 +1,21 @@ +package com.ruoyi.common; + + +public class Constant { + + public static final String WMS_BASE_URL = "http://localhost:8080/api2wms/"; + public static final String RCS_BASE_URL = "http://127.0.0.1:8081/rcms-dps/rest/"; + + + public static final String JOB_FINISH_URL = WMS_BASE_URL+"jobFinish"; + public static final String CONTINUE_TASK_URL = RCS_BASE_URL+"continueTask"; + public static final String CANCEL_TASK_URL = RCS_BASE_URL+"queryAgvStatus";; + public static final String GEN_AGV_SCHEDULING_TASK = RCS_BASE_URL+"genAgvSchedulingTask";; + public static final String CREATE_IN_MYDO_JOB_URL = WMS_BASE_URL+"requestEnterByTrayCode"; + public static final String REQUEST_MYDO_JOB_URL = WMS_BASE_URL+"requestByNoTrayCode"; + public static final String RE_ASSIGN_URL = WMS_BASE_URL+"reAssign"; + public static final String RE_MATERIAL_URL = WMS_BASE_URL+"reMaterial"; + public static final String REQUEST_ENTER_URL = WMS_BASE_URL+"requestEnterByTrayCodeAndEntranceId"; + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/TaskStatusCode.java b/ruoyi-admin/src/main/java/com/ruoyi/common/TaskStatusCode.java new file mode 100644 index 0000000..328e888 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/TaskStatusCode.java @@ -0,0 +1,54 @@ +package com.ruoyi.common; + +/** + * 任务执行状态 + */ +public class TaskStatusCode { + + /** + * 任务状态-待执行 + */ + public static final String TASK_STATUS_CREATE = "1"; + + /** + * 任务状态-待执行 + */ + public static final String TASK_STATUS_PENDING = "2"; + + /** + * 任务状态-正执行 + */ + public static final String TASK_STATUS_RUNNING = "3"; + + /** + * 任务状态-已完成 + */ + public static final String TASK_STATUS_FINISH = "4"; + + /** + * 任务状态-作废 + */ + public static final String TASK_STATUS_INVALID = "5"; + /** + * 任务执行阶段 AGV + */ + public static final String TASK_EP_AGV = "1"; + /** + * 任务执行阶段 入口输送机 + */ + public static final String TASK_EP_SSJ = "2"; + /** + * 任务执行阶段 巷道口 + */ + public static final String TASK_EP_ROADWAY_SSJ = "3"; + /** + * 任务执行阶段 堆垛机 + */ + public static final String TASK_EP_DDJ = "4"; + + /** + * 任务执行阶段 回传wms完成 + */ + public static final String TASK_EP_WMS_FINISH = "5"; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/TaskType.java b/ruoyi-admin/src/main/java/com/ruoyi/common/TaskType.java new file mode 100644 index 0000000..614398f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/TaskType.java @@ -0,0 +1,15 @@ +package com.ruoyi.common; + +public class TaskType { + + /** + * 堆垛机任务类型 + */ + //1-二楼双向 + public static final int DDJ_RWLX_1 = 1; + //2-一楼单向 + public static final int DDJ_RWLX_2 = 2; + //3-一楼双向 + public static final int DDJ_RWLX_3 = 3; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/domain/CustomThread.java b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/CustomThread.java new file mode 100644 index 0000000..b19c179 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/CustomThread.java @@ -0,0 +1,13 @@ +package com.ruoyi.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CustomThread { + public Thread thread; + public T runnable; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/domain/HslRead.java b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/HslRead.java new file mode 100644 index 0000000..a60a5c9 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/HslRead.java @@ -0,0 +1,105 @@ +package com.ruoyi.common.domain; + +import HslCommunication.BasicFramework.SoftBasic; +import HslCommunication.Core.Net.IReadWriteNet; +import HslCommunication.Core.Types.OperateResult; +import HslCommunication.Core.Types.OperateResultExOne; + +import javax.swing.*; +import java.awt.*; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class HslRead { + + private static SimpleDateFormat dateFormat = new SimpleDateFormat("[HH:mm:ss] "); + + /** + * 将结果信息显示出来,在界面里进行显示 + * @param result 等待显示的结果信息 + * @param x + * @param address 读取的地址数据 + * @param textBox 显示结果的文本框 + * @param scrollPane 支持滚动条的控件 + * @param 类型对象 + * @return + * @return + * @return + * @return + * @return + */ + public static T ReadResultRender(OperateResultExOne result ) { + + T x = null ; + if (result.IsSuccess) { + if (result.Content.getClass().isArray()) { + + } else { + x= (T) result.Content; + } + } else { + + JOptionPane.showMessageDialog( + null, + dateFormat.format(new Date()) + "] Read Failed\r\nReason:" + result.ToMessageShowString(), + "Result", + JOptionPane.WARNING_MESSAGE); + } + return x; + } + + public static int WriteResultRender(OperateResult result, String address ) { //void + int x=0; + if (result.IsSuccess) { +// JOptionPane.showMessageDialog( +// null, +// dateFormat.format(new Date()) + "[" + address + "] Write Success", +// "Result", +// JOptionPane.PLAIN_MESSAGE); + x=1; + } else { +// JOptionPane.showMessageDialog( +// null, +// dateFormat.format(new Date()) + "[" + address + "] Write Failed\r\nReason:" + result.ToMessageShowString(), +// "Result", +// JOptionPane.WARNING_MESSAGE); + x=2; + } + return x; + } + + public static void BulkReadRenderResult(IReadWriteNet readWrite, JTextField addTextBox, JTextField lengthTextBox, JTextArea resultTextBox ) { + try { + OperateResultExOne read = readWrite.Read(addTextBox.getText(), Short.parseShort(lengthTextBox.getText())); + if (read.IsSuccess) { + resultTextBox.setText("Result:" + SoftBasic.ByteToHexString(read.Content)); + } else { + JOptionPane.showMessageDialog(null, "Read Failed:" + read.ToMessageShowString(), "Result", JOptionPane.WARNING_MESSAGE); + } + } catch (Exception ex) { + JOptionPane.showMessageDialog(null, "Read Failed:" + ex.getMessage(), "Result", JOptionPane.ERROR_MESSAGE); + } + } + + public static void SetPanelEnabled(JPanel panel, boolean enable) { + Component[] components = panel.getComponents(); + + //System.out.println("Component Length:" + components.length); + for (Component component : components) { + if (component instanceof JPanel) { + SetPanelEnabled((JPanel) component, enable); + } else { + component.setEnabled(enable); + } + } + + } + + public static String IpAddressInputWrong = "IpAddress input wrong"; + public static String PortInputWrong = "Port input wrong"; + public static String SlotInputWrong = "Slot input wrong"; + public static String BaudRateInputWrong = "Baud rate input wrong"; + public static String DataBitsInputWrong = "Data bit input wrong"; + public static String StopBitInputWrong = "Stop bit input wrong"; + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/domain/PlcConnectInfo.java b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/PlcConnectInfo.java new file mode 100644 index 0000000..fcc9b39 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/PlcConnectInfo.java @@ -0,0 +1,14 @@ +package com.ruoyi.common.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@AllArgsConstructor +@NoArgsConstructor +@Data +public class PlcConnectInfo { + + private String ip; + private Integer port; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/common/domain/SendData.java b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/SendData.java new file mode 100644 index 0000000..08c0ff8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/common/domain/SendData.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.domain; + +/** + * 发送数据的实体类 + * @num 长度 + * @num2 值 + */ +public class SendData { + private static final int DINT = 4; + private static final int INT = 2; + private int num; + private int num2; + + public SendData(int num, int num2) { + this.num = num; + this.num2 = num2; + } + + public SendData(int num, String num2) { + this.num = num; + this.num2 = Integer.parseInt(num2); + } + + public int getNum() { + return num; + } + + public void setNum(int num) { + this.num = num; + } + + public int getNum2() { + return num2; + } + + public void setNum2(int num2) { + this.num2 = num2; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/controller/DeviceCarController.java b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/controller/DeviceCarController.java new file mode 100644 index 0000000..0b449b7 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/controller/DeviceCarController.java @@ -0,0 +1,104 @@ +package com.ruoyi.device.DeviceCar.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; +import com.ruoyi.device.DeviceCar.service.IDeviceCarService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 设备信息Controller + * + * @author ruoyi + * @date 2025-06-12 + */ +@RestController +@RequestMapping("/device/DeviceCar") +public class DeviceCarController extends BaseController +{ + @Autowired + private IDeviceCarService deviceCarService; + + /** + * 查询设备信息列表 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:list')") + @GetMapping("/list") + public TableDataInfo list(DeviceCar deviceCar) + { + startPage(); + List list = deviceCarService.selectDeviceCarList(deviceCar); + return getDataTable(list); + } + + /** + * 导出设备信息列表 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:export')") + @Log(title = "设备信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DeviceCar deviceCar) + { + List list = deviceCarService.selectDeviceCarList(deviceCar); + ExcelUtil util = new ExcelUtil(DeviceCar.class); + util.exportExcel(response, list, "设备信息数据"); + } + + /** + * 获取设备信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(deviceCarService.selectDeviceCarById(id)); + } + + /** + * 新增设备信息 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:add')") + @Log(title = "设备信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DeviceCar deviceCar) + { + return toAjax(deviceCarService.insertDeviceCar(deviceCar)); + } + + /** + * 修改设备信息 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:edit')") + @Log(title = "设备信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DeviceCar deviceCar) + { + return toAjax(deviceCarService.updateDeviceCar(deviceCar)); + } + + /** + * 删除设备信息 + */ + @PreAuthorize("@ss.hasPermi('device:DeviceCar:remove')") + @Log(title = "设备信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(deviceCarService.deleteDeviceCarByIds(ids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/domain/DeviceCar.java b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/domain/DeviceCar.java new file mode 100644 index 0000000..78131bf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/domain/DeviceCar.java @@ -0,0 +1,233 @@ +package com.ruoyi.device.DeviceCar.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 设备信息对象 device_car + * + * @author ruoyi + * @date 2025-06-12 + */ +public class DeviceCar extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** $column.columnComment */ + private Long id; + + /** 设备唯一编码 */ + @Excel(name = "设备唯一编码") + private String deviceCode ; + + /** 设备名称 */ + @Excel(name = "设备名称") + private String name ; + + /** 1-AGV 2-CTU*/ + @Excel(name = "1-AGV 2-CTU") + private String type; + + /** 设备IP */ + @Excel(name = "设备IP") + private String ipAddress; + + /** 关联缓冲区 */ + @Excel(name = "关联缓冲区") + private String bufferAreaId ; + + /** 0-F 1-T */ + @Excel(name = "0-F 1-T") + private String isActive; + + /** 状态 */ + @Excel(name = "状态") + private String status; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare1; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare2; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare3; + + /** 备用字段 */ + @Excel(name = "备用字段") + private String spare4; + + /** 任务号 */ + @Excel(name = "任务号") + private String taskNo; + + /** 执行阶段 */ + @Excel(name = "执行阶段") + private String zxjd; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setDeviceCode (String deviceCode ) + { + this.deviceCode = deviceCode ; + } + + public String getDeviceCode () + { + return deviceCode ; + } + + public void setName (String name ) + { + this.name = name ; + } + + public String getName () + { + return name ; + } + + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + + public void setIpAddress(String ipAddress) + { + this.ipAddress = ipAddress; + } + + public String getIpAddress() + { + return ipAddress; + } + + public void setBufferAreaId (String bufferAreaId ) + { + this.bufferAreaId = bufferAreaId ; + } + + public String getBufferAreaId () + { + return bufferAreaId ; + } + + public void setIsActive(String isActive) + { + this.isActive = isActive; + } + + public String getIsActive() + { + return isActive; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setSpare1(String spare1) + { + this.spare1 = spare1; + } + + public String getSpare1() + { + return spare1; + } + + public void setSpare2(String spare2) + { + this.spare2 = spare2; + } + + public String getSpare2() + { + return spare2; + } + + public void setSpare3(String spare3) + { + this.spare3 = spare3; + } + + public String getSpare3() + { + return spare3; + } + + public void setSpare4(String spare4) + { + this.spare4 = spare4; + } + + public String getSpare4() + { + return spare4; + } + + public void setTaskNo(String taskNo) + { + this.taskNo = taskNo; + } + + public String getTaskNo() + { + return taskNo; + } + + public void setZxjd(String zxjd) + { + this.zxjd = zxjd; + } + + public String getZxjd() + { + return zxjd; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("deviceCode ", getDeviceCode ()) + .append("name ", getName ()) + .append("type", getType()) + .append("ipAddress", getIpAddress()) + .append("bufferAreaId ", getBufferAreaId ()) + .append("isActive", getIsActive()) + .append("createTime", getCreateTime()) + .append("status", getStatus()) + .append("spare1", getSpare1()) + .append("spare2", getSpare2()) + .append("spare3", getSpare3()) + .append("spare4", getSpare4()) + .append("taskNo", getTaskNo()) + .append("zxjd", getZxjd()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/mapper/DeviceCarMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/mapper/DeviceCarMapper.java new file mode 100644 index 0000000..bbf39ad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/mapper/DeviceCarMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.DeviceCar.mapper; + +import java.util.List; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; + +/** + * 设备信息Mapper接口 + * + * @author ruoyi + * @date 2025-06-12 + */ +public interface DeviceCarMapper +{ + /** + * 查询设备信息 + * + * @param id 设备信息主键 + * @return 设备信息 + */ + public DeviceCar selectDeviceCarById(Long id); + + /** + * 查询设备信息列表 + * + * @param deviceCar 设备信息 + * @return 设备信息集合 + */ + public List selectDeviceCarList(DeviceCar deviceCar); + + /** + * 新增设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + public int insertDeviceCar(DeviceCar deviceCar); + + /** + * 修改设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + public int updateDeviceCar(DeviceCar deviceCar); + + /** + * 删除设备信息 + * + * @param id 设备信息主键 + * @return 结果 + */ + public int deleteDeviceCarById(Long id); + + /** + * 批量删除设备信息 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeviceCarByIds(Long[] ids); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/IDeviceCarService.java b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/IDeviceCarService.java new file mode 100644 index 0000000..f7e1032 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/IDeviceCarService.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.DeviceCar.service; + +import java.util.List; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; + +/** + * 设备信息Service接口 + * + * @author ruoyi + * @date 2025-06-12 + */ +public interface IDeviceCarService +{ + /** + * 查询设备信息 + * + * @param id 设备信息主键 + * @return 设备信息 + */ + public DeviceCar selectDeviceCarById(Long id); + + /** + * 查询设备信息列表 + * + * @param deviceCar 设备信息 + * @return 设备信息集合 + */ + public List selectDeviceCarList(DeviceCar deviceCar); + + /** + * 新增设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + public int insertDeviceCar(DeviceCar deviceCar); + + /** + * 修改设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + public int updateDeviceCar(DeviceCar deviceCar); + + /** + * 批量删除设备信息 + * + * @param ids 需要删除的设备信息主键集合 + * @return 结果 + */ + public int deleteDeviceCarByIds(Long[] ids); + + /** + * 删除设备信息信息 + * + * @param id 设备信息主键 + * @return 结果 + */ + public int deleteDeviceCarById(Long id); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/impl/DeviceCarServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/impl/DeviceCarServiceImpl.java new file mode 100644 index 0000000..1d8300b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/DeviceCar/service/impl/DeviceCarServiceImpl.java @@ -0,0 +1,95 @@ +package com.ruoyi.device.DeviceCar.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.device.DeviceCar.mapper.DeviceCarMapper; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; +import com.ruoyi.device.DeviceCar.service.IDeviceCarService; + +/** + * 设备信息Service业务层处理 + * + * @author ruoyi + * @date 2025-06-12 + */ +@Service +public class DeviceCarServiceImpl implements IDeviceCarService +{ + @Autowired + private DeviceCarMapper deviceCarMapper; + + /** + * 查询设备信息 + * + * @param id 设备信息主键 + * @return 设备信息 + */ + @Override + public DeviceCar selectDeviceCarById(Long id) + { + return deviceCarMapper.selectDeviceCarById(id); + } + + /** + * 查询设备信息列表 + * + * @param deviceCar 设备信息 + * @return 设备信息 + */ + @Override + public List selectDeviceCarList(DeviceCar deviceCar) + { + return deviceCarMapper.selectDeviceCarList(deviceCar); + } + + /** + * 新增设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + @Override + public int insertDeviceCar(DeviceCar deviceCar) + { + deviceCar.setCreateTime(DateUtils.getNowDate()); + return deviceCarMapper.insertDeviceCar(deviceCar); + } + + /** + * 修改设备信息 + * + * @param deviceCar 设备信息 + * @return 结果 + */ + @Override + public int updateDeviceCar(DeviceCar deviceCar) + { + return deviceCarMapper.updateDeviceCar(deviceCar); + } + + /** + * 批量删除设备信息 + * + * @param ids 需要删除的设备信息主键 + * @return 结果 + */ + @Override + public int deleteDeviceCarByIds(Long[] ids) + { + return deviceCarMapper.deleteDeviceCarByIds(ids); + } + + /** + * 删除设备信息信息 + * + * @param id 设备信息主键 + * @return 结果 + */ + @Override + public int deleteDeviceCarById(Long id) + { + return deviceCarMapper.deleteDeviceCarById(id); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/controller/MsgDdjController.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/controller/MsgDdjController.java new file mode 100644 index 0000000..072ad84 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/controller/MsgDdjController.java @@ -0,0 +1,104 @@ +package com.ruoyi.device.ddj.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ddj.service.IMsgDdjService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 堆垛机状态表Controller + * + * @author ruoyi + * @date 2025-09-22 + */ +@RestController +@RequestMapping("/device/ddj") +public class MsgDdjController extends BaseController +{ + @Autowired + private IMsgDdjService msgDdjService; + + /** + * 查询堆垛机状态表列表 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:list')") + @GetMapping("/list") + public TableDataInfo list(MsgDdj msgDdj) + { + startPage(); + List list = msgDdjService.selectMsgDdjList(msgDdj); + return getDataTable(list); + } + + /** + * 导出堆垛机状态表列表 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:export')") + @Log(title = "堆垛机状态表", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MsgDdj msgDdj) + { + List list = msgDdjService.selectMsgDdjList(msgDdj); + ExcelUtil util = new ExcelUtil(MsgDdj.class); + util.exportExcel(response, list, "堆垛机状态表数据"); + } + + /** + * 获取堆垛机状态表详细信息 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(msgDdjService.selectMsgDdjById(id)); + } + + /** + * 新增堆垛机状态表 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:add')") + @Log(title = "堆垛机状态表", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MsgDdj msgDdj) + { + return toAjax(msgDdjService.insertMsgDdj(msgDdj)); + } + + /** + * 修改堆垛机状态表 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:edit')") + @Log(title = "堆垛机状态表", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MsgDdj msgDdj) + { + return toAjax(msgDdjService.updateMsgDdj(msgDdj)); + } + + /** + * 删除堆垛机状态表 + */ + @PreAuthorize("@ss.hasPermi('device:ddj:remove')") + @Log(title = "堆垛机状态表", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(msgDdjService.deleteMsgDdjByIds(ids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/domain/MsgDdj.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/domain/MsgDdj.java new file mode 100644 index 0000000..9f797b8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/domain/MsgDdj.java @@ -0,0 +1,105 @@ +package com.ruoyi.device.ddj.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 堆垛机状态表对象 msg_ddj + * + * @author ruoyi + * @date 2025-09-22 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class MsgDdj extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** id */ + private Integer id; + + /** 堆垛机名称 */ + @Excel(name = "堆垛机名称") + private String name; + + /** 任务号 */ + @Excel(name = "任务号") + private Long rwh; + + /** 阶段标志 */ + @Excel(name = "阶段标志") + private Integer jdbz; + + /** 状态 */ + @Excel(name = "状态") + private Integer sbzt; + + /** 操作方式 */ + @Excel(name = "操作方式") + private Integer czfs; + + /** 允许下发 */ + @Excel(name = "允许下发") + private Integer yxxf; + + /** 原排 */ + @Excel(name = "原排") + private Integer yph; + + /** 原列 */ + @Excel(name = "原列") + private Integer ylh; + + /** 原层 */ + @Excel(name = "原层") + private Integer ych; + + /** 目标排 */ + @Excel(name = "目标排") + private Integer mbph; + + /** 目标列 */ + @Excel(name = "目标列") + private Integer mblh; + + /** 目标层 */ + @Excel(name = "目标层") + private Integer mbch; + + /** 心跳信号 */ + @Excel(name = "心跳信号") + private Integer xtxh; + + /** 当前巷道 */ + @Excel(name = "当前巷道") + private Integer dqxd; + + /** 货叉状态 */ + @Excel(name = "货叉状态") + private Integer hczt; + + /** 备用字段1 */ + @Excel(name = "备用字段1") + private Integer spare1; + + /** 备用字段2 */ + @Excel(name = "备用字段2") + private Integer spare2; + + /** 备用字段3 */ + @Excel(name = "备用字段3") + private Integer spare3; + + private String plcDbReadAddress; + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/mapper/MsgDdjMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/mapper/MsgDdjMapper.java new file mode 100644 index 0000000..b99cab3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/mapper/MsgDdjMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.ddj.mapper; + +import java.util.List; +import com.ruoyi.device.ddj.domain.MsgDdj; + +/** + * 堆垛机状态表Mapper接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface MsgDdjMapper +{ + /** + * 查询堆垛机状态表 + * + * @param id 堆垛机状态表主键 + * @return 堆垛机状态表 + */ + public MsgDdj selectMsgDdjById(Long id); + + /** + * 查询堆垛机状态表列表 + * + * @param msgDdj 堆垛机状态表 + * @return 堆垛机状态表集合 + */ + public List selectMsgDdjList(MsgDdj msgDdj); + + /** + * 新增堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + public int insertMsgDdj(MsgDdj msgDdj); + + /** + * 修改堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + public int updateMsgDdj(MsgDdj msgDdj); + + /** + * 删除堆垛机状态表 + * + * @param id 堆垛机状态表主键 + * @return 结果 + */ + public int deleteMsgDdjById(Long id); + + /** + * 批量删除堆垛机状态表 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMsgDdjByIds(Long[] ids); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/IMsgDdjService.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/IMsgDdjService.java new file mode 100644 index 0000000..af6f3e8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/IMsgDdjService.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.ddj.service; + +import java.util.List; +import com.ruoyi.device.ddj.domain.MsgDdj; + +/** + * 堆垛机状态表Service接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface IMsgDdjService +{ + /** + * 查询堆垛机状态表 + * + * @param id 堆垛机状态表主键 + * @return 堆垛机状态表 + */ + public MsgDdj selectMsgDdjById(Long id); + + /** + * 查询堆垛机状态表列表 + * + * @param msgDdj 堆垛机状态表 + * @return 堆垛机状态表集合 + */ + public List selectMsgDdjList(MsgDdj msgDdj); + + /** + * 新增堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + public int insertMsgDdj(MsgDdj msgDdj); + + /** + * 修改堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + public int updateMsgDdj(MsgDdj msgDdj); + + /** + * 批量删除堆垛机状态表 + * + * @param ids 需要删除的堆垛机状态表主键集合 + * @return 结果 + */ + public int deleteMsgDdjByIds(Long[] ids); + + /** + * 删除堆垛机状态表信息 + * + * @param id 堆垛机状态表主键 + * @return 结果 + */ + public int deleteMsgDdjById(Long id); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/impl/MsgDdjServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/impl/MsgDdjServiceImpl.java new file mode 100644 index 0000000..62accf8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ddj/service/impl/MsgDdjServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.device.ddj.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.device.ddj.mapper.MsgDdjMapper; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ddj.service.IMsgDdjService; + +/** + * 堆垛机状态表Service业务层处理 + * + * @author ruoyi + * @date 2025-09-22 + */ +@Service +public class MsgDdjServiceImpl implements IMsgDdjService +{ + @Autowired + private MsgDdjMapper msgDdjMapper; + + /** + * 查询堆垛机状态表 + * + * @param id 堆垛机状态表主键 + * @return 堆垛机状态表 + */ + @Override + public MsgDdj selectMsgDdjById(Long id) + { + return msgDdjMapper.selectMsgDdjById(id); + } + + /** + * 查询堆垛机状态表列表 + * + * @param msgDdj 堆垛机状态表 + * @return 堆垛机状态表 + */ + @Override + public List selectMsgDdjList(MsgDdj msgDdj) + { + return msgDdjMapper.selectMsgDdjList(msgDdj); + } + + /** + * 新增堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + @Override + public int insertMsgDdj(MsgDdj msgDdj) + { + return msgDdjMapper.insertMsgDdj(msgDdj); + } + + /** + * 修改堆垛机状态表 + * + * @param msgDdj 堆垛机状态表 + * @return 结果 + */ + @Override + public int updateMsgDdj(MsgDdj msgDdj) + { + return msgDdjMapper.updateMsgDdj(msgDdj); + } + + /** + * 批量删除堆垛机状态表 + * + * @param ids 需要删除的堆垛机状态表主键 + * @return 结果 + */ + @Override + public int deleteMsgDdjByIds(Long[] ids) + { + return msgDdjMapper.deleteMsgDdjByIds(ids); + } + + /** + * 删除堆垛机状态表信息 + * + * @param id 堆垛机状态表主键 + * @return 结果 + */ + @Override + public int deleteMsgDdjById(Long id) + { + return msgDdjMapper.deleteMsgDdjById(id); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/controller/DeviceSsjController.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/controller/DeviceSsjController.java new file mode 100644 index 0000000..231ada4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/controller/DeviceSsjController.java @@ -0,0 +1,104 @@ +package com.ruoyi.device.ssj.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.service.IDeviceSsjService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 输送机Controller + * + * @author ruoyi + * @date 2025-07-04 + */ +@RestController +@RequestMapping("/device/ssj") +public class DeviceSsjController extends BaseController +{ + @Autowired + private IDeviceSsjService deviceSsjService; + + /** + * 查询输送机列表 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:list')") + @GetMapping("/list") + public TableDataInfo list(DeviceSsj deviceSsj) + { + startPage(); + List list = deviceSsjService.selectDeviceSsjList(deviceSsj); + return getDataTable(list); + } + + /** + * 导出输送机列表 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:export')") + @Log(title = "输送机", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DeviceSsj deviceSsj) + { + List list = deviceSsjService.selectDeviceSsjList(deviceSsj); + ExcelUtil util = new ExcelUtil(DeviceSsj.class); + util.exportExcel(response, list, "输送机数据"); + } + + /** + * 获取输送机详细信息 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(deviceSsjService.selectDeviceSsjById(id)); + } + + /** + * 新增输送机 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:add')") + @Log(title = "输送机", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DeviceSsj deviceSsj) + { + return toAjax(deviceSsjService.insertDeviceSsj(deviceSsj)); + } + + /** + * 修改输送机 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:edit')") + @Log(title = "输送机", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DeviceSsj deviceSsj) + { + return toAjax(deviceSsjService.updateDeviceSsj(deviceSsj)); + } + + /** + * 删除输送机 + */ + @PreAuthorize("@ss.hasPermi('device:ssj:remove')") + @Log(title = "输送机", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(deviceSsjService.deleteDeviceSsjByIds(ids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/domain/DeviceSsj.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/domain/DeviceSsj.java new file mode 100644 index 0000000..2e4b9fb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/domain/DeviceSsj.java @@ -0,0 +1,86 @@ +package com.ruoyi.device.ssj.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 输送机对象 device_ssj + * + * @author ruoyi + * @date 2025-07-04 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class DeviceSsj extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** $column.columnComment */ + private Long id; + + /** 设备唯一编码 */ + @Excel(name = "设备唯一编码") + private String deviceCode ; + + /** 设备名称 */ + @Excel(name = "设备名称") + private String name ; + + /** 类型 */ + @Excel(name = "类型") + private String type; + + /** 设备IP */ + @Excel(name = "设备IP") + private String ipAddress; + + /** PLC读取地址 */ + @Excel(name = "PLC读取地址") + private String plcDbReadAddress; + + /** 0-F 1-T */ + @Excel(name = "0-F 1-T") + private String isActive; + + /** PLC写入地址 */ + @Excel(name = "PLC写入地址") + private String plcDbWriteAddress; + + /** 入库请求 */ + @Excel(name = "入库请求") + private int rkqq; + + /** 空闲 */ + @Excel(name = "空闲") + private int kx; + + /** 到位 */ + @Excel(name = "到位") + private int dw; + + /** 条码 */ + @Excel(name = "条码") + private String trayCode; + + /** 任务号 */ + @Excel(name = "任务号") + private int taskNo; + + @Excel(name = "异常") + private int exception; + + @Excel(name = "托盘尺寸") + private int traySize; + + @Excel(name="允许放货") + private int allowPut; + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/mapper/DeviceSsjMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/mapper/DeviceSsjMapper.java new file mode 100644 index 0000000..2fbe26f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/mapper/DeviceSsjMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.ssj.mapper; + +import java.util.List; +import com.ruoyi.device.ssj.domain.DeviceSsj; + +/** + * 输送机Mapper接口 + * + * @author ruoyi + * @date 2025-07-04 + */ +public interface DeviceSsjMapper +{ + /** + * 查询输送机 + * + * @param id 输送机主键 + * @return 输送机 + */ + public DeviceSsj selectDeviceSsjById(Long id); + + /** + * 查询输送机列表 + * + * @param deviceSsj 输送机 + * @return 输送机集合 + */ + public List selectDeviceSsjList(DeviceSsj deviceSsj); + + /** + * 新增输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + public int insertDeviceSsj(DeviceSsj deviceSsj); + + /** + * 修改输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + public int updateDeviceSsj(DeviceSsj deviceSsj); + + /** + * 删除输送机 + * + * @param id 输送机主键 + * @return 结果 + */ + public int deleteDeviceSsjById(Long id); + + /** + * 批量删除输送机 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDeviceSsjByIds(Long[] ids); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/IDeviceSsjService.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/IDeviceSsjService.java new file mode 100644 index 0000000..8a000e4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/IDeviceSsjService.java @@ -0,0 +1,61 @@ +package com.ruoyi.device.ssj.service; + +import java.util.List; +import com.ruoyi.device.ssj.domain.DeviceSsj; + +/** + * 输送机Service接口 + * + * @author ruoyi + * @date 2025-07-04 + */ +public interface IDeviceSsjService +{ + /** + * 查询输送机 + * + * @param id 输送机主键 + * @return 输送机 + */ + public DeviceSsj selectDeviceSsjById(Long id); + + /** + * 查询输送机列表 + * + * @param deviceSsj 输送机 + * @return 输送机集合 + */ + public List selectDeviceSsjList(DeviceSsj deviceSsj); + + /** + * 新增输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + public int insertDeviceSsj(DeviceSsj deviceSsj); + + /** + * 修改输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + public int updateDeviceSsj(DeviceSsj deviceSsj); + + /** + * 批量删除输送机 + * + * @param ids 需要删除的输送机主键集合 + * @return 结果 + */ + public int deleteDeviceSsjByIds(Long[] ids); + + /** + * 删除输送机信息 + * + * @param id 输送机主键 + * @return 结果 + */ + public int deleteDeviceSsjById(Long id); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/impl/DeviceSsjServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/impl/DeviceSsjServiceImpl.java new file mode 100644 index 0000000..d3f2513 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/device/ssj/service/impl/DeviceSsjServiceImpl.java @@ -0,0 +1,95 @@ +package com.ruoyi.device.ssj.service.impl; + +import java.util.List; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.device.ssj.mapper.DeviceSsjMapper; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.service.IDeviceSsjService; + +/** + * 输送机Service业务层处理 + * + * @author ruoyi + * @date 2025-07-04 + */ +@Service +public class DeviceSsjServiceImpl implements IDeviceSsjService +{ + @Autowired + private DeviceSsjMapper deviceSsjMapper; + + /** + * 查询输送机 + * + * @param id 输送机主键 + * @return 输送机 + */ + @Override + public DeviceSsj selectDeviceSsjById(Long id) + { + return deviceSsjMapper.selectDeviceSsjById(id); + } + + /** + * 查询输送机列表 + * + * @param deviceSsj 输送机 + * @return 输送机 + */ + @Override + public List selectDeviceSsjList(DeviceSsj deviceSsj) + { + return deviceSsjMapper.selectDeviceSsjList(deviceSsj); + } + + /** + * 新增输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + @Override + public int insertDeviceSsj(DeviceSsj deviceSsj) + { + deviceSsj.setCreateTime(DateUtils.getNowDate()); + return deviceSsjMapper.insertDeviceSsj(deviceSsj); + } + + /** + * 修改输送机 + * + * @param deviceSsj 输送机 + * @return 结果 + */ + @Override + public int updateDeviceSsj(DeviceSsj deviceSsj) + { + return deviceSsjMapper.updateDeviceSsj(deviceSsj); + } + + /** + * 批量删除输送机 + * + * @param ids 需要删除的输送机主键 + * @return 结果 + */ + @Override + public int deleteDeviceSsjByIds(Long[] ids) + { + return deviceSsjMapper.deleteDeviceSsjByIds(ids); + } + + /** + * 删除输送机信息 + * + * @param id 输送机主键 + * @return 结果 + */ + @Override + public int deleteDeviceSsjById(Long id) + { + return deviceSsjMapper.deleteDeviceSsjById(id); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/controller/DynamicMonitoringController.java b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/controller/DynamicMonitoringController.java new file mode 100644 index 0000000..89e7f40 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/controller/DynamicMonitoringController.java @@ -0,0 +1,53 @@ +package com.ruoyi.dynamicMonitoring.controller; + +import java.util.List; +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.dynamicMonitoring.service.IDynamicMonitoringService; + +/** + * 大屏堆垛机数据Controller + * + * @author ruoyi + */ +@RestController +@RequestMapping("/dynamicMonitoring/list") +public class DynamicMonitoringController { + @Autowired + private IDynamicMonitoringService largeScreenDdjService; + + /** + * 查询堆垛机数据列表 + */ + @GetMapping("/ddjList") + public AjaxResult ddjList() { + List> list = largeScreenDdjService.queryDdjList(); + return AjaxResult.success(list); + } + + /** + * 查询输送机数据列表 + */ + @GetMapping("/ssjList") + public AjaxResult ssjList() { + List> list = largeScreenDdjService.querySsjList(); + return AjaxResult.success(list); + } + + /** + * 查询异常设备列表 + */ + @GetMapping("/alarmList") + public AjaxResult alarmList() { + Map alarmData = largeScreenDdjService.queryAlarmList(); + AjaxResult result = new AjaxResult(200, "操作成功"); + result.put("alarm", alarmData.get("alarm")); + return result; + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/IDynamicMonitoringService.java b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/IDynamicMonitoringService.java new file mode 100644 index 0000000..4e08738 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/IDynamicMonitoringService.java @@ -0,0 +1,32 @@ +package com.ruoyi.dynamicMonitoring.service; + +import java.util.List; +import java.util.Map; + +/** + * 大屏堆垛机数据Service接口 + * + * @author ruoyi + */ +public interface IDynamicMonitoringService { + /** + * 查询堆垛机数据列表 + * + * @return 堆垛机数据列表 + */ + public List> queryDdjList(); + + /** + * 查询输送机数据列表 + * + * @return 输送机数据列表 + */ + public List> querySsjList(); + + /** + * 查询异常设备列表 + * + * @return 异常设备列表 + */ + public Map queryAlarmList(); +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/impl/DynamicMonitoringServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/impl/DynamicMonitoringServiceImpl.java new file mode 100644 index 0000000..959632e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/dynamicMonitoring/service/impl/DynamicMonitoringServiceImpl.java @@ -0,0 +1,121 @@ +package com.ruoyi.dynamicMonitoring.service.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ddj.mapper.MsgDdjMapper; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.mapper.DeviceSsjMapper; +import com.ruoyi.dynamicMonitoring.service.IDynamicMonitoringService; + +/** + * 大屏堆垛机数据Service业务层处理 + * + * @author ruoyi + */ +@Service +public class DynamicMonitoringServiceImpl implements IDynamicMonitoringService { + @Autowired + private MsgDdjMapper msgDdjMapper; + + @Autowired + private DeviceSsjMapper deviceSsjMapper; + + /** + * 查询堆垛机数据列表 + * + * @return 堆垛机数据列表 + */ + @Override + public List> queryDdjList() { + List ddjList = msgDdjMapper.selectMsgDdjList(null); + List> resultList = new ArrayList<>(); + + for (MsgDdj ddj : ddjList) { + Map map = new HashMap<>(); + map.put("id", ddj.getId().toString()); + map.put("name", ddj.getName()); + map.put("zxjd", ddj.getJdbz().toString()); + map.put("sbzt", ddj.getSbzt().toString()); + map.put("rwh", ddj.getRwh().toString()); + map.put("yp", ddj.getYph().toString()); + map.put("ywz", ddj.getYlh().toString()); + map.put("yc", ddj.getYch().toString()); + map.put("mbp", ddj.getMbph().toString()); + map.put("mbwz", ddj.getMblh().toString()); + map.put("mbc", ddj.getMbch().toString()); + resultList.add(map); + } + + return resultList; + } + + /** + * 查询输送机数据列表 + * + * @return 输送机数据列表 + */ + @Override + public List> querySsjList() { + List ssjList = deviceSsjMapper.selectDeviceSsjList(null); + List> resultList = new ArrayList<>(); + + for (DeviceSsj ssj : ssjList) { + Map map = new HashMap<>(); + map.put("id", ssj.getDeviceCode()); + map.put("name", ssj.getName()); + map.put("kx", String.valueOf(ssj.getKx())); + map.put("dw", String.valueOf(ssj.getDw())); + map.put("rwh", String.valueOf(ssj.getTaskNo())); + map.put("sbzt", ssj.getException()); + map.put("trayCode", ssj.getTrayCode()); + resultList.add(map); + } + + return resultList; + } + + /** + * 查询异常设备列表 + * + * @return 异常设备列表 + */ + @Override + public Map queryAlarmList() { + List> alarmList = new ArrayList<>(); + + // 查询堆垛机状态异常的设备(状态码>1000) + List ddjList = msgDdjMapper.selectMsgDdjList(null); + for (MsgDdj ddj : ddjList) { + if (ddj.getSbzt() != null && ddj.getSbzt() > 1000) { + Map map = new HashMap<>(); + map.put("id", ddj.getId().toString()); + map.put("name", "堆垛机"); + map.put("code", ddj.getSbzt().toString()); + map.put("content", null); + alarmList.add(map); + } + } + + // 查询输送机状态异常的设备(状态码>3) + List ssjList = deviceSsjMapper.selectDeviceSsjList(null); + for (DeviceSsj ssj : ssjList) { + if (ssj.getException() > 3) { + Map map = new HashMap<>(); + map.put("id", ssj.getDeviceCode()); + map.put("name", "输送机"); + map.put("code", String.valueOf(ssj.getException())); + map.put("content", null); + alarmList.add(map); + } + } + + Map result = new HashMap<>(); + result.put("alarm", alarmList); + return result; + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/index/controller/IndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/index/controller/IndexController.java new file mode 100644 index 0000000..dce0408 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/index/controller/IndexController.java @@ -0,0 +1,330 @@ +package com.ruoyi.index.controller; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.device.DeviceCar.service.IDeviceCarService; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.queue.agv.service.IAgvQueueFinishService; +import com.ruoyi.device.ssj.service.IDeviceSsjService; +import com.ruoyi.device.DeviceCar.domain.DeviceCar; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.domain.AgvQueueFinish; +import com.ruoyi.device.ssj.domain.DeviceSsj; + +/** + * 首页数据Controller + * + * @author ruoyi + * @date 2025-01-08 + */ +@RestController +@RequestMapping("/index") +public class IndexController extends BaseController +{ + @Autowired + private IDeviceCarService deviceCarService; + + @Autowired + private IAgvQueueService agvQueueService; + + @Autowired + private IAgvQueueFinishService agvQueueFinishService; + + @Autowired + private IDeviceSsjService deviceSsjService; + + /** + * 获取设备统计数据 + */ + @GetMapping("/deviceStats") + public AjaxResult getDeviceStats() + { + try { + DeviceCar deviceCar = new DeviceCar(); + List deviceList = deviceCarService.selectDeviceCarList(deviceCar); + + Map stats = new HashMap<>(); + int agvCount = 0; + int ctuCount = 0; + int ssjCount = 0; + + for (DeviceCar device : deviceList) { + String type = device.getType(); + if ("1".equals(type)) { + agvCount++; + } else if ("2".equals(type)) { + ctuCount++; + } else if ("3".equals(type)) { + ssjCount++; + } + } + + stats.put("agvCount", agvCount); + stats.put("ctuCount", ctuCount); + stats.put("ssjCount", ssjCount); + stats.put("totalCount", deviceList.size()); + + return AjaxResult.success(stats); + } catch (Exception e) { + return AjaxResult.error("获取设备统计数据失败"); + } + } + + /** + * 获取任务统计数据 + */ + @GetMapping("/taskStats") + public AjaxResult getTaskStats() + { + try { + AgvQueue agvQueue = new AgvQueue(); + List queueList = agvQueueService.selectAgvQueueList(agvQueue); + + int pendingCount = 0; + int runningCount = 0; + + for (AgvQueue queue : queueList) { + String status = queue.getStatus(); + if ("0".equals(status)||"1".equals(status)) { + pendingCount++; + } else if ("2".equals(status)||"3".equals(status)) { + runningCount++; + } + } + + // 获取今日完成任务数 + AgvQueueFinish finishQuery = new AgvQueueFinish(); + List finishList = agvQueueFinishService.selectAgvQueueFinishList(finishQuery); + int finishedCount = finishList.size(); + + Map stats = new HashMap<>(); + stats.put("pendingCount", pendingCount); + stats.put("runningCount", runningCount); + stats.put("finishedCount", finishedCount); + stats.put("totalCount", pendingCount + runningCount + finishedCount); + + return AjaxResult.success(stats); + } catch (Exception e) { + return AjaxResult.error("获取任务统计数据失败"); + } + } + + /** + * 获取当前任务队列 + */ + @GetMapping("/currentTasks") + public AjaxResult getCurrentTasks() + { + try { + AgvQueue agvQueue = new AgvQueue(); + List taskList = agvQueueService.selectAgvQueueList(agvQueue); + + return AjaxResult.success(taskList); + } catch (Exception e) { + return AjaxResult.error("获取当前任务队列失败"); + } + } + + /** + * 获取运行中的设备列表 + */ + @GetMapping("/deviceList") + public AjaxResult getDeviceList() + { + try { + List> runningDevices = new ArrayList<>(); + + // 查询运行中的AGV和CTU设备 + DeviceCar deviceCar = new DeviceCar(); + List deviceList = deviceCarService.selectDeviceCarList(deviceCar); + + for (DeviceCar device : deviceList) { + String taskNo = device.getTaskNo(); + // 判断是否为运行中设备:taskNo不为空且不为0 + if (taskNo != null && !taskNo.trim().isEmpty() && !"0".equals(taskNo.trim())) { + Map deviceInfo = new HashMap<>(); + deviceInfo.put("deviceCode", device.getDeviceCode()); + deviceInfo.put("deviceName", device.getName()); + deviceInfo.put("deviceType", "1".equals(device.getType()) ? "AGV" : "CTU"); + deviceInfo.put("taskNo", device.getTaskNo()); + runningDevices.add(deviceInfo); + } + } + + // 查询运行中的SSJ设备 + DeviceSsj deviceSsj = new DeviceSsj(); + List ssjList = deviceSsjService.selectDeviceSsjList(deviceSsj); + + for (DeviceSsj ssj : ssjList) { + int taskNo = ssj.getTaskNo(); + // 判断是否为运行中设备:taskNo不为0 + if (taskNo != 0) { + Map deviceInfo = new HashMap<>(); + deviceInfo.put("deviceCode", ssj.getDeviceCode()); + deviceInfo.put("deviceName", ssj.getName()); + deviceInfo.put("deviceType", "SSJ"); + deviceInfo.put("taskNo", String.valueOf(ssj.getTaskNo())); + runningDevices.add(deviceInfo); + } + } + + return AjaxResult.success(runningDevices); + } catch (Exception e) { + return AjaxResult.error("获取运行中设备列表失败"); + } + } + + /** + * 获取任务类型分布数据 + */ + @GetMapping("/taskTypeChart") + public AjaxResult getTaskTypeChart() + { + try { + AgvQueue agvQueue = new AgvQueue(); + List queueList = agvQueueService.selectAgvQueueList(agvQueue); + + AgvQueueFinish finishQuery = new AgvQueueFinish(); + List finishList = agvQueueFinishService.selectAgvQueueFinishList(finishQuery); + + Map taskTypeCount = new HashMap<>(); + + // 统计队列中的任务类型 + for (AgvQueue queue : queueList) { + String wmsTaskType = queue.getWmsTaskType(); + if (wmsTaskType != null && !wmsTaskType.isEmpty()) { + taskTypeCount.put(wmsTaskType, taskTypeCount.getOrDefault(wmsTaskType, 0) + 1); + } + } + + // 统计完成任务的类型 + for (AgvQueueFinish finish : finishList) { + String wmsTaskType = finish.getWmsTaskType(); + if (wmsTaskType != null && !wmsTaskType.isEmpty()) { + taskTypeCount.put(wmsTaskType, taskTypeCount.getOrDefault(wmsTaskType, 0) + 1); + } + } + + List> chartData = new ArrayList<>(); + for (Map.Entry entry : taskTypeCount.entrySet()) { + Map item = new HashMap<>(); + item.put("name", entry.getKey()); + item.put("value", entry.getValue()); + chartData.add(item); + } + + return AjaxResult.success(chartData); + } catch (Exception e) { + return AjaxResult.error("获取任务类型分布数据失败"); + } + } + + /** + * 获取设备状态分布数据 + */ + @GetMapping("/deviceStatusChart") + public AjaxResult getDeviceStatusChart() + { + try { + Map> statusCount = new HashMap<>(); + statusCount.put("AGV", new HashMap<>()); + statusCount.put("CTU", new HashMap<>()); + statusCount.put("SSJ", new HashMap<>()); + + // 查询DeviceCar数据(AGV和CTU) + DeviceCar deviceCar = new DeviceCar(); + List deviceList = deviceCarService.selectDeviceCarList(deviceCar); + + for (DeviceCar device : deviceList) { + String type = device.getType(); + String taskNo = device.getTaskNo(); + String deviceType = ""; + String status = "其他"; + + if ("1".equals(type)) { + deviceType = "AGV"; + } else if ("2".equals(type)) { + deviceType = "CTU"; + } + + // 根据taskNo判断状态:不等于空或0时为运行状态 + if (taskNo != null && !taskNo.trim().isEmpty() && !"0".equals(taskNo.trim())) { + status = "运行"; + } + + if (!deviceType.isEmpty()) { + Map typeStatusCount = statusCount.get(deviceType); + typeStatusCount.put(status, typeStatusCount.getOrDefault(status, 0) + 1); + } + } + + // 查询DeviceSsj数据(SSJ) + DeviceSsj deviceSsj = new DeviceSsj(); + List ssjList = deviceSsjService.selectDeviceSsjList(deviceSsj); + + for (DeviceSsj ssj : ssjList) { + int taskNo = ssj.getTaskNo(); + String status = "其他"; + + // 根据taskNo判断状态:不等于空或0时为运行状态 + if (taskNo != 0) { + status = "运行"; + } + + Map typeStatusCount = statusCount.get("SSJ"); + typeStatusCount.put(status, typeStatusCount.getOrDefault(status, 0) + 1); + } + + return AjaxResult.success(statusCount); + } catch (Exception e) { + return AjaxResult.error("获取设备状态分布数据失败"); + } + } + + /** + * 获取首页综合数据 + */ + @GetMapping("/dashboard") + public AjaxResult getDashboardData() + { + try { + Map dashboardData = new HashMap<>(); + + // 获取设备统计 + AjaxResult deviceStatsResult = getDeviceStats(); + if (deviceStatsResult.get("code").equals(200)) { + dashboardData.put("deviceStats", deviceStatsResult.get("data")); + } + + // 获取任务统计 + AjaxResult taskStatsResult = getTaskStats(); + if (taskStatsResult.get("code").equals(200)) { + dashboardData.put("taskStats", taskStatsResult.get("data")); + } + + // 获取当前任务 + AjaxResult currentTasksResult = getCurrentTasks(); + if (currentTasksResult.get("code").equals(200)) { + dashboardData.put("currentTasks", currentTasksResult.get("data")); + } + + // 获取设备列表 + AjaxResult deviceListResult = getDeviceList(); + if (deviceListResult.get("code").equals(200)) { + dashboardData.put("deviceList", deviceListResult.get("data")); + } + + return AjaxResult.success(dashboardData); + } catch (Exception e) { + return AjaxResult.error("获取首页数据失败"); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/controller/JobCallBackController.java b/ruoyi-admin/src/main/java/com/ruoyi/job/controller/JobCallBackController.java new file mode 100644 index 0000000..2a8e464 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/controller/JobCallBackController.java @@ -0,0 +1,111 @@ +package com.ruoyi.job.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.service.IJobCallBackService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 任务完成回传Controller + * + * @author ruoyi + * @date 2025-09-25 + */ +@RestController +@RequestMapping("/job/callBack") +public class JobCallBackController extends BaseController +{ + @Autowired + private IJobCallBackService jobCallBackService; + + /** + * 查询任务完成回传列表 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:list')") + @GetMapping("/list") + public TableDataInfo list(JobCallBack jobCallBack) + { + startPage(); + List list = jobCallBackService.selectJobCallBackList(jobCallBack); + return getDataTable(list); + } + + /** + * 导出任务完成回传列表 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:export')") + @Log(title = "任务完成回传", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, JobCallBack jobCallBack) + { + List list = jobCallBackService.selectJobCallBackList(jobCallBack); + ExcelUtil util = new ExcelUtil(JobCallBack.class); + util.exportExcel(response, list, "任务完成回传数据"); + } + + /** + * 获取任务完成回传详细信息 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:query')") + @GetMapping(value = "/{id}") + public AjaxResult getInfo(@PathVariable("id") Long id) + { + return success(jobCallBackService.selectJobCallBackById(id)); + } + + /** + * 新增任务完成回传 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:add')") + @Log(title = "任务完成回传", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody JobCallBack jobCallBack) + { + return toAjax(jobCallBackService.insertJobCallBack(jobCallBack)); + } + + /** + * 修改任务完成回传 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:edit')") + @Log(title = "任务完成回传", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody JobCallBack jobCallBack) + { + return toAjax(jobCallBackService.updateJobCallBack(jobCallBack)); + } + + /** + * 删除任务完成回传 + */ + @PreAuthorize("@ss.hasPermi('job:callBack:remove')") + @Log(title = "任务完成回传", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public AjaxResult remove(@PathVariable Long[] ids) + { + return toAjax(jobCallBackService.deleteJobCallBackByIds(ids)); + } + + @Scheduled(fixedDelay=1500) + public void jobCallBack() { + jobCallBackService.jobCallBack(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/controller/MydoJobController.java b/ruoyi-admin/src/main/java/com/ruoyi/job/controller/MydoJobController.java new file mode 100644 index 0000000..1038227 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/controller/MydoJobController.java @@ -0,0 +1,116 @@ +package com.ruoyi.job.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 作业信息Controller + * + * @author ruoyi + * @date 2025-09-22 + */ +@RestController +@RequestMapping("/job/mydoJob") +public class MydoJobController extends BaseController +{ + @Autowired + private IMydoJobService mydoJobService; + + /** + * 查询作业信息列表 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:list')") + @GetMapping("/list") + public TableDataInfo list(MydoJob mydoJob) + { + startPage(); + List list = mydoJobService.selectMydoJobList(mydoJob); + return getDataTable(list); + } + + /** + * 查询作业信息列表 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:list')") + @GetMapping("/listFinish") + public TableDataInfo listFinish(MydoJob mydoJob) + { + startPage(); + List list = mydoJobService.selectMydoJobFinishList(mydoJob); + return getDataTable(list); + } + + /** + * 导出作业信息列表 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:export')") + @Log(title = "作业信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, MydoJob mydoJob) + { + List list = mydoJobService.selectMydoJobList(mydoJob); + ExcelUtil util = new ExcelUtil(MydoJob.class); + util.exportExcel(response, list, "作业信息数据"); + } + + /** + * 获取作业信息详细信息 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") String jobId) + { + return success(mydoJobService.selectMydoJobByJobId(jobId)); + } + + /** + * 新增作业信息 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:add')") + @Log(title = "作业信息", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody MydoJob mydoJob) + { + return toAjax(mydoJobService.insertMydoJob(mydoJob)); + } + + /** + * 修改作业信息 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:edit')") + @Log(title = "作业信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody MydoJob mydoJob) + { + return toAjax(mydoJobService.updateMydoJob(mydoJob)); + } + + /** + * 删除作业信息 + */ + @PreAuthorize("@ss.hasPermi('job:mydoJob:remove')") + @Log(title = "作业信息", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable String[] jobIds) + { + return toAjax(mydoJobService.deleteMydoJobByJobIds(jobIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/domain/JobCallBack.java b/ruoyi-admin/src/main/java/com/ruoyi/job/domain/JobCallBack.java new file mode 100644 index 0000000..e75c470 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/domain/JobCallBack.java @@ -0,0 +1,143 @@ +package com.ruoyi.job.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 任务完成回传对象 job_call_back + * + * @author ruoyi + * @date 2025-09-25 + */ +public class JobCallBack extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 类型 */ + @Excel(name = "类型") + private String type; + + /** 任务号 */ + @Excel(name = "任务号") + private Long taskNo; + + /** mydojob作业号 */ + @Excel(name = "mydojob作业号") + private String mydojobId; + + /** 状态 */ + @Excel(name = "状态") + private String status; + + /** 备用字段1 */ + @Excel(name = "备用字段1") + private String spare1; + + /** 备用字段2 */ + @Excel(name = "备用字段2") + private String spare2; + + /** 备用字段3 */ + @Excel(name = "备用字段3") + private String spare3; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + + public void setTaskNo(Long taskNo) + { + this.taskNo = taskNo; + } + + public Long getTaskNo() + { + return taskNo; + } + + public void setMydojobId(String mydojobId) + { + this.mydojobId = mydojobId; + } + + public String getMydojobId() + { + return mydojobId; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setSpare1(String spare1) + { + this.spare1 = spare1; + } + + public String getSpare1() + { + return spare1; + } + + public void setSpare2(String spare2) + { + this.spare2 = spare2; + } + + public String getSpare2() + { + return spare2; + } + + public void setSpare3(String spare3) + { + this.spare3 = spare3; + } + + public String getSpare3() + { + return spare3; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("type", getType()) + .append("taskNo", getTaskNo()) + .append("mydojobId", getMydojobId()) + .append("status", getStatus()) + .append("createTime", getCreateTime()) + .append("spare1", getSpare1()) + .append("spare2", getSpare2()) + .append("spare3", getSpare3()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/domain/MydoJob.java b/ruoyi-admin/src/main/java/com/ruoyi/job/domain/MydoJob.java new file mode 100644 index 0000000..0d7c0a6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/domain/MydoJob.java @@ -0,0 +1,92 @@ +package com.ruoyi.job.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 作业信息对象 mydo_job_wcs + * + * @author ruoyi + * @date 2025-09-22 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class MydoJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + private String jobId; + + /** 任务状态 */ + @Excel(name = "任务状态") + private String status; + + /** 任务号 */ + @Excel(name = "任务号") + private String taskNo; + + /** 巷道ID */ + @Excel(name = "巷道ID") + private String roadwayId; + + /** 货位ID */ + @Excel(name = "货位ID") + private String cargoSpaceId; + + /** 目标巷道ID */ + @Excel(name = "目标巷道ID") + private String targetRoadwayId; + + /** 目标货位ID */ + @Excel(name = "目标货位ID") + private String targetCargoSpaceId; + + /** 优先级 */ + @Excel(name = "优先级") + private Long priority; + + /** 执行阶段 */ + @Excel(name = "执行阶段") + private String executionPhase; + + /** 任务类型 */ + @Excel(name = "任务类型") + private String inOrOut; + + /** 开始时间 */ + @Excel(name = "开始时间") + private String startTime; + + /** 结束时间 */ + @Excel(name = "结束时间") + private String finishTime; + + /** 入口ID */ + @Excel(name = "入口ID") + private String entranceId; + + /** 出口ID */ + @Excel(name = "出口ID") + private String exitId; + + /** 仓库ID */ + @Excel(name = "仓库ID") + private String warehouseId; + + /** 重分标志 */ + @Excel(name = "重分标志") + private String faultFlag; + + private String trayCode; + + private String inOut; +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/JobCallBackMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/JobCallBackMapper.java new file mode 100644 index 0000000..163c6f0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/JobCallBackMapper.java @@ -0,0 +1,63 @@ +package com.ruoyi.job.mapper; + +import java.util.List; +import com.ruoyi.job.domain.JobCallBack; + +/** + * 任务完成回传Mapper接口 + * + * @author ruoyi + * @date 2025-09-25 + */ +public interface JobCallBackMapper +{ + /** + * 查询任务完成回传 + * + * @param id 任务完成回传主键 + * @return 任务完成回传 + */ + public JobCallBack selectJobCallBackById(Long id); + + /** + * 查询任务完成回传列表 + * + * @param jobCallBack 任务完成回传 + * @return 任务完成回传集合 + */ + public List selectJobCallBackList(JobCallBack jobCallBack); + + /** + * 新增任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + public int insertJobCallBack(JobCallBack jobCallBack); + + /** + * 修改任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + public int updateJobCallBack(JobCallBack jobCallBack); + + /** + * 删除任务完成回传 + * + * @param id 任务完成回传主键 + * @return 结果 + */ + public int deleteJobCallBackById(Long id); + + /** + * 批量删除任务完成回传 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteJobCallBackByIds(Long[] ids); + + List selectJobCallBackListNoSuccess(); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/MydoJobMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/MydoJobMapper.java new file mode 100644 index 0000000..76aaa74 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/mapper/MydoJobMapper.java @@ -0,0 +1,84 @@ +package com.ruoyi.job.mapper; + +import java.util.HashMap; +import java.util.List; +import com.ruoyi.job.domain.MydoJob; +import org.apache.ibatis.annotations.Param; + +/** + * 作业信息Mapper接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface MydoJobMapper +{ + /** + * 查询作业信息 + * + * @param jobId 作业信息主键 + * @return 作业信息 + */ + public MydoJob selectMydoJobByJobId(String jobId); + + /** + * 查询作业信息列表 + * + * @param mydoJob 作业信息 + * @return 作业信息集合 + */ + public List selectMydoJobList(MydoJob mydoJob); + + /** + * 新增作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + public int insertMydoJob(MydoJob mydoJob); + + /** + * 修改作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + public int updateMydoJob(MydoJob mydoJob); + + /** + * 按任务号查作业 + */ + public MydoJob selectMydoJobByTaskNo(String taskNo); + + /** + * 删除作业信息 + * + * @param jobId 作业信息主键 + * @return 结果 + */ + public int deleteMydoJobByJobId(String jobId); + + /** + * 批量删除作业信息 + * + * @param jobIds 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteMydoJobByJobIds(String[] jobIds); + + void ddjTaskFinish(HashMap map); + + void InvalidMydoJob(HashMap map); + + MydoJob selectMydoJobByExitList(@Param("exitList") List exitList); + + void insertHistory(String jobId); + + List selectInOrOutMydoJobList(MydoJob mydoJob); + + MydoJob selectMydoJobByEntranceList(@Param("entranceList") List entranceList); + + List selectMydoJobFinishList(MydoJob mydoJob); + + MydoJob selectRunningMydoJobByExitList(@Param("exitList") List exitList); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/service/IJobCallBackService.java b/ruoyi-admin/src/main/java/com/ruoyi/job/service/IJobCallBackService.java new file mode 100644 index 0000000..388beb3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/service/IJobCallBackService.java @@ -0,0 +1,63 @@ +package com.ruoyi.job.service; + +import java.util.List; +import com.ruoyi.job.domain.JobCallBack; + +/** + * 任务完成回传Service接口 + * + * @author ruoyi + * @date 2025-09-25 + */ +public interface IJobCallBackService +{ + /** + * 查询任务完成回传 + * + * @param id 任务完成回传主键 + * @return 任务完成回传 + */ + public JobCallBack selectJobCallBackById(Long id); + + /** + * 查询任务完成回传列表 + * + * @param jobCallBack 任务完成回传 + * @return 任务完成回传集合 + */ + public List selectJobCallBackList(JobCallBack jobCallBack); + + /** + * 新增任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + public int insertJobCallBack(JobCallBack jobCallBack); + + /** + * 修改任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + public int updateJobCallBack(JobCallBack jobCallBack); + + /** + * 批量删除任务完成回传 + * + * @param ids 需要删除的任务完成回传主键集合 + * @return 结果 + */ + public int deleteJobCallBackByIds(Long[] ids); + + /** + * 删除任务完成回传信息 + * + * @param id 任务完成回传主键 + * @return 结果 + */ + public int deleteJobCallBackById(Long id); + + void jobCallBack(); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/service/IMydoJobService.java b/ruoyi-admin/src/main/java/com/ruoyi/job/service/IMydoJobService.java new file mode 100644 index 0000000..94f7050 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/service/IMydoJobService.java @@ -0,0 +1,79 @@ +package com.ruoyi.job.service; + +import java.util.List; +import com.ruoyi.job.domain.MydoJob; + +/** + * 作业信息Service接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface IMydoJobService +{ + /** + * 查询作业信息 + * + * @param jobId 作业信息主键 + * @return 作业信息 + */ + public MydoJob selectMydoJobByJobId(String jobId); + + /** + * 查询作业信息列表 + * + * @param mydoJob 作业信息 + * @return 作业信息集合 + */ + public List selectMydoJobList(MydoJob mydoJob); + + /** + * 新增作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + public int insertMydoJob(MydoJob mydoJob); + + /** + * 修改作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + public int updateMydoJob(MydoJob mydoJob); + + /** + * 批量删除作业信息 + * + * @param jobIds 需要删除的作业信息主键集合 + * @return 结果 + */ + public int deleteMydoJobByJobIds(String[] jobIds); + + /** + * 删除作业信息信息 + * + * @param jobId 作业信息主键 + * @return 结果 + */ + public int deleteMydoJobByJobId(String jobId); + + MydoJob selectMydoJobByTaskNo(Long rwh); + + boolean ddjTaskFinish(Long rwh, Integer id); + + boolean InvalidMydoJob(Long rwh, Integer id); + + MydoJob selectMydoJobByExitList(List exitList); + + void insertHistory(String jobId); + + List selectInOrOutMydoJobList(MydoJob mydoJob); + + MydoJob selectMydoJobByEntranceList(List entranceList); + + List selectMydoJobFinishList(MydoJob mydoJob); + + MydoJob selectRunningMydoJobByExitList(List strings); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/JobCallBackServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/JobCallBackServiceImpl.java new file mode 100644 index 0000000..07f9afb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/JobCallBackServiceImpl.java @@ -0,0 +1,119 @@ +package com.ruoyi.job.service.impl; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.ruoyi.api.domain.RequestLog; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.job.domain.MydoJob; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.job.mapper.JobCallBackMapper; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.service.IJobCallBackService; + +/** + * 任务完成回传Service业务层处理 + * + * @author ruoyi + * @date 2025-09-25 + */ +@Service +public class JobCallBackServiceImpl implements IJobCallBackService +{ + @Autowired + private JobCallBackMapper jobCallBackMapper; + @Autowired + private IApiService apiService; + + /** + * 查询任务完成回传 + * + * @param id 任务完成回传主键 + * @return 任务完成回传 + */ + @Override + public JobCallBack selectJobCallBackById(Long id) + { + return jobCallBackMapper.selectJobCallBackById(id); + } + + /** + * 查询任务完成回传列表 + * + * @param jobCallBack 任务完成回传 + * @return 任务完成回传 + */ + @Override + public List selectJobCallBackList(JobCallBack jobCallBack) + { + return jobCallBackMapper.selectJobCallBackList(jobCallBack); + } + + /** + * 新增任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + @Override + public int insertJobCallBack(JobCallBack jobCallBack) + { + jobCallBack.setCreateTime(DateUtils.getNowDate()); + return jobCallBackMapper.insertJobCallBack(jobCallBack); + } + + /** + * 修改任务完成回传 + * + * @param jobCallBack 任务完成回传 + * @return 结果 + */ + @Override + public int updateJobCallBack(JobCallBack jobCallBack) + { + return jobCallBackMapper.updateJobCallBack(jobCallBack); + } + + /** + * 批量删除任务完成回传 + * + * @param ids 需要删除的任务完成回传主键 + * @return 结果 + */ + @Override + public int deleteJobCallBackByIds(Long[] ids) + { + return jobCallBackMapper.deleteJobCallBackByIds(ids); + } + + /** + * 删除任务完成回传信息 + * + * @param id 任务完成回传主键 + * @return 结果 + */ + @Override + public int deleteJobCallBackById(Long id) + { + return jobCallBackMapper.deleteJobCallBackById(id); + } + + @Override + public void jobCallBack() { + List jobCallBackList = jobCallBackMapper.selectJobCallBackListNoSuccess(); + for (JobCallBack jobCallBack : jobCallBackList) { + Map param = new HashMap<>(); + param.put("jobId", jobCallBack.getMydojobId()); + param.put("taskNo", jobCallBack.getTaskNo()); + param.put("wmsType", jobCallBack.getType()); + RequestLog requestLog = apiService.finishMydoJob(param); + if ("1".equals(requestLog.getIsSuccessful())){ + jobCallBack.setStatus("1"); + jobCallBackMapper.updateJobCallBack(jobCallBack); + } + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/MydoJobServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/MydoJobServiceImpl.java new file mode 100644 index 0000000..cad2dbc --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/job/service/impl/MydoJobServiceImpl.java @@ -0,0 +1,160 @@ +package com.ruoyi.job.service.impl; + +import java.util.HashMap; +import java.util.List; + +import cn.hutool.core.convert.Convert; +import com.ruoyi.common.utils.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.job.mapper.MydoJobMapper; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; + +/** + * 作业信息Service业务层处理 + * + * @author ruoyi + * @date 2025-09-22 + */ +@Service +public class MydoJobServiceImpl implements IMydoJobService +{ + @Autowired + private MydoJobMapper mydoJobMapper; + + /** + * 查询作业信息 + * + * @param jobId 作业信息主键 + * @return 作业信息 + */ + @Override + public MydoJob selectMydoJobByJobId(String jobId) + { + return mydoJobMapper.selectMydoJobByJobId(jobId); + } + + /** + * 查询作业信息列表 + * + * @param mydoJob 作业信息 + * @return 作业信息 + */ + @Override + public List selectMydoJobList(MydoJob mydoJob) + { + return mydoJobMapper.selectMydoJobList(mydoJob); + } + + /** + * 新增作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + @Override + public int insertMydoJob(MydoJob mydoJob) + { + mydoJob.setCreateTime(DateUtils.getNowDate()); + return mydoJobMapper.insertMydoJob(mydoJob); + } + + /** + * 修改作业信息 + * + * @param mydoJob 作业信息 + * @return 结果 + */ + @Override + public int updateMydoJob(MydoJob mydoJob) + { + return mydoJobMapper.updateMydoJob(mydoJob); + } + + /** + * 批量删除作业信息 + * + * @param jobIds 需要删除的作业信息主键 + * @return 结果 + */ + @Override + public int deleteMydoJobByJobIds(String[] jobIds) + { + return mydoJobMapper.deleteMydoJobByJobIds(jobIds); + } + + /** + * 删除作业信息信息 + * + * @param jobId 作业信息主键 + * @return 结果 + */ + @Override + public int deleteMydoJobByJobId(String jobId) + { + return mydoJobMapper.deleteMydoJobByJobId(jobId); + } + + @Override + public MydoJob selectMydoJobByTaskNo(Long rwh) { + String s = String.valueOf(rwh); + return mydoJobMapper.selectMydoJobByTaskNo(s); + } + + @Override + public boolean ddjTaskFinish(Long rwh, Integer id) { + HashMap map = new HashMap<>(); + map.put("v_rwh", rwh); + map.put("v_ddj_id", id); + mydoJobMapper.ddjTaskFinish(map); + if (Convert.toInt(map.get("v_result"))==10){ + return true; + }else { + throw new RuntimeException(map.get("v_result").toString()); + } + } + + @Override + public boolean InvalidMydoJob(Long rwh, Integer id) { + HashMap map = new HashMap<>(); + map.put("v_rwh", rwh); + map.put("v_ddj_id", id); + mydoJobMapper.InvalidMydoJob(map); + if (Convert.toInt(map.get("v_result"))==10){ + return true; + }else { + throw new RuntimeException(map.get("v_result").toString()); + } + } + + @Override + public MydoJob selectMydoJobByExitList(List exitList) { + return mydoJobMapper.selectMydoJobByExitList(exitList); + } + + @Override + public void insertHistory(String jobId) { + mydoJobMapper.insertHistory(jobId); + } + + @Override + public List selectInOrOutMydoJobList(MydoJob mydoJob) { + return mydoJobMapper.selectInOrOutMydoJobList(mydoJob); + } + + @Override + public MydoJob selectMydoJobByEntranceList(List entranceList) { + return mydoJobMapper.selectMydoJobByEntranceList(entranceList); + } + + @Override + public List selectMydoJobFinishList(MydoJob mydoJob) { + return mydoJobMapper.selectMydoJobFinishList(mydoJob); + } + + @Override + public MydoJob selectRunningMydoJobByExitList(List exitList) { + return mydoJobMapper.selectRunningMydoJobByExitList(exitList); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueController.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueController.java new file mode 100644 index 0000000..7e10f8f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueController.java @@ -0,0 +1,104 @@ +package com.ruoyi.queue.agv.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * AGV任务队列Controller + * + * @author ruoyi + * @date 2025-07-03 + */ +@RestController +@RequestMapping("/agv/queue") +public class AgvQueueController extends BaseController +{ + @Autowired + private IAgvQueueService agvQueueService; + + /** + * 查询AGV任务队列列表 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:list')") + @GetMapping("/list") + public TableDataInfo list(AgvQueue agvQueue) + { + startPage(); + List list = agvQueueService.selectAgvQueueList(agvQueue); + return getDataTable(list); + } + + /** + * 导出AGV任务队列列表 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:export')") + @Log(title = "AGV任务队列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, AgvQueue agvQueue) + { + List list = agvQueueService.selectAgvQueueList(agvQueue); + ExcelUtil util = new ExcelUtil(AgvQueue.class); + util.exportExcel(response, list, "AGV任务队列数据"); + } + + /** + * 获取AGV任务队列详细信息 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:query')") + @GetMapping(value = "/{reqCode}") + public AjaxResult getInfo(@PathVariable("reqCode") String reqCode) + { + return success(agvQueueService.selectAgvQueueByReqCode(reqCode)); + } + + /** + * 新增AGV任务队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:add')") + @Log(title = "AGV任务队列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody AgvQueue agvQueue) + { + return toAjax(agvQueueService.insertAgvQueue(agvQueue)); + } + + /** + * 修改AGV任务队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:edit')") + @Log(title = "AGV任务队列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody AgvQueue agvQueue) + { + return toAjax(agvQueueService.updateAgvQueue(agvQueue)); + } + + /** + * 删除AGV任务队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queue:remove')") + @Log(title = "AGV任务队列", businessType = BusinessType.DELETE) + @DeleteMapping("/{reqCodes}") + public AjaxResult remove(@PathVariable String[] reqCodes) + { + return toAjax(agvQueueService.deleteAgvQueueByReqCodes(reqCodes)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueFinishController.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueFinishController.java new file mode 100644 index 0000000..f7cee92 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/controller/AgvQueueFinishController.java @@ -0,0 +1,104 @@ +package com.ruoyi.queue.agv.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.queue.agv.domain.AgvQueueFinish; +import com.ruoyi.queue.agv.service.IAgvQueueFinishService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * agv历史队列Controller + * + * @author ruoyi + * @date 2025-07-30 + */ +@RestController +@RequestMapping("/agv/queueFinish") +public class AgvQueueFinishController extends BaseController +{ + @Autowired + private IAgvQueueFinishService agvQueueFinishService; + + /** + * 查询agv历史队列列表 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:list')") + @GetMapping("/list") + public TableDataInfo list(AgvQueueFinish agvQueueFinish) + { + startPage(); + List list = agvQueueFinishService.selectAgvQueueFinishList(agvQueueFinish); + return getDataTable(list); + } + + /** + * 导出agv历史队列列表 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:export')") + @Log(title = "agv历史队列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, AgvQueueFinish agvQueueFinish) + { + List list = agvQueueFinishService.selectAgvQueueFinishList(agvQueueFinish); + ExcelUtil util = new ExcelUtil(AgvQueueFinish.class); + util.exportExcel(response, list, "agv历史队列数据"); + } + + /** + * 获取agv历史队列详细信息 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:query')") + @GetMapping(value = "/{reqCode}") + public AjaxResult getInfo(@PathVariable("reqCode") String reqCode) + { + return success(agvQueueFinishService.selectAgvQueueFinishByReqCode(reqCode)); + } + + /** + * 新增agv历史队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:add')") + @Log(title = "agv历史队列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody AgvQueueFinish agvQueueFinish) + { + return toAjax(agvQueueFinishService.insertAgvQueueFinish(agvQueueFinish)); + } + + /** + * 修改agv历史队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:edit')") + @Log(title = "agv历史队列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody AgvQueueFinish agvQueueFinish) + { + return toAjax(agvQueueFinishService.updateAgvQueueFinish(agvQueueFinish)); + } + + /** + * 删除agv历史队列 + */ + @PreAuthorize("@ss.hasPermi('agv:queueFinish:remove')") + @Log(title = "agv历史队列", businessType = BusinessType.DELETE) + @DeleteMapping("/{reqCodes}") + public AjaxResult remove(@PathVariable String[] reqCodes) + { + return toAjax(agvQueueFinishService.deleteAgvQueueFinishByReqCodes(reqCodes)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueue.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueue.java new file mode 100644 index 0000000..f9165b6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueue.java @@ -0,0 +1,277 @@ +package com.ruoyi.queue.agv.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * AGV任务队列对象 agv_queue + * + * @author ruoyi + * @date 2025-07-03 + */ +public class AgvQueue extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 请求编号 */ + private String reqCode; + + /** 请求时间截 yyyy-MM-dd HH:mm:ss */ + @Excel(name = "请求时间截 yyyy-MM-dd HH:mm:ss") + private String reqTime; + + /** 起点 */ + @Excel(name = "起点 ") + private String positionCode1; + + /** 终点 */ + @Excel(name = "终点") + private String positionCode2; + + /** 任务类型 */ + @Excel(name = "任务类型 ") + private String taskType; + + /** 容器类型 */ + @Excel(name = "容器类型") + private String ctnrType; + + /** 容器编号 */ + @Excel(name = "容器编号") + private String ctnrCode; + + /** 容器数量 */ + @Excel(name = "容器数量") + private String ctnrNum; + + /** 任务模式 */ + @Excel(name = "任务模式") + private String taskMode; + + /** 优先级 */ + @Excel(name = "优先级") + private String priority; + + /** 设备类型 */ + @Excel(name = "设备类型") + private String agvType; + + /** 继续请求 */ + @Excel(name = "继续请求") + private String request; + + /** 结束时间 yyyy-MM-dd HH:mm:ss */ + @Excel(name = "结束时间 yyyy-MM-dd HH:mm:ss") + private String finishTime; + + /** wms托盘码/容器码 */ + @Excel(name = "wms托盘码/容器码") + private String wmsTraycode; + + /** wms任务号 */ + @Excel(name = "wms任务号") + private String wmsTaskNo; + + private String status; + + private String taskCode; + + private String wmsTaskType; + + public String getWmsTaskType() { + return wmsTaskType; + } + + public void setWmsTaskType(String wmsTaskType) { + this.wmsTaskType = wmsTaskType; + } + + public String getTaskCode() { + return taskCode; + } + + public void setTaskCode(String taskCode) { + this.taskCode = taskCode; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public void setReqCode(String reqCode) + { + this.reqCode = reqCode; + } + + public String getReqCode() + { + return reqCode; + } + + public void setReqTime(String reqTime) + { + this.reqTime = reqTime; + } + + public String getReqTime() + { + return reqTime; + } + + public void setPositionCode1(String positionCode1) + { + this.positionCode1 = positionCode1; + } + + public String getPositionCode1() + { + return positionCode1; + } + + public void setPositionCode2(String positionCode2) + { + this.positionCode2 = positionCode2; + } + + public String getPositionCode2() + { + return positionCode2; + } + + public void setTaskType(String taskType) + { + this.taskType = taskType; + } + + public String getTaskType() + { + return taskType; + } + + public void setCtnrType(String ctnrType) + { + this.ctnrType = ctnrType; + } + + public String getCtnrType() + { + return ctnrType; + } + + public void setCtnrCode(String ctnrCode) + { + this.ctnrCode = ctnrCode; + } + + public String getCtnrCode() + { + return ctnrCode; + } + + public void setCtnrNum(String ctnrNum) + { + this.ctnrNum = ctnrNum; + } + + public String getCtnrNum() + { + return ctnrNum; + } + + public void setTaskMode(String taskMode) + { + this.taskMode = taskMode; + } + + public String getTaskMode() + { + return taskMode; + } + + public void setPriority(String priority) + { + this.priority = priority; + } + + public String getPriority() + { + return priority; + } + + public void setAgvType(String agvType) + { + this.agvType = agvType; + } + + public String getAgvType() + { + return agvType; + } + + public void setRequest(String request) + { + this.request = request; + } + + public String getRequest() + { + return request; + } + + public void setFinishTime(String finishTime) + { + this.finishTime = finishTime; + } + + public String getFinishTime() + { + return finishTime; + } + + public void setWmsTraycode(String wmsTraycode) + { + this.wmsTraycode = wmsTraycode; + } + + public String getWmsTraycode() + { + return wmsTraycode; + } + + public void setWmsTaskNo(String wmsTaskNo) + { + this.wmsTaskNo = wmsTaskNo; + } + + public String getWmsTaskNo() + { + return wmsTaskNo; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("reqCode", getReqCode()) + .append("reqTime", getReqTime()) + .append("positionCode1", getPositionCode1()) + .append("positionCode2", getPositionCode2()) + .append("taskType", getTaskType()) + .append("ctnrType", getCtnrType()) + .append("ctnrCode", getCtnrCode()) + .append("ctnrNum", getCtnrNum()) + .append("taskMode", getTaskMode()) + .append("priority", getPriority()) + .append("agvType", getAgvType()) + .append("request", getRequest()) + .append("finishTime", getFinishTime()) + .append("wmsTraycode", getWmsTraycode()) + .append("wmsTaskNo", getWmsTaskNo()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueueFinish.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueueFinish.java new file mode 100644 index 0000000..f29e654 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/domain/AgvQueueFinish.java @@ -0,0 +1,292 @@ +package com.ruoyi.queue.agv.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * agv历史队列对象 agv_queue_finish + * + * @author ruoyi + * @date 2025-07-30 + */ +public class AgvQueueFinish extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 请求编号 */ + private String reqCode; + + /** 请求时间 */ + @Excel(name = "请求时间") + private String reqTime; + + /** 起点 */ + @Excel(name = "起点 ") + private String positionCode1; + + /** 终点 */ + @Excel(name = "终点") + private String positionCode2; + + /** 任务类型 */ + @Excel(name = "任务类型 ") + private String taskType; + + /** 容器类型 */ + @Excel(name = "容器类型") + private String ctnrType; + + /** 容器编号 */ + @Excel(name = "容器编号") + private String ctnrCode; + + /** 容器数量 */ + @Excel(name = "容器数量") + private String ctnrNum; + + /** 任务模式 */ + @Excel(name = "任务模式") + private String taskMode; + + /** 优先级 */ + @Excel(name = "优先级") + private String priority; + + /** 设备类型 */ + @Excel(name = "设备类型") + private String agvType; + + /** 继续请求 */ + @Excel(name = "继续请求") + private String request; + + /** 结束时间 */ + @Excel(name = "结束时间") + private String finishTime; + + /** wms托盘码/容器码 */ + @Excel(name = "wms托盘码/容器码") + private String wmsTraycode; + + /** wms任务号 */ + @Excel(name = "wms任务号") + private String wmsTaskNo; + + /** 任务单号 */ + @Excel(name = "任务单号") + private String taskCode; + + /** 任务状态 */ + @Excel(name = "任务状态") + private String status; + + /** wms任务类型 */ + @Excel(name = "wms任务类型") + private String wmsTaskType; + + public void setReqCode(String reqCode) + { + this.reqCode = reqCode; + } + + public String getReqCode() + { + return reqCode; + } + + public void setReqTime(String reqTime) + { + this.reqTime = reqTime; + } + + public String getReqTime() + { + return reqTime; + } + + public void setPositionCode1(String positionCode1) + { + this.positionCode1 = positionCode1; + } + + public String getPositionCode1() + { + return positionCode1; + } + + public void setPositionCode2(String positionCode2) + { + this.positionCode2 = positionCode2; + } + + public String getPositionCode2() + { + return positionCode2; + } + + public void setTaskType(String taskType) + { + this.taskType = taskType; + } + + public String getTaskType() + { + return taskType; + } + + public void setCtnrType(String ctnrType) + { + this.ctnrType = ctnrType; + } + + public String getCtnrType() + { + return ctnrType; + } + + public void setCtnrCode(String ctnrCode) + { + this.ctnrCode = ctnrCode; + } + + public String getCtnrCode() + { + return ctnrCode; + } + + public void setCtnrNum(String ctnrNum) + { + this.ctnrNum = ctnrNum; + } + + public String getCtnrNum() + { + return ctnrNum; + } + + public void setTaskMode(String taskMode) + { + this.taskMode = taskMode; + } + + public String getTaskMode() + { + return taskMode; + } + + public void setPriority(String priority) + { + this.priority = priority; + } + + public String getPriority() + { + return priority; + } + + public void setAgvType(String agvType) + { + this.agvType = agvType; + } + + public String getAgvType() + { + return agvType; + } + + public void setRequest(String request) + { + this.request = request; + } + + public String getRequest() + { + return request; + } + + public void setFinishTime(String finishTime) + { + this.finishTime = finishTime; + } + + public String getFinishTime() + { + return finishTime; + } + + public void setWmsTraycode(String wmsTraycode) + { + this.wmsTraycode = wmsTraycode; + } + + public String getWmsTraycode() + { + return wmsTraycode; + } + + public void setWmsTaskNo(String wmsTaskNo) + { + this.wmsTaskNo = wmsTaskNo; + } + + public String getWmsTaskNo() + { + return wmsTaskNo; + } + + public void setTaskCode(String taskCode) + { + this.taskCode = taskCode; + } + + public String getTaskCode() + { + return taskCode; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + public void setWmsTaskType(String wmsTaskType) + { + this.wmsTaskType = wmsTaskType; + } + + public String getWmsTaskType() + { + return wmsTaskType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("reqCode", getReqCode()) + .append("reqTime", getReqTime()) + .append("positionCode1", getPositionCode1()) + .append("positionCode2", getPositionCode2()) + .append("taskType", getTaskType()) + .append("ctnrType", getCtnrType()) + .append("ctnrCode", getCtnrCode()) + .append("ctnrNum", getCtnrNum()) + .append("taskMode", getTaskMode()) + .append("priority", getPriority()) + .append("agvType", getAgvType()) + .append("request", getRequest()) + .append("finishTime", getFinishTime()) + .append("wmsTraycode", getWmsTraycode()) + .append("wmsTaskNo", getWmsTaskNo()) + .append("taskCode", getTaskCode()) + .append("status", getStatus()) + .append("wmsTaskType", getWmsTaskType()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueFinishMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueFinishMapper.java new file mode 100644 index 0000000..46af074 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueFinishMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.queue.agv.mapper; + +import java.util.List; +import com.ruoyi.queue.agv.domain.AgvQueueFinish; + +/** + * agv历史队列Mapper接口 + * + * @author ruoyi + * @date 2025-07-30 + */ +public interface AgvQueueFinishMapper +{ + /** + * 查询agv历史队列 + * + * @param reqCode agv历史队列主键 + * @return agv历史队列 + */ + public AgvQueueFinish selectAgvQueueFinishByReqCode(String reqCode); + + /** + * 查询agv历史队列列表 + * + * @param agvQueueFinish agv历史队列 + * @return agv历史队列集合 + */ + public List selectAgvQueueFinishList(AgvQueueFinish agvQueueFinish); + + /** + * 新增agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + public int insertAgvQueueFinish(AgvQueueFinish agvQueueFinish); + + /** + * 修改agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + public int updateAgvQueueFinish(AgvQueueFinish agvQueueFinish); + + /** + * 删除agv历史队列 + * + * @param reqCode agv历史队列主键 + * @return 结果 + */ + public int deleteAgvQueueFinishByReqCode(String reqCode); + + /** + * 批量删除agv历史队列 + * + * @param reqCodes 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteAgvQueueFinishByReqCodes(String[] reqCodes); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueMapper.java new file mode 100644 index 0000000..7cedfad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/mapper/AgvQueueMapper.java @@ -0,0 +1,70 @@ +package com.ruoyi.queue.agv.mapper; + +import java.util.List; +import com.ruoyi.queue.agv.domain.AgvQueue; + +/** + * AGV任务队列Mapper接口 + * + * @author ruoyi + * @date 2025-07-03 + */ +public interface AgvQueueMapper +{ + /** + * 查询AGV任务队列 + * + * @param reqCode AGV任务队列主键 + * @return AGV任务队列 + */ + public AgvQueue selectAgvQueueByReqCode(String reqCode); + + /** + * 查询AGV任务队列列表 + * + * @param agvQueue AGV任务队列 + * @return AGV任务队列集合 + */ + public List selectAgvQueueList(AgvQueue agvQueue); + + /** + * 新增AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + public int insertAgvQueue(AgvQueue agvQueue); + + /** + * 修改AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + public int updateAgvQueue(AgvQueue agvQueue); + + /** + * 删除AGV任务队列 + * + * @param reqCode AGV任务队列主键 + * @return 结果 + */ + public int deleteAgvQueueByReqCode(String reqCode); + + /** + * 批量删除AGV任务队列 + * + * @param reqCodes 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteAgvQueueByReqCodes(String[] reqCodes); + + /** + * 根据任务单号获取agv队列 + * @param taskCode + * @return + */ + AgvQueue selectAgvQueueByTaskCode(String taskCode); + + void insertHistory(String reqCode); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueFinishService.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueFinishService.java new file mode 100644 index 0000000..bd77622 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueFinishService.java @@ -0,0 +1,61 @@ +package com.ruoyi.queue.agv.service; + +import java.util.List; +import com.ruoyi.queue.agv.domain.AgvQueueFinish; + +/** + * agv历史队列Service接口 + * + * @author ruoyi + * @date 2025-07-30 + */ +public interface IAgvQueueFinishService +{ + /** + * 查询agv历史队列 + * + * @param reqCode agv历史队列主键 + * @return agv历史队列 + */ + public AgvQueueFinish selectAgvQueueFinishByReqCode(String reqCode); + + /** + * 查询agv历史队列列表 + * + * @param agvQueueFinish agv历史队列 + * @return agv历史队列集合 + */ + public List selectAgvQueueFinishList(AgvQueueFinish agvQueueFinish); + + /** + * 新增agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + public int insertAgvQueueFinish(AgvQueueFinish agvQueueFinish); + + /** + * 修改agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + public int updateAgvQueueFinish(AgvQueueFinish agvQueueFinish); + + /** + * 批量删除agv历史队列 + * + * @param reqCodes 需要删除的agv历史队列主键集合 + * @return 结果 + */ + public int deleteAgvQueueFinishByReqCodes(String[] reqCodes); + + /** + * 删除agv历史队列信息 + * + * @param reqCode agv历史队列主键 + * @return 结果 + */ + public int deleteAgvQueueFinishByReqCode(String reqCode); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueService.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueService.java new file mode 100644 index 0000000..0e565c1 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/IAgvQueueService.java @@ -0,0 +1,74 @@ +package com.ruoyi.queue.agv.service; + +import java.util.List; +import com.ruoyi.queue.agv.domain.AgvQueue; + +/** + * AGV任务队列Service接口 + * + * @author ruoyi + * @date 2025-07-03 + */ +public interface IAgvQueueService +{ + /** + * 查询AGV任务队列 + * + * @param reqCode AGV任务队列主键 + * @return AGV任务队列 + */ + public AgvQueue selectAgvQueueByReqCode(String reqCode); + + /** + * 查询AGV任务队列列表 + * + * @param agvQueue AGV任务队列 + * @return AGV任务队列集合 + */ + public List selectAgvQueueList(AgvQueue agvQueue); + + /** + * 新增AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + public int insertAgvQueue(AgvQueue agvQueue); + + /** + * 修改AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + public int updateAgvQueue(AgvQueue agvQueue); + + /** + * 批量删除AGV任务队列 + * + * @param reqCodes 需要删除的AGV任务队列主键集合 + * @return 结果 + */ + public int deleteAgvQueueByReqCodes(String[] reqCodes); + + /** + * 删除AGV任务队列信息 + * + * @param reqCode AGV任务队列主键 + * @return 结果 + */ + public int deleteAgvQueueByReqCode(String reqCode); + + /** + * 根据任务单号获取agv队列 + * @param taskCode + * @return + */ + AgvQueue selectAgvQueueByTaskCode(String taskCode); + + /** + * 插入agv完成历史 + * @param reqCode + */ + void insertHistory(String reqCode); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueFinishServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueFinishServiceImpl.java new file mode 100644 index 0000000..462042a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueFinishServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.queue.agv.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.queue.agv.mapper.AgvQueueFinishMapper; +import com.ruoyi.queue.agv.domain.AgvQueueFinish; +import com.ruoyi.queue.agv.service.IAgvQueueFinishService; + +/** + * agv历史队列Service业务层处理 + * + * @author ruoyi + * @date 2025-07-30 + */ +@Service +public class AgvQueueFinishServiceImpl implements IAgvQueueFinishService +{ + @Autowired + private AgvQueueFinishMapper agvQueueFinishMapper; + + /** + * 查询agv历史队列 + * + * @param reqCode agv历史队列主键 + * @return agv历史队列 + */ + @Override + public AgvQueueFinish selectAgvQueueFinishByReqCode(String reqCode) + { + return agvQueueFinishMapper.selectAgvQueueFinishByReqCode(reqCode); + } + + /** + * 查询agv历史队列列表 + * + * @param agvQueueFinish agv历史队列 + * @return agv历史队列 + */ + @Override + public List selectAgvQueueFinishList(AgvQueueFinish agvQueueFinish) + { + return agvQueueFinishMapper.selectAgvQueueFinishList(agvQueueFinish); + } + + /** + * 新增agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + @Override + public int insertAgvQueueFinish(AgvQueueFinish agvQueueFinish) + { + return agvQueueFinishMapper.insertAgvQueueFinish(agvQueueFinish); + } + + /** + * 修改agv历史队列 + * + * @param agvQueueFinish agv历史队列 + * @return 结果 + */ + @Override + public int updateAgvQueueFinish(AgvQueueFinish agvQueueFinish) + { + return agvQueueFinishMapper.updateAgvQueueFinish(agvQueueFinish); + } + + /** + * 批量删除agv历史队列 + * + * @param reqCodes 需要删除的agv历史队列主键 + * @return 结果 + */ + @Override + public int deleteAgvQueueFinishByReqCodes(String[] reqCodes) + { + return agvQueueFinishMapper.deleteAgvQueueFinishByReqCodes(reqCodes); + } + + /** + * 删除agv历史队列信息 + * + * @param reqCode agv历史队列主键 + * @return 结果 + */ + @Override + public int deleteAgvQueueFinishByReqCode(String reqCode) + { + return agvQueueFinishMapper.deleteAgvQueueFinishByReqCode(reqCode); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueServiceImpl.java new file mode 100644 index 0000000..df2bc6f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/agv/service/impl/AgvQueueServiceImpl.java @@ -0,0 +1,103 @@ +package com.ruoyi.queue.agv.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.queue.agv.mapper.AgvQueueMapper; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.service.IAgvQueueService; + +/** + * AGV任务队列Service业务层处理 + * + * @author ruoyi + * @date 2025-07-03 + */ +@Service +public class AgvQueueServiceImpl implements IAgvQueueService +{ + @Autowired + private AgvQueueMapper agvQueueMapper; + + /** + * 查询AGV任务队列 + * + * @param reqCode AGV任务队列主键 + * @return AGV任务队列 + */ + @Override + public AgvQueue selectAgvQueueByReqCode(String reqCode) + { + return agvQueueMapper.selectAgvQueueByReqCode(reqCode); + } + + /** + * 查询AGV任务队列列表 + * + * @param agvQueue AGV任务队列 + * @return AGV任务队列 + */ + @Override + public List selectAgvQueueList(AgvQueue agvQueue) + { + return agvQueueMapper.selectAgvQueueList(agvQueue); + } + + /** + * 新增AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + @Override + public int insertAgvQueue(AgvQueue agvQueue) + { + return agvQueueMapper.insertAgvQueue(agvQueue); + } + + /** + * 修改AGV任务队列 + * + * @param agvQueue AGV任务队列 + * @return 结果 + */ + @Override + public int updateAgvQueue(AgvQueue agvQueue) + { + return agvQueueMapper.updateAgvQueue(agvQueue); + } + + /** + * 批量删除AGV任务队列 + * + * @param reqCodes 需要删除的AGV任务队列主键 + * @return 结果 + */ + @Override + public int deleteAgvQueueByReqCodes(String[] reqCodes) + { + return agvQueueMapper.deleteAgvQueueByReqCodes(reqCodes); + } + + /** + * 删除AGV任务队列信息 + * + * @param reqCode AGV任务队列主键 + * @return 结果 + */ + @Override + public int deleteAgvQueueByReqCode(String reqCode) + { + return agvQueueMapper.deleteAgvQueueByReqCode(reqCode); + } + + @Override + public AgvQueue selectAgvQueueByTaskCode(String taskCode) { + return agvQueueMapper.selectAgvQueueByTaskCode(taskCode); + } + + @Override + public void insertHistory(String reqCode) { + agvQueueMapper.insertHistory(reqCode); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueController.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueController.java new file mode 100644 index 0000000..fb9fa67 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueController.java @@ -0,0 +1,104 @@ +package com.ruoyi.queue.ddj.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 堆垛机执行队列Controller + * + * @author ruoyi + * @date 2025-09-22 + */ +@RestController +@RequestMapping("/queue/ddj") +public class DdjDeviceExecutionQueueController extends BaseController +{ + @Autowired + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + + /** + * 查询堆垛机执行队列列表 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:list')") + @GetMapping("/list") + public TableDataInfo list(DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + startPage(); + List list = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueList(ddjDeviceExecutionQueue); + return getDataTable(list); + } + + /** + * 导出堆垛机执行队列列表 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:export')") + @Log(title = "堆垛机执行队列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + List list = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueList(ddjDeviceExecutionQueue); + ExcelUtil util = new ExcelUtil(DdjDeviceExecutionQueue.class); + util.exportExcel(response, list, "堆垛机执行队列数据"); + } + + /** + * 获取堆垛机执行队列详细信息 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:query')") + @GetMapping(value = "/{uuid}") + public AjaxResult getInfo(@PathVariable("uuid") String uuid) + { + return success(ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueByUuid(uuid)); + } + + /** + * 新增堆垛机执行队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:add')") + @Log(title = "堆垛机执行队列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + return toAjax(ddjDeviceExecutionQueueService.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue)); + } + + /** + * 修改堆垛机执行队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:edit')") + @Log(title = "堆垛机执行队列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + return toAjax(ddjDeviceExecutionQueueService.updateDdjDeviceExecutionQueue(ddjDeviceExecutionQueue)); + } + + /** + * 删除堆垛机执行队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddj:remove')") + @Log(title = "堆垛机执行队列", businessType = BusinessType.DELETE) + @DeleteMapping("/{uuids}") + public AjaxResult remove(@PathVariable String[] uuids) + { + return toAjax(ddjDeviceExecutionQueueService.deleteDdjDeviceExecutionQueueByUuids(uuids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueFinishController.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueFinishController.java new file mode 100644 index 0000000..4a275aa --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/controller/DdjDeviceExecutionQueueFinishController.java @@ -0,0 +1,104 @@ +package com.ruoyi.queue.ddj.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueueFinish; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueFinishService; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 堆垛机完成队列Controller + * + * @author ruoyi + * @date 2025-09-22 + */ +@RestController +@RequestMapping("/queue/ddjFinish") +public class DdjDeviceExecutionQueueFinishController extends BaseController +{ + @Autowired + private IDdjDeviceExecutionQueueFinishService ddjDeviceExecutionQueueFinishService; + + /** + * 查询堆垛机完成队列列表 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:list')") + @GetMapping("/list") + public TableDataInfo list(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + startPage(); + List list = ddjDeviceExecutionQueueFinishService.selectDdjDeviceExecutionQueueFinishList(ddjDeviceExecutionQueueFinish); + return getDataTable(list); + } + + /** + * 导出堆垛机完成队列列表 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:export')") + @Log(title = "堆垛机完成队列", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + List list = ddjDeviceExecutionQueueFinishService.selectDdjDeviceExecutionQueueFinishList(ddjDeviceExecutionQueueFinish); + ExcelUtil util = new ExcelUtil(DdjDeviceExecutionQueueFinish.class); + util.exportExcel(response, list, "堆垛机完成队列数据"); + } + + /** + * 获取堆垛机完成队列详细信息 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:query')") + @GetMapping(value = "/{uuid}") + public AjaxResult getInfo(@PathVariable("uuid") String uuid) + { + return success(ddjDeviceExecutionQueueFinishService.selectDdjDeviceExecutionQueueFinishByUuid(uuid)); + } + + /** + * 新增堆垛机完成队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:add')") + @Log(title = "堆垛机完成队列", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + return toAjax(ddjDeviceExecutionQueueFinishService.insertDdjDeviceExecutionQueueFinish(ddjDeviceExecutionQueueFinish)); + } + + /** + * 修改堆垛机完成队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:edit')") + @Log(title = "堆垛机完成队列", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + return toAjax(ddjDeviceExecutionQueueFinishService.updateDdjDeviceExecutionQueueFinish(ddjDeviceExecutionQueueFinish)); + } + + /** + * 删除堆垛机完成队列 + */ + @PreAuthorize("@ss.hasPermi('queue:ddjFinish:remove')") + @Log(title = "堆垛机完成队列", businessType = BusinessType.DELETE) + @DeleteMapping("/{uuids}") + public AjaxResult remove(@PathVariable String[] uuids) + { + return toAjax(ddjDeviceExecutionQueueFinishService.deleteDdjDeviceExecutionQueueFinishByUuids(uuids)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueue.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueue.java new file mode 100644 index 0000000..fce38e0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueue.java @@ -0,0 +1,106 @@ +package com.ruoyi.queue.ddj.domain; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 堆垛机执行队列对象 ddj_device_execution_queue + * + * @author ruoyi + * @date 2025-09-22 + */ +@AllArgsConstructor +@NoArgsConstructor +@Data +@ToString +public class DdjDeviceExecutionQueue extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** uuid */ + private String uuid; + + /** 任务号 */ + @Excel(name = "任务号") + private Long rwh; + + /** 设备名称 */ + @Excel(name = "设备名称") + private String devName; + + /** 设备id */ + @Excel(name = "设备id") + private Integer devId; + + /** 任务状态 */ + @Excel(name = "任务状态") + private Integer rwzt; + + /** 原排 */ + @Excel(name = "原排") + private Integer yph; + + /** 原列 */ + @Excel(name = "原列") + private Integer ylh; + + /** 原层 */ + @Excel(name = "原层") + private Integer ych; + + /** 目标排 */ + @Excel(name = "目标排") + private Integer mbph; + + /** 目标列 */ + @Excel(name = "目标列") + private Integer mblh; + + /** 目标层 */ + @Excel(name = "目标层") + private Integer mbch; + + /** 堆垛机下发 */ + @Excel(name = "堆垛机下发") + private String ddjxf; + + /** 任务类型 */ + @Excel(name = "任务类型") + private Integer rwlx; + + /** 生成时间 */ + @Excel(name = "生成时间") + private String scsj; + + /** 下发时间 */ + @Excel(name = "下发时间") + private String xfsj; + + /** 结束时间 */ + @Excel(name = "结束时间") + private String jssj; + + /** 命令方式 */ + @Excel(name = "命令方式") + private Integer mlfs; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare1; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare2; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare3; + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueueFinish.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueueFinish.java new file mode 100644 index 0000000..228a801 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/domain/DdjDeviceExecutionQueueFinish.java @@ -0,0 +1,322 @@ +package com.ruoyi.queue.ddj.domain; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 堆垛机完成队列对象 ddj_device_execution_queue_finish + * + * @author ruoyi + * @date 2025-09-22 + */ +public class DdjDeviceExecutionQueueFinish extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** uuid */ + private String uuid; + + /** 任务号 */ + @Excel(name = "任务号") + private Long rwh; + + /** 设备名称 */ + @Excel(name = "设备名称") + private String devName; + + /** 设备id */ + @Excel(name = "设备id") + private Long devId; + + /** 任务状态 */ + @Excel(name = "任务状态") + private Long rwzt; + + /** 原排 */ + @Excel(name = "原排") + private Long yph; + + /** 原列 */ + @Excel(name = "原列") + private Long ylh; + + /** 原层 */ + @Excel(name = "原层") + private Long ych; + + /** 目标排 */ + @Excel(name = "目标排") + private Long mbph; + + /** 目标列 */ + @Excel(name = "目标列") + private Long mblh; + + /** 目标层 */ + @Excel(name = "目标层") + private Long mbch; + + /** 堆垛机下发 */ + @Excel(name = "堆垛机下发") + private String ddjxf; + + /** 任务类型 */ + @Excel(name = "任务类型") + private Long rwlx; + + /** 生成时间 */ + @Excel(name = "生成时间") + private String scsj; + + /** 下发时间 */ + @Excel(name = "下发时间") + private String xfsj; + + /** 结束时间 */ + @Excel(name = "结束时间") + private String jssj; + + /** 命令方式 */ + @Excel(name = "命令方式") + private Long mlfs; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare1; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare2; + + /** $column.columnComment */ + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") + private String spare3; + + public void setUuid(String uuid) + { + this.uuid = uuid; + } + + public String getUuid() + { + return uuid; + } + + public void setRwh(Long rwh) + { + this.rwh = rwh; + } + + public Long getRwh() + { + return rwh; + } + + public void setDevName(String devName) + { + this.devName = devName; + } + + public String getDevName() + { + return devName; + } + + public void setDevId(Long devId) + { + this.devId = devId; + } + + public Long getDevId() + { + return devId; + } + + public void setRwzt(Long rwzt) + { + this.rwzt = rwzt; + } + + public Long getRwzt() + { + return rwzt; + } + + public void setYph(Long yph) + { + this.yph = yph; + } + + public Long getYph() + { + return yph; + } + + public void setYlh(Long ylh) + { + this.ylh = ylh; + } + + public Long getYlh() + { + return ylh; + } + + public void setYch(Long ych) + { + this.ych = ych; + } + + public Long getYch() + { + return ych; + } + + public void setMbph(Long mbph) + { + this.mbph = mbph; + } + + public Long getMbph() + { + return mbph; + } + + public void setMblh(Long mblh) + { + this.mblh = mblh; + } + + public Long getMblh() + { + return mblh; + } + + public void setMbch(Long mbch) + { + this.mbch = mbch; + } + + public Long getMbch() + { + return mbch; + } + + public void setDdjxf(String ddjxf) + { + this.ddjxf = ddjxf; + } + + public String getDdjxf() + { + return ddjxf; + } + + public void setRwlx(Long rwlx) + { + this.rwlx = rwlx; + } + + public Long getRwlx() + { + return rwlx; + } + + public void setScsj(String scsj) + { + this.scsj = scsj; + } + + public String getScsj() + { + return scsj; + } + + public void setXfsj(String xfsj) + { + this.xfsj = xfsj; + } + + public String getXfsj() + { + return xfsj; + } + + public void setJssj(String jssj) + { + this.jssj = jssj; + } + + public String getJssj() + { + return jssj; + } + + public void setMlfs(Long mlfs) + { + this.mlfs = mlfs; + } + + public Long getMlfs() + { + return mlfs; + } + + public void setSpare1(String spare1) + { + this.spare1 = spare1; + } + + public String getSpare1() + { + return spare1; + } + + public void setSpare2(String spare2) + { + this.spare2 = spare2; + } + + public String getSpare2() + { + return spare2; + } + + public void setSpare3(String spare3) + { + this.spare3 = spare3; + } + + public String getSpare3() + { + return spare3; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("uuid", getUuid()) + .append("rwh", getRwh()) + .append("devName", getDevName()) + .append("devId", getDevId()) + .append("rwzt", getRwzt()) + .append("yph", getYph()) + .append("ylh", getYlh()) + .append("ych", getYch()) + .append("mbph", getMbph()) + .append("mblh", getMblh()) + .append("mbch", getMbch()) + .append("ddjxf", getDdjxf()) + .append("rwlx", getRwlx()) + .append("scsj", getScsj()) + .append("xfsj", getXfsj()) + .append("jssj", getJssj()) + .append("mlfs", getMlfs()) + .append("spare1", getSpare1()) + .append("spare2", getSpare2()) + .append("spare3", getSpare3()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueFinishMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueFinishMapper.java new file mode 100644 index 0000000..98acf99 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueFinishMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.queue.ddj.mapper; + +import java.util.List; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueueFinish; + +/** + * 堆垛机完成队列Mapper接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface DdjDeviceExecutionQueueFinishMapper +{ + /** + * 查询堆垛机完成队列 + * + * @param uuid 堆垛机完成队列主键 + * @return 堆垛机完成队列 + */ + public DdjDeviceExecutionQueueFinish selectDdjDeviceExecutionQueueFinishByUuid(String uuid); + + /** + * 查询堆垛机完成队列列表 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 堆垛机完成队列集合 + */ + public List selectDdjDeviceExecutionQueueFinishList(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 新增堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + public int insertDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 修改堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + public int updateDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 删除堆垛机完成队列 + * + * @param uuid 堆垛机完成队列主键 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueFinishByUuid(String uuid); + + /** + * 批量删除堆垛机完成队列 + * + * @param uuids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueFinishByUuids(String[] uuids); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueMapper.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueMapper.java new file mode 100644 index 0000000..533a08f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/mapper/DdjDeviceExecutionQueueMapper.java @@ -0,0 +1,66 @@ +package com.ruoyi.queue.ddj.mapper; + +import java.util.List; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import org.apache.ibatis.annotations.Param; + +/** + * 堆垛机执行队列Mapper接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface DdjDeviceExecutionQueueMapper +{ + /** + * 查询堆垛机执行队列 + * + * @param uuid 堆垛机执行队列主键 + * @return 堆垛机执行队列 + */ + public DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByUuid(String uuid); + + /** + * 查询堆垛机执行队列列表 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 堆垛机执行队列集合 + */ + public List selectDdjDeviceExecutionQueueList(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 新增堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + public int insertDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 修改堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + public int updateDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 删除堆垛机执行队列 + * + * @param uuid 堆垛机执行队列主键 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueByUuid(String uuid); + + /** + * 批量删除堆垛机执行队列 + * + * @param uuids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueByUuids(String[] uuids); + + List selectDdjDeviceExecutionQueueListByRwlx(@Param("rwlx")Integer rwlx,@Param("devId") Integer devId); + + DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByRwhAndXd(@Param("rwh") Long rwh, @Param("devId") Integer devId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueFinishService.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueFinishService.java new file mode 100644 index 0000000..e50a3ad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueFinishService.java @@ -0,0 +1,61 @@ +package com.ruoyi.queue.ddj.service; + +import java.util.List; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueueFinish; + +/** + * 堆垛机完成队列Service接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface IDdjDeviceExecutionQueueFinishService +{ + /** + * 查询堆垛机完成队列 + * + * @param uuid 堆垛机完成队列主键 + * @return 堆垛机完成队列 + */ + public DdjDeviceExecutionQueueFinish selectDdjDeviceExecutionQueueFinishByUuid(String uuid); + + /** + * 查询堆垛机完成队列列表 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 堆垛机完成队列集合 + */ + public List selectDdjDeviceExecutionQueueFinishList(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 新增堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + public int insertDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 修改堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + public int updateDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish); + + /** + * 批量删除堆垛机完成队列 + * + * @param uuids 需要删除的堆垛机完成队列主键集合 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueFinishByUuids(String[] uuids); + + /** + * 删除堆垛机完成队列信息 + * + * @param uuid 堆垛机完成队列主键 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueFinishByUuid(String uuid); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueService.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueService.java new file mode 100644 index 0000000..667b76b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/IDdjDeviceExecutionQueueService.java @@ -0,0 +1,65 @@ +package com.ruoyi.queue.ddj.service; + +import java.util.List; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; + +/** + * 堆垛机执行队列Service接口 + * + * @author ruoyi + * @date 2025-09-22 + */ +public interface IDdjDeviceExecutionQueueService +{ + /** + * 查询堆垛机执行队列 + * + * @param uuid 堆垛机执行队列主键 + * @return 堆垛机执行队列 + */ + public DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByUuid(String uuid); + + /** + * 查询堆垛机执行队列列表 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 堆垛机执行队列集合 + */ + public List selectDdjDeviceExecutionQueueList(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 新增堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + public int insertDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 修改堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + public int updateDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue); + + /** + * 批量删除堆垛机执行队列 + * + * @param uuids 需要删除的堆垛机执行队列主键集合 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueByUuids(String[] uuids); + + /** + * 删除堆垛机执行队列信息 + * + * @param uuid 堆垛机执行队列主键 + * @return 结果 + */ + public int deleteDdjDeviceExecutionQueueByUuid(String uuid); + + DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByRwh(Long rwh,Integer devId); + + List selectDdjDeviceExecutionQueueListByRwlx(Integer rwlx,Integer devId); +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueFinishServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueFinishServiceImpl.java new file mode 100644 index 0000000..7bdc6a5 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueFinishServiceImpl.java @@ -0,0 +1,93 @@ +package com.ruoyi.queue.ddj.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.queue.ddj.mapper.DdjDeviceExecutionQueueFinishMapper; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueueFinish; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueFinishService; + +/** + * 堆垛机完成队列Service业务层处理 + * + * @author ruoyi + * @date 2025-09-22 + */ +@Service +public class DdjDeviceExecutionQueueFinishServiceImpl implements IDdjDeviceExecutionQueueFinishService +{ + @Autowired + private DdjDeviceExecutionQueueFinishMapper ddjDeviceExecutionQueueFinishMapper; + + /** + * 查询堆垛机完成队列 + * + * @param uuid 堆垛机完成队列主键 + * @return 堆垛机完成队列 + */ + @Override + public DdjDeviceExecutionQueueFinish selectDdjDeviceExecutionQueueFinishByUuid(String uuid) + { + return ddjDeviceExecutionQueueFinishMapper.selectDdjDeviceExecutionQueueFinishByUuid(uuid); + } + + /** + * 查询堆垛机完成队列列表 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 堆垛机完成队列 + */ + @Override + public List selectDdjDeviceExecutionQueueFinishList(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + return ddjDeviceExecutionQueueFinishMapper.selectDdjDeviceExecutionQueueFinishList(ddjDeviceExecutionQueueFinish); + } + + /** + * 新增堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + @Override + public int insertDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + return ddjDeviceExecutionQueueFinishMapper.insertDdjDeviceExecutionQueueFinish(ddjDeviceExecutionQueueFinish); + } + + /** + * 修改堆垛机完成队列 + * + * @param ddjDeviceExecutionQueueFinish 堆垛机完成队列 + * @return 结果 + */ + @Override + public int updateDdjDeviceExecutionQueueFinish(DdjDeviceExecutionQueueFinish ddjDeviceExecutionQueueFinish) + { + return ddjDeviceExecutionQueueFinishMapper.updateDdjDeviceExecutionQueueFinish(ddjDeviceExecutionQueueFinish); + } + + /** + * 批量删除堆垛机完成队列 + * + * @param uuids 需要删除的堆垛机完成队列主键 + * @return 结果 + */ + @Override + public int deleteDdjDeviceExecutionQueueFinishByUuids(String[] uuids) + { + return ddjDeviceExecutionQueueFinishMapper.deleteDdjDeviceExecutionQueueFinishByUuids(uuids); + } + + /** + * 删除堆垛机完成队列信息 + * + * @param uuid 堆垛机完成队列主键 + * @return 结果 + */ + @Override + public int deleteDdjDeviceExecutionQueueFinishByUuid(String uuid) + { + return ddjDeviceExecutionQueueFinishMapper.deleteDdjDeviceExecutionQueueFinishByUuid(uuid); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueServiceImpl.java b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueServiceImpl.java new file mode 100644 index 0000000..6ee1597 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/queue/ddj/service/impl/DdjDeviceExecutionQueueServiceImpl.java @@ -0,0 +1,103 @@ +package com.ruoyi.queue.ddj.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.queue.ddj.mapper.DdjDeviceExecutionQueueMapper; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; + +/** + * 堆垛机执行队列Service业务层处理 + * + * @author ruoyi + * @date 2025-09-22 + */ +@Service +public class DdjDeviceExecutionQueueServiceImpl implements IDdjDeviceExecutionQueueService +{ + @Autowired + private DdjDeviceExecutionQueueMapper ddjDeviceExecutionQueueMapper; + + /** + * 查询堆垛机执行队列 + * + * @param uuid 堆垛机执行队列主键 + * @return 堆垛机执行队列 + */ + @Override + public DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByUuid(String uuid) + { + return ddjDeviceExecutionQueueMapper.selectDdjDeviceExecutionQueueByUuid(uuid); + } + + /** + * 查询堆垛机执行队列列表 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 堆垛机执行队列 + */ + @Override + public List selectDdjDeviceExecutionQueueList(DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + return ddjDeviceExecutionQueueMapper.selectDdjDeviceExecutionQueueList(ddjDeviceExecutionQueue); + } + + /** + * 新增堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + @Override + public int insertDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + return ddjDeviceExecutionQueueMapper.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + } + + /** + * 修改堆垛机执行队列 + * + * @param ddjDeviceExecutionQueue 堆垛机执行队列 + * @return 结果 + */ + @Override + public int updateDdjDeviceExecutionQueue(DdjDeviceExecutionQueue ddjDeviceExecutionQueue) + { + return ddjDeviceExecutionQueueMapper.updateDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + } + + /** + * 批量删除堆垛机执行队列 + * + * @param uuids 需要删除的堆垛机执行队列主键 + * @return 结果 + */ + @Override + public int deleteDdjDeviceExecutionQueueByUuids(String[] uuids) + { + return ddjDeviceExecutionQueueMapper.deleteDdjDeviceExecutionQueueByUuids(uuids); + } + + /** + * 删除堆垛机执行队列信息 + * + * @param uuid 堆垛机执行队列主键 + * @return 结果 + */ + @Override + public int deleteDdjDeviceExecutionQueueByUuid(String uuid) + { + return ddjDeviceExecutionQueueMapper.deleteDdjDeviceExecutionQueueByUuid(uuid); + } + + @Override + public DdjDeviceExecutionQueue selectDdjDeviceExecutionQueueByRwh(Long rwh,Integer devId) { + return ddjDeviceExecutionQueueMapper.selectDdjDeviceExecutionQueueByRwhAndXd(rwh,devId); + } + + @Override + public List selectDdjDeviceExecutionQueueListByRwlx(Integer rwlx,Integer devId) { + return ddjDeviceExecutionQueueMapper.selectDdjDeviceExecutionQueueListByRwlx(rwlx,devId); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java new file mode 100644 index 0000000..b0325c8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ScheduleThread.java @@ -0,0 +1,189 @@ +package com.ruoyi.schedule; + +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ddj.service.IMsgDdjService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.service.IDeviceSsjService; +import com.ruoyi.common.domain.PlcConnectInfo; +import com.ruoyi.schedule.plcRead.ddj.PlcDdjRead; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import com.ruoyi.common.domain.CustomThread; +import com.ruoyi.schedule.scheduleTask.ddj.Ddj1Thread; +import com.ruoyi.schedule.scheduleTask.monit.MonitoringThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor1.Floor1BothWayInSsjThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor1.Floor1BothWayOutSsjThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor1.Floor1SingleSsjThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor2.Floor2InOutSsjThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor2.Floor2RoadWaySsjThread; +import com.ruoyi.schedule.scheduleTask.ssj.floor2.LineSsjThread; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Slf4j +public class ScheduleThread { + + public CustomThread plcSsjReadCustomThread; + public CustomThread plcDdjReadCustomThread; + + public CustomThread floor1BothWayInSsjCustomThread; + public CustomThread floor1BothWayOutSsjCustomThread; + public CustomThread floor1SingleSsjCustomThread; + public CustomThread floor2InSsjCustomThread; + public CustomThread floor2OutSsjCustomThread; + public CustomThread lineSsjCustomThread; + public CustomThread monitoringCustomThread; + public CustomThread ddj1CustomThread; + @Autowired + private IDeviceSsjService deviceSsjService; + @Autowired + private IMsgDdjService msgDdjService; + + private boolean isRun = false; + private boolean isRun2 = false; + + /** + * 项目初始化启动 + */ + @PostConstruct + public void init() { + log.info("初始化调度服务"); + //读取信息初始化 + plcReadInit(); + //调度初始化 + scheduleInit(); + log.info("调度服务执行成功"); + } + + /** + * 调度初始化 + */ + private void scheduleInit() { + MonitoringThread monitoringThread = new MonitoringThread(); + monitoringCustomThread = new CustomThread(new Thread(monitoringThread), monitoringThread); + + Floor1BothWayInSsjThread floor1BothWayInSsjThread = new Floor1BothWayInSsjThread(); + floor1BothWayInSsjCustomThread = new CustomThread(new Thread(floor1BothWayInSsjThread), floor1BothWayInSsjThread); + + Floor1BothWayOutSsjThread floor1BothWayOutSsjThread = new Floor1BothWayOutSsjThread(); + floor1BothWayOutSsjCustomThread = new CustomThread(new Thread(floor1BothWayOutSsjThread), floor1BothWayOutSsjThread); + + Floor1SingleSsjThread floor1SingleSsjThread = new Floor1SingleSsjThread(); + floor1SingleSsjCustomThread = new CustomThread(new Thread(floor1SingleSsjThread), floor1SingleSsjThread); + + Floor2InOutSsjThread floor2InSsjThread = new Floor2InOutSsjThread(); + floor2InSsjCustomThread = new CustomThread(new Thread(floor2InSsjThread), floor2InSsjThread); + + LineSsjThread lineSsjThread = new LineSsjThread(); + lineSsjCustomThread = new CustomThread(new Thread(lineSsjThread), lineSsjThread); + + Floor2RoadWaySsjThread floor2OutSsjThread = new Floor2RoadWaySsjThread(); + floor2OutSsjCustomThread = new CustomThread(new Thread(floor2OutSsjThread), floor2OutSsjThread); + + Ddj1Thread ddj1Thread = new Ddj1Thread(); + ddj1CustomThread = new CustomThread(new Thread(ddj1Thread), ddj1Thread); + } + + /** + * plc读取初始化 + */ + private void plcReadInit() { + ConcurrentHashMap ssjList = new ConcurrentHashMap<>(); + //获取要去取的输送机 + List deviceSsjs = deviceSsjService.selectDeviceSsjList(new DeviceSsj()); + for (DeviceSsj deviceSsj : deviceSsjs) { + ssjList.put(deviceSsj.getDeviceCode(), deviceSsj); + } + + + //plc输送机读取线程 + PlcSsjRead plcSsjRead = new PlcSsjRead(ssjList, new PlcConnectInfo("127.0.0.1", 102)); + Thread plcSsjReadThread = new Thread(plcSsjRead); + plcSsjReadCustomThread = new CustomThread(plcSsjReadThread, plcSsjRead); + + //plc堆垛机读取现场 + ConcurrentHashMap ddjList = new ConcurrentHashMap<>(); + List msgDdjs = msgDdjService.selectMsgDdjList(new MsgDdj()); + for (MsgDdj msgDdj : msgDdjs) { + switch (msgDdj.getId()){ + case 1: + msgDdj.setPlcDbReadAddress("DB41.0"); + break; + case 2: + msgDdj.setPlcDbReadAddress("DB42.0"); + break; + } + ddjList.put("ddj" + msgDdj.getId(), msgDdj); + } + PlcDdjRead plcDdjRead = new PlcDdjRead(ddjList, new PlcConnectInfo("127.0.0.1", 102)); + Thread plcDdjReadThread = new Thread(plcDdjRead); + plcDdjReadCustomThread = new CustomThread(plcDdjReadThread, plcDdjRead); + + } + + /** + * 开始线程 + */ + public void startAllThread() { + plcSsjReadCustomThread.getThread().start(); + plcDdjReadCustomThread.getThread().start(); + isRun = true; + } + + /** + * 开启线程2 + */ + public void startAllThread2() { + monitoringCustomThread.thread.start(); + floor1BothWayInSsjCustomThread.thread.start(); + floor1BothWayOutSsjCustomThread.thread.start(); + floor1SingleSsjCustomThread.thread.start(); + floor2InSsjCustomThread.thread.start(); + lineSsjCustomThread.thread.start(); + floor2OutSsjCustomThread.thread.start(); + ddj1CustomThread.thread.start(); + isRun2 = true; + } + + /** + * 停止线程 + */ + public void stopAllThread() { + plcSsjReadCustomThread.runnable.isRun = false; + plcDdjReadCustomThread.runnable.isRun = false; + plcReadInit(); + isRun = false; + } + + /** + * 停止线程2 + */ + public void stopAllThread2() { + monitoringCustomThread.runnable.isRun = false; + floor1BothWayInSsjCustomThread.runnable.isRun = false; + floor1BothWayOutSsjCustomThread.runnable.isRun = false; + floor1SingleSsjCustomThread.runnable.isRun = false; + floor2InSsjCustomThread.runnable.isRun = false; + lineSsjCustomThread.runnable.isRun = false; + floor2OutSsjCustomThread.runnable.isRun = false; + ddj1CustomThread.runnable.isRun = false; + scheduleInit(); + isRun2 = false; + } + + /** + * 获取调度状态 + */ + public HashMap getScheduleStatus() { + HashMap map = new HashMap<>(); + map.put("isRun",isRun); + map.put("isRun2",isRun2); + return map; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/ThreadOperationController.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ThreadOperationController.java new file mode 100644 index 0000000..dc04fb4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/ThreadOperationController.java @@ -0,0 +1,72 @@ +package com.ruoyi.schedule; + +import com.ruoyi.common.core.domain.AjaxResult; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +@RestController +@RequestMapping("/device/operation") +public class ThreadOperationController { + + @Resource + private ScheduleThread scheduleThread; + + /** + * 开启plc读取线程 + * + * @return + */ + @RequestMapping("/startAllThread") + //@PreAuthorize("@ss.hasPermi('device:operation:start')") + public AjaxResult startAllThread() { + scheduleThread.startAllThread(); + return AjaxResult.success(); + } + + /** + * 开启调度线程 + * + * @return + */ + @RequestMapping("/startAllThread2") + //@PreAuthorize("@ss.hasPermi('device:operation:start')") + public AjaxResult startAllThread2() { + scheduleThread.startAllThread2(); + return AjaxResult.success(); + } + + /** + * 关闭线程 + * + * @return + */ + @RequestMapping("/stopAllThread") + //@PreAuthorize("@ss.hasPermi('device:operation:stop')") + public AjaxResult stopAllThread() { + scheduleThread.stopAllThread(); + return AjaxResult.success(); + } + + /** + * 关闭线程 + * + * @return + */ + @RequestMapping("/stopAllThread2") + //@PreAuthorize("@ss.hasPermi('device:operation:stop')") + public AjaxResult stopAllThread2() { + scheduleThread.stopAllThread2(); + return AjaxResult.success(); + } + + /** + * 获取调度状态 + * @return + */ + @RequestMapping("/getScheduleStatus") + public AjaxResult getScheduleStatus() { + return AjaxResult.success(scheduleThread.getScheduleStatus()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ddj/PlcDdjRead.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ddj/PlcDdjRead.java new file mode 100644 index 0000000..ad91de3 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ddj/PlcDdjRead.java @@ -0,0 +1,143 @@ +package com.ruoyi.schedule.plcRead.ddj; + +import HslCommunication.BasicFramework.SoftBasic; +import HslCommunication.Core.Net.IReadWriteNet; +import HslCommunication.Core.Types.OperateResult; +import HslCommunication.Core.Types.OperateResultExOne; +import HslCommunication.Profinet.Siemens.SiemensPLCS; +import HslCommunication.Profinet.Siemens.SiemensS7Net; +import cn.hutool.core.convert.Convert; +import com.ruoyi.common.domain.HslRead; +import com.ruoyi.common.domain.PlcConnectInfo; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.schedule.plcRead.util.ParseUtil; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +public class PlcDdjRead extends Thread { + public static ConcurrentHashMap deviceDdjMap; + public volatile boolean isRun = true; + + public SiemensS7Net siemensS7Net = null; + public static IReadWriteNet readWriteNet = null; + private final String ip; + private final int port; + private boolean success = false; + + public PlcDdjRead(ConcurrentHashMap deviceDdjMap, PlcConnectInfo plcConnectInfo) { + PlcDdjRead.deviceDdjMap = deviceDdjMap; + this.ip = plcConnectInfo.getIp(); + this.port = plcConnectInfo.getPort(); + } + + public static int WriteToSSJ(String address, byte[] data) { + int a = 0; + if (readWriteNet != null) { + a = HslRead.WriteResultRender(readWriteNet.Write(address, data), address); + } + return a; + } + + @Override + public void run() { + while (isRun) { + try { + if (IsConnect()) { + for (String key : deviceDdjMap.keySet()) { + MsgDdj msgDdj = deviceDdjMap.get(key); + if (msgDdj != null) { + readPlcDdj(msgDdj); + } + } + Thread.sleep(1000); + } else { + reConnect(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } catch (Exception e) { + log.info("********IP:" + this.ip + " port:" + this.port + "��ȡ�����쳣 " + e.getMessage()); + } + + } + } + + private void readPlcDdj(MsgDdj msgDdj) { + OperateResultExOne read2 = readWriteNet.Read(msgDdj.getPlcDbReadAddress(), Convert.toShort("38")); + String ddsx = "0"; + if (read2.IsSuccess) { + ddsx = SoftBasic.ByteToHexString(read2.Content); + msgDdj.setCzfs(ParseUtil.optInt(0, ddsx)); + msgDdj.setSbzt(ParseUtil.optInt(2, ddsx)); + msgDdj.setRwh(Convert.toLong(ParseUtil.optDInt(4, ddsx))); + msgDdj.setHczt(ParseUtil.optInt(8, ddsx)); + msgDdj.setJdbz(ParseUtil.optInt(10, ddsx)); + msgDdj.setYph(ParseUtil.optInt(12, ddsx)); + msgDdj.setYlh(ParseUtil.optInt(14, ddsx)); + msgDdj.setYch(ParseUtil.optInt(16, ddsx)); + msgDdj.setMbph(ParseUtil.optInt(18, ddsx)); + msgDdj.setMblh(ParseUtil.optInt(20, ddsx)); + msgDdj.setMbch(ParseUtil.optInt(22, ddsx)); + msgDdj.setYxxf(ParseUtil.optInt(24, ddsx)); + msgDdj.setXtxh(ParseUtil.optInt(26, ddsx)); + } + } + + + void ConnectServer() { + siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500); + try { + siemensS7Net.setIpAddress(this.ip); + siemensS7Net.setPort(this.port); + siemensS7Net.setRack((byte) 0); + siemensS7Net.setSlot((byte) 1); + + OperateResult connect = siemensS7Net.ConnectServer(); + if (connect.IsSuccess) { + setIsConnect(true); + readWriteNet = siemensS7Net; + } + } catch (Exception ex) { + log.info("********IP:" + this.ip + " port:" + this.port + "连接失败 " + ex.getMessage()); + } + + } + + public void setIsConnect(boolean bflag) { + if (!success) { + success = bflag; + } + } + + public boolean IsConnect() { + if (success) { + return success; + } + return false; + } + + void reConnect() { + closeServer(); + ConnectServer(); + log.info("重新连接 SSJ-PLC"); + } + + void closeServer() { + if (readWriteNet != null) { + readWriteNet.ConnectClose(); + } + setIsConnect(false); + } + + public static int WriteToDDJ(String address, byte[] data) { + int a = 0; + if (readWriteNet != null) { + a = HslRead.WriteResultRender(readWriteNet.Write(address, data), address); + } + return a; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ssj/PlcSsjRead.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ssj/PlcSsjRead.java new file mode 100644 index 0000000..488a062 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/ssj/PlcSsjRead.java @@ -0,0 +1,236 @@ +package com.ruoyi.schedule.plcRead.ssj; + +import HslCommunication.BasicFramework.SoftBasic; +import HslCommunication.Core.Net.IReadWriteNet; +import HslCommunication.Core.Types.OperateResult; +import HslCommunication.Core.Types.OperateResultExOne; +import HslCommunication.Profinet.Siemens.SiemensPLCS; +import HslCommunication.Profinet.Siemens.SiemensS7Net; +import com.ruoyi.common.domain.SendData; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.common.domain.HslRead; +import com.ruoyi.common.domain.PlcConnectInfo; +import com.ruoyi.schedule.plcRead.util.ParseUtil; +import com.ruoyi.schedule.plcRead.util.Tools; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; + +@Slf4j +public class PlcSsjRead implements Runnable { + + public static ConcurrentHashMap deviceSsjMap; + public volatile boolean isRun = true; + + public SiemensS7Net siemensS7Net = null; + public static IReadWriteNet readWriteNet = null; + private final String ip; + private final int port; + private boolean success = false; + + public PlcSsjRead(ConcurrentHashMap deviceSsjMap, PlcConnectInfo plcConnectInfo) { + PlcSsjRead.deviceSsjMap = deviceSsjMap; + this.ip = plcConnectInfo.getIp(); + this.port = plcConnectInfo.getPort(); + } + + public static int WriteToSSJ(String address, byte[] data) { + int a = 0; + if (readWriteNet != null) { + a = HslRead.WriteResultRender(readWriteNet.Write(address, data), address); + } + return a; + } + + @Override + public void run() { + while (isRun) { + try { + if (IsConnect()) { + int step = 20; + int baseOffset = 0; + for (String ssjCode : deviceSsjMap.keySet()) { + DeviceSsj ssjDevice = deviceSsjMap.get(ssjCode); + switch (ssjDevice.getType()) { + case "1": + readPlcLineSsj(ssjDevice); + break; + case "2": + readPlcInOutSsj(ssjDevice); + break; + case "3": + readPlcOtherSsj(ssjDevice); + break; + case "4": + readPlcRoadWaySsj(ssjDevice); + break; + } + } + Thread.sleep(1000); + } else { + reConnect(); + } + } catch (InterruptedException e) { + // 捕获中断异常,退出循环 + Thread.currentThread().interrupt(); // 重新设置中断标志 + return; + } catch (Exception e) { + log.info("********IP:" + this.ip + " port:" + this.port + "读取数据异常 " + e.getMessage()); + } + + } + } + + /** + * 其他输送机 + * + * @param ssjDevice + */ + private void readPlcOtherSsj(DeviceSsj ssjDevice) { + OperateResultExOne read2 = readWriteNet.Read(ssjDevice.getPlcDbReadAddress(), Short.parseShort("20")); + String ddsx1 = "0"; + if (read2.IsSuccess) { + ddsx1 = SoftBasic.ByteToHexString(read2.Content); + ssjDevice.setKx(ParseUtil.optInt(0, ddsx1)); + ssjDevice.setDw(ParseUtil.optInt(2, ddsx1)); + ssjDevice.setTaskNo(ParseUtil.optDInt(6, ddsx1)); + } + } + + /** + * 巷道输送机 + * + * @param ssjDevice + */ + private void readPlcRoadWaySsj(DeviceSsj ssjDevice) { + OperateResultExOne read2 = readWriteNet.Read(ssjDevice.getPlcDbReadAddress(), Short.parseShort("20")); + String ddsx1 = "0"; + if (read2.IsSuccess) { + ddsx1 = SoftBasic.ByteToHexString(read2.Content); + ssjDevice.setKx(ParseUtil.optInt(0, ddsx1)); + ssjDevice.setDw(ParseUtil.optInt(2, ddsx1)); + ssjDevice.setRkqq(ParseUtil.optInt(4, ddsx1)); + ssjDevice.setTaskNo(ParseUtil.optDInt(6, ddsx1)); + } + } + + /** + * 入出库输送机 + * + * @param ssjDevice + */ + private void readPlcInOutSsj(DeviceSsj ssjDevice) { + OperateResultExOne read2 = readWriteNet.Read(ssjDevice.getPlcDbReadAddress(), Short.parseShort("20")); + String ddsx1 = "0"; + if (read2.IsSuccess) { + ddsx1 = SoftBasic.ByteToHexString(read2.Content); + ssjDevice.setKx(ParseUtil.optInt(0, ddsx1)); + ssjDevice.setDw(ParseUtil.optInt(2, ddsx1)); + ssjDevice.setException(ParseUtil.optInt(4, ddsx1)); + ssjDevice.setAllowPut(ParseUtil.optInt(6, ddsx1)); + ssjDevice.setTaskNo(ParseUtil.optDInt(8, ddsx1)); + + } + } + + /** + * 产线输送机 + * + * @param ssjDevice + */ + private void readPlcLineSsj(DeviceSsj ssjDevice) { + OperateResultExOne read2 = readWriteNet.Read(ssjDevice.getPlcDbReadAddress(), Short.parseShort("220")); + String ddsx1 = "0"; + if (read2.IsSuccess) { + ddsx1 = SoftBasic.ByteToHexString(read2.Content); + ssjDevice.setKx(ParseUtil.optInt(0, ddsx1)); + ssjDevice.setDw(ParseUtil.optInt(2, ddsx1)); + ssjDevice.setRkqq(ParseUtil.optInt(4, ddsx1)); + ssjDevice.setTraySize(ParseUtil.optInt(6, ddsx1)); + ssjDevice.setTaskNo(ParseUtil.optDInt(8, ddsx1)); + + String trayCodeReadAddress=""; + switch (ssjDevice.getDeviceCode()){ + case "G1005": + trayCodeReadAddress = "DB502.0"; + break; + case "L2012": + trayCodeReadAddress = "DB503.0"; + break; + case "L2013": + trayCodeReadAddress = "DB504.0"; + break; + } + OperateResultExOne trayCodeRead = readWriteNet.Read(trayCodeReadAddress, Short.parseShort("220")); + if (trayCodeRead.IsSuccess){ + byte[] content = trayCodeRead.Content; + //去除bytes中 + ssjDevice.setTrayCode(ParseUtil.filterAndConvert(trayCodeRead.Content)); + } + } + } + + + + void ConnectServer() { + siemensS7Net = new SiemensS7Net(SiemensPLCS.S1500); + try { + siemensS7Net.setIpAddress(this.ip); + siemensS7Net.setPort(this.port); + siemensS7Net.setRack((byte) 0); + siemensS7Net.setSlot((byte) 1); + + OperateResult connect = siemensS7Net.ConnectServer(); + if (connect.IsSuccess) { + setIsConnect(true); + readWriteNet = siemensS7Net; + } + } catch (Exception ex) { + log.info("********IP:" + this.ip + " port:" + this.port + "连接失败 " + ex.getMessage()); + } + + } + + public void setIsConnect(boolean bflag) { + if (!success) { + success = bflag; + } + } + + public boolean IsConnect() { + if (success) { + return success; + } + return false; + } + + void reConnect() { + closeServer(); + ConnectServer(); + log.info("重新连接 SSJ-PLC"); + } + + void closeServer() { + if (readWriteNet != null) { + readWriteNet.ConnectClose(); + } + setIsConnect(false); + } + + public static int writeToSsj(String address, String taskNo, int qx , int completed) { + + int jym = Tools.getJYM(Integer.parseInt(taskNo), qx, completed); + SendData taskNoSendData = new SendData(Tools.INT, taskNo); + SendData qxSendData = new SendData(Tools.INT, qx); + SendData completedSendData = new SendData(Tools.INT, completed); + SendData jymSendData = new SendData(Tools.INT, jym); + byte[] bytes = Tools.sendData(taskNoSendData, qxSendData, completedSendData, jymSendData); + return PlcSsjRead.WriteToSSJ(address, bytes); + } + + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/ParseUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/ParseUtil.java new file mode 100644 index 0000000..afc93d2 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/ParseUtil.java @@ -0,0 +1,80 @@ +package com.ruoyi.schedule.plcRead.util; + + +import HslCommunication.BasicFramework.SoftBasic; + +/** + * @author zn kai + * @create 2021-06-28 14:04 + */ +public class ParseUtil { + + + + /** + * short 16λתint + * @param start + * @param data + * @return + */ + public static int optInt(int start, String data) { + return Integer.parseInt(data.substring(start * 2, (start + 2) * 2), 16); + } + /** + * int 16λתint + * @param start + * @param data + * @return + */ + public static int optDInt(int start, String data) { + return Integer.parseInt(data.substring(start * 2, (start + 4) * 2), 16); + } + /** + * long 16λתlong + * @param start + * @param data + * @return + */ + public static Long optLong(int start, String data) { + return Long.parseLong(data.substring(start * 2, (start + 8) * 2), 16); + } + /** + * string 16λתstring + * @param start + * @param data + * @return + */ + + public static String optString(int start, String data) { + byte[] bytes = SoftBasic.HexStringToBytes(data.substring(start * 2, (start + 7) * 2)); + return Tools.bytetostringtool(bytes); + } + + + /** + * 过滤ASCII数组中的无效字符,只保留可打印字符(32-126) + * @param asciiArray 原始ASCII码数组 + * @return 转换后的字符串 + */ + public static String filterAndConvert(byte[] asciiArray) { + if (asciiArray == null) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + + for (int code : asciiArray) { + // 只保留可打印ASCII字符(32-空格到126-~) + if (code >= 32 && code <= 126) { + sb.append((char) code); + } + } + + return sb.toString(); + } + + + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/Tools.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/Tools.java new file mode 100644 index 0000000..060590b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/plcRead/util/Tools.java @@ -0,0 +1,403 @@ +package com.ruoyi.schedule.plcRead.util; + +import HslCommunication.BasicFramework.SoftBasic; +import com.ruoyi.common.domain.SendData; + +import java.io.Serializable; +import java.nio.charset.StandardCharsets; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; + +/** + * 描述:cyc16校验码 + * + * @author zhu + */ +public class Tools implements Serializable { + public static int Debug = 0x20; + public final static int DEBUG_CONVERSIONS = 0x1000; + public static final int DINT = 4; + + public static final int INT = 2; + + public static void main(String[] arg) { + int jym = getJYM(1, 2, 3); + System.out.println(jym); + } + + /*4、CRC-16校验码计算方法: + 常用查表法和计算法。计算方法一般都是: + (1)、预置1个16位的寄存器为十六进制FFFF(即全为1),称此寄存器为CRC寄存器; + (2)、把第一个8位二进制数据(既通讯信息帧的第一个字节)与16位的CRC寄存器的低 + 8位相异或,把结果放于CRC寄存器,高八位数据不变; + (3)、把CRC寄存器的内容右移一位(朝低位)用0填补最高位,并检查右移后的移出位; + (4)、如果移出位为0:重复第3步(再次右移一位);如果移出位为1,CRC寄存器与多 + 项式A001(1010 0000 0000 0001)进行异或; + (5)、重复步骤3和4,直到右移8次,这样整个8位数据全部进行了处理; + (6)、重复步骤2到步骤5,进行通讯信息帧下一个字节的处理; + (7)、将该通讯信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低 + 字节进行交换; + (8)、最后得到的CRC寄存器内容即为:CRC码。 + 以上计算步骤中的多项式A001是8005按位颠倒后的结果。 + 查表法是将移位异或的计算结果做成了一个表,就是将0~256放入一个长度为16位的寄存器中的低八位,高八位填充0,然后将该寄存器与多项式0XA001按照上述3、4步骤,直到八位全部移出,最后寄存器中的值就是表格中的数据,高八位、低八位分别单独一个表。*/ + + + /** + * 全部校验码可用这个获取 + * + * @param ints 传入的校验码 + * @return 返回的int + */ + public static int getJYM(int... ints) { + //将整形任务号转为32位,能后转为四个字节,最后转为整形组4 + int[] ints1 = new int[ints.length * 4]; + for (int i = 0; i < ints.length; i++) { + int[] son; + son = binaryTints(printBits(ints[i])); + for (int j = 0; j < 4; j++) { + int s = j + i * 4; + ints1[s] = son[j]; + System.out.println(Arrays.toString(ints1)); + } + } + int a = 0; + for (int j : ints1) { + a = a + j; + } + return a; + } + + /** + * @param hex 字符串 + * @return a 整型 + */ + public static int[] binaryTints(String hex) { + int[] a = new int[4]; + String hexString = change32(hex); + a[0] = Integer.parseInt(hexString.substring(0, 8), 2);//将 + a[1] = Integer.parseInt(hexString.substring(8, 16), 2); + a[2] = Integer.parseInt(hexString.substring(16, 24), 2); + a[3] = Integer.parseInt(hexString.substring(24, 32), 2); + return a; + } + + public static int[] binaryTints16(String hex) { + int[] a = new int[2]; + String hexString = change16(hex); + a[0] = Integer.parseInt(hexString.substring(0, 8), 2);//将二进制字符串转为整形 + a[1] = Integer.parseInt(hexString.substring(8, 16), 2); + return a; + } + + public static int binaryTints8(String hex) { + int a; + String hexString = change8(hex); + a = Integer.parseInt(hexString.substring(0, 8), 2);//将二进制字符串转为整形 + return a; + } + + public static int BytesToInt(int[] ss) { + if (ss != null) { + StringBuilder lString = new StringBuilder(); + for (int s : ss) { + lString.append(change8(printBits(s))); + } + return Integer.parseInt(lString.toString(), 2); + } else { + return 0; + } + } + + /** + * 将字符转为整形 + **/ + public static int showAscii(char x) { + return x; + } + + /** + * 将整形转为字符 + **/ + public static char showChar(int x) { + return (char) x; + } + + /** + * 托盘条码解析 + **/ + public static String byteToStringTool(byte[] aaa) { + StringBuilder builder = new StringBuilder(); + for (byte b : aaa) { + String x = Character.toString(showChar(b)); + builder.append(x); + } + return builder.toString(); + } + + /** + * 将整形转为二进制字符串 + */ + public static String printBits(int n) { + return Integer.toBinaryString(n); + } + + /** + * 将整形转为浮点型 + */ + public static Float printFloat(int n) { + return Float.intBitsToFloat(n); + } + + /** + * 不满8位加高位加0 + **/ + public static String change8(String value) { + if (value != null && !value.equals("")) { + StringBuilder ss = new StringBuilder(value); + for (int i = 0; i < 8 - value.length(); i++) { + ss.insert(0, "0"); + } + return ss.toString(); + } else { + return "00000000"; + } + } + + /** + * 不满32位加高位加0 4个字节 + **/ + public static String change32(String value) { + if (value != null && !value.equals("")) { + StringBuilder ss = new StringBuilder(value); + for (int i = 0; i < 32 - value.length(); i++) { + ss.insert(0, "0"); + } + return ss.toString(); + } else { + return "00000000000000000000000000000000"; + } + } + + /** + * 不满16位加高位加0 4个字节 + **/ + public static String change16(String value) { + if (value != null && !value.equals("")) { + StringBuilder ss = new StringBuilder(value); + for (int i = 0; i < 16 - value.length(); i++) { + ss.insert(0, "0"); + } + return ss.toString(); + } else { + return "0000000000000000"; + } + } + + + ///字节转整型 + public static int get(byte a1, byte a2, byte a3, byte a4, byte a5, byte a6, byte a7, byte a8, + byte a9, byte a10, byte a11, byte a12, byte a13, byte a14, byte a15, byte a16, + byte a17, byte a18, byte a19, byte a20, byte a21, byte a22, byte a23, byte a24, byte a25) { + int[] ints = new int[25]; + ints[0] = binaryTints8(printBits(a1)); + ints[1] = binaryTints8(printBits(a2)); + ints[2] = binaryTints8(printBits(a3)); + ints[3] = binaryTints8(printBits(a4)); + ints[4] = binaryTints8(printBits(a5)); + ints[5] = binaryTints8(printBits(a6)); + ints[6] = binaryTints8(printBits(a7)); + ints[7] = binaryTints8(printBits(a8)); + ints[8] = binaryTints8(printBits(a9)); + ints[9] = binaryTints8(printBits(a10)); + ints[10] = binaryTints8(printBits(a11)); + ints[11] = binaryTints8(printBits(a12)); + ints[12] = binaryTints8(printBits(a13)); + ints[13] = binaryTints8(printBits(a14)); + ints[14] = binaryTints8(printBits(a15)); + ints[15] = binaryTints8(printBits(a16)); + ints[16] = binaryTints8(printBits(a17)); + ints[17] = binaryTints8(printBits(a18)); + ints[18] = binaryTints8(printBits(a19)); + ints[19] = binaryTints8(printBits(a20)); + ints[20] = binaryTints8(printBits(a21)); + ints[21] = binaryTints8(printBits(a22)); + ints[22] = binaryTints8(printBits(a23)); + ints[23] = binaryTints8(printBits(a24)); + ints[24] = binaryTints8(printBits(a25)); + for (int anInt : ints) { + System.out.print(anInt + " "); + } + System.out.println(); + return BytesToInt(ints); + } + + + + // + public static byte[] int2byte(int res) {///Java中10进制转byte[] + byte[] targets = new byte[4]; + + targets[3] = (byte) (res & 0xff);// 最低位 + targets[2] = (byte) ((res >> 8) & 0xff);// 次低位 + targets[1] = (byte) ((res >> 16) & 0xff);// 次高位 + targets[0] = (byte) (res >>> 24);// 最高位,无符号右移。 + return targets; + } + + /** + * 多个byte 合并成一个byte + */ + public static byte[] byteMerge(byte[]... values) { + int length_byte = 0; + for (byte[] value : values) { + length_byte += value.length; + } + byte[] all_byte = new byte[length_byte]; + int countLength = 0; + for (byte[] b : values) { + System.arraycopy(b, 0, all_byte, countLength, b.length); + countLength += b.length; + } + return all_byte; + } + + /** ???????????**/ + public static String bytetostringtool(byte[] aaa) { + String tptm = ""; + for(int i=0;i> 8 * i) & 0xff); + } + return b; + } + + + + //下发字节数组 + public static byte[] sendDataToDevice(long taskNo,String trayCode, int...ints){ + int length = 0; + byte[] xfData = null; + if (trayCode == null){ + length = 4 + (ints.length * 2); + xfData = new byte[length]; + byte[] RWH = new byte[4]; + RWH = Tools.int2byte((int) taskNo); + for (int i = 0; i < length; i++){ + if (i < 4){ + xfData[i] = RWH[i]; + } else { + if (i % 2 == 0){ + byte[] param = new byte[2]; + param = intbyte(ints[(i - 4) / 2]); + xfData[i] = param[0]; + xfData[i + 1] = param[1]; + } + } + } + } else { + length = 12 + (ints.length * 2); + xfData = new byte[length]; + byte[] RWH = new byte[4]; + RWH = Tools.int2byte((int) taskNo); + byte[] tCode = new byte[8]; + if ("".equals(trayCode)){ + for (int i = 0;i < tCode.length;i++){ + tCode[i] = 0; + } + }else { + byte[] code = trayCode.getBytes(); + for (int i = 0; i < tCode.length; i++) { + tCode[i] = code[i]; + } + } + for (int i = 0; i< length; i++){ + if (i < 4){ + xfData[i] = RWH[i]; + } else if (i > 3 && i< 12) { + xfData[i] = tCode[i - 4]; + } else { + if (i % 2 == 0){ + byte[] param = new byte[2]; + param = intbyte(ints[(i - 12) / 2]); + xfData[i] = param[0]; + xfData[i + 1] = param[1]; + } + } + } + } + return xfData; + } + + /** + * 字节数组转字符船 + */ + public static String byteToString(String substring) { + return new String(SoftBasic.HexStringToBytes(substring), StandardCharsets.UTF_8); + } + + + public static String getDate() { + Date t = new Date(); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return df.format(t); + } + + /** + * 下发到PLC的反馈字节 + */ + public static byte[] sendData(SendData... list) { + byte[][] values = new byte[list.length][0]; + for (int i = 0; i < list.length; i++) { + byte[] bytes = Tools.intToBytes(list[i].getNum2(), list[i].getNum()); + //System.out.println(bytes.length); + values[i] = bytes; + } + int length_byte = 0; + for (byte[] value : values) { + length_byte += value.length; + } + byte[] all_byte = new byte[length_byte]; + int countLength = 0; + for (byte[] b : values) { + System.arraycopy(b, 0, all_byte, countLength, b.length); + countLength += b.length; + } + return all_byte; + } + + //int 转2字节 + public static byte[] intbyte(int res) {///Java中10进制转byte[] + byte[] targets = new byte[2]; + targets[1] = (byte) ((res >> 0) & 0xff);// 次高位 + targets[0] = (byte) (res >>> 8);// 最高位,无符号右移。 + return targets; + } + + public static int[] binarysToints(String hex){ + int[] a = new int[4]; + String hexString = change32(hex); + a[0] = Integer.parseInt(hexString.substring(0,8),2);//将二进制字符串转为整形 + a[1] = Integer.parseInt(hexString.substring(8,16),2); + a[2] = Integer.parseInt(hexString.substring(16,24),2); + a[3] = Integer.parseInt(hexString.substring(24,32),2); + return a; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/agv/AgvDispatch.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/agv/AgvDispatch.java new file mode 100644 index 0000000..39735a5 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/agv/AgvDispatch.java @@ -0,0 +1,150 @@ +package com.ruoyi.schedule.scheduleTask.agv; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.HttpUtil; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class AgvDispatch { + + // 基础URL + private final String baseUrl; + + // 客户端编号 + private String clientCode; + + // 令牌号 + private String tokenCode; + + public AgvDispatch(String ip, int port) { + this.baseUrl = String.format("http://%s:%d/rcms/services/rest", ip, port); + } + + public AgvDispatch(String baseUrl) { + this.baseUrl = baseUrl; + } + + /** + * 生成唯一请求编号 + */ + private String generateReqCode() { + return IdUtil.fastSimpleUUID(); + } + + // ------------------------------ 常用接口 ------------------------------ + + /** + * 2.1.1 生成任务单 + * @param taskTyp 任务类型 + * @param wbCode 工作位 + * @param positionCodePath 位置路径 + * @param podCode 货架编号 + * @return 接口返回结果 + */ + public String genAgvSchedulingTask(String taskTyp, String wbCode, + Object[] positionCodePath, String podCode) throws IOException { + String url = baseUrl + "/hikRpcService/genAgvSchedulingTask"; + Map params = new HashMap<>(); + params.put("taskTyp", taskTyp); + params.put("wbCode", wbCode); + params.put("positionCodePath", positionCodePath); + params.put("podCode", podCode); + return HttpUtil.post(url, params); + } + + /** + * 2.1.2 继续执行任务 + * @param taskCode 任务单号 + * @return 接口返回结果 + */ + public String continueTask(String taskCode) throws IOException { + String url = baseUrl + "/hikRpcService/continueTask"; + Map params = new HashMap<>(); + params.put("taskCode", taskCode); + return HttpUtil.post(url, params); + } + + /** + * 2.1.3 取消任务 + * @param taskCode 任务单号 + * @param forceCancel 取消类型(0-直接放地上,1-软取消) + * @return 接口返回结果 + */ + public String cancelTask(String taskCode, String forceCancel) throws IOException { + String url = baseUrl + "/hikRpcService/cancelTask"; + Map params = new HashMap<>(); + params.put("taskCode", taskCode); + params.put("forceCancel", forceCancel); + return HttpUtil.post(url, params); + } + + // ------------------------------ 可选接口 ------------------------------ + + /** + * 3.1.1 任务优先级设置 + * @param priorities 优先级列表 + * @return 接口返回结果 + */ + public String setTaskPriority(Object[] priorities) throws IOException { + String url = baseUrl + "/hikRpcService/setTaskPriority"; + Map params = new HashMap<>(); + params.put("priorities", priorities); + return HttpUtil.post(url, params); + } + + /** + * 3.1.2 货架与位置绑定、解绑 + * @param podCode 货架编号 + * @param positionCode 位置编号 + * @param indBind 1-绑定,0-解绑 + * @return 接口返回结果 + */ + public String bindPodAndBerth(String podCode, String positionCode, String indBind) throws IOException { + String url = baseUrl + "/hikRpcService/bindPodAndBerth"; + Map params = new HashMap<>(); + params.put("podCode", podCode); + params.put("positionCode", positionCode); + params.put("indBind", indBind); + return HttpUtil.post(url, params); + } + + /** + * 3.1.9 查询任务状态 + * @param taskCodes 任务单编号数组 + * @return 接口返回结果 + */ + public String queryTaskStatus(String[] taskCodes) throws IOException { + String url = baseUrl + "/hikRpcService/queryTaskStatus"; + Map params = new HashMap<>(); + params.put("taskCodes", taskCodes); + return HttpUtil.post(url, params); + } + + /** + * 3.1.10 查询AGV状态 + * @param mapCode 地图编号 + * @return 接口返回结果 + */ + public String queryAgvStatus(String mapCode) throws IOException { + String url = "http://" + baseUrl.split("//")[1].split("/")[0] + "/rcms-dps/rest/queryAgvStatus"; + Map params = new HashMap<>(); + params.put("mapCode", mapCode); + return HttpUtil.post(url, params); + } + + /** + * 3.1.16 料箱出库TPS(CTU+分拨墙) + * @param taskTyp 任务类型(传1) + * @param data 任务数据列表 + * @return 接口返回结果 + */ + public String getOutPod(String taskTyp, Object[] data) throws IOException { + String url = baseUrl + "/hikTpsService/getOutPod"; + Map params = new HashMap<>(); + params.put("taskTyp", taskTyp); + params.put("data", data); + return HttpUtil.post(url, params); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ddj/Ddj1Thread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ddj/Ddj1Thread.java new file mode 100644 index 0000000..53be582 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ddj/Ddj1Thread.java @@ -0,0 +1,257 @@ +package com.ruoyi.schedule.scheduleTask.ddj; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjUtil; +import com.ruoyi.api.domain.wcs2wms.JobParam; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ddj.domain.MsgDdj; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueFinishService; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.schedule.plcRead.ddj.PlcDdjRead; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import com.ruoyi.schedule.plcRead.util.Tools; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +@Slf4j +public class Ddj1Thread implements Runnable { + + public HashMap> ddjCodeList; + + private IApiService apiService; + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + private IDdjDeviceExecutionQueueFinishService ddjDeviceExecutionQueueFinishService; + private IMydoJobService mydoJobService; + + public boolean isRun = true; + + + public void init() { + //todo 获取设备编码列表 + ddjCodeList = new HashMap<>(); + ArrayList ddjCodes = new ArrayList<>(); + ddjCodes.add("ddj1"); + ddjCodes.add("ddj2"); + ddjCodeList.put("ddj", ddjCodes); + + apiService = SpringUtils.getBean(IApiService.class); + ddjDeviceExecutionQueueService = SpringUtils.getBean(IDdjDeviceExecutionQueueService.class); + ddjDeviceExecutionQueueFinishService = SpringUtils.getBean(IDdjDeviceExecutionQueueFinishService.class); + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + log.info("初始化服务 {},{},{},{}", apiService, ddjDeviceExecutionQueueService, ddjDeviceExecutionQueueFinishService,mydoJobService); + } + + @Override + public void run() { + init(); + while (isRun) { + for (String type : ddjCodeList.keySet()) { + + if ("ddj".equals(type)) { + for (String ddjCode : ddjCodeList.get(type)) { + try { + if (PlcDdjRead.deviceDdjMap.containsKey(ddjCode)) { + ddjTask(PlcDdjRead.deviceDdjMap.get(ddjCode)); + } else { + log.error("设备不存在 ssjCode{}", ddjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } + + } + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + private void ddjTask(MsgDdj ddj) { + /* + 第一部分 + 堆垛机线程进来后第一件事先进行堆垛机正执行任务的账务处理 + 以防存在漏处理,没处理的情况发生 + */ + if (ddj.getCzfs() == 5 && ddj.getSbzt() == 0 && ddj.getJdbz() == 5) { + Long rwh = ddj.getRwh(); + //完成作业 + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(rwh); + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueByRwh(rwh,ddj.getId()); + if (mydoJob != null && ddjDeviceExecutionQueue != null) { + //回传完成信号 + ddjTaskFinish(rwh, ddj.getId()); + } + } + + /* + 第二部分 + 跟踪队列执行阶段 + */ + if (ddj.getCzfs() == 5 && ddj.getSbzt() == 1 && (ddj.getJdbz() == 2 || ddj.getJdbz() == 3 || ddj.getJdbz() == 4)) { + //根据堆垛机上任务号,修改任务执行阶段 + Long rwh = ddj.getRwh(); + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueByRwh(rwh, ddj.getId()); + if (ddjDeviceExecutionQueue != null) { + //修改队列执行阶段 + ddjDeviceExecutionQueue.setRwzt(ddj.getJdbz()); + ddjDeviceExecutionQueueService.updateDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + } + } + + /* + 第三部分 + 放货有货,重新分配货位 + */ + if (ddj.getCzfs() == 5 && ddj.getSbzt() == 1052 && ddj.getJdbz() < 5 && ddj.getYxxf() == 1) { + //判断堆垛机上任务是否已重分,若未重分,则调用接口,请求重分货位 + Long rwh = ddj.getRwh(); + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(rwh); + if ("0".equals(mydoJob.getFaultFlag())){ + mydoJob.setStatus("1"); + mydoJobService.updateMydoJob(mydoJob); + AjaxResult ajaxResult = apiService.reAssign(new JobParam(rwh.toString(), null, null)); + if (ajaxResult.isSuccess()){ + mydoJob.setFaultFlag("2"); + mydoJobService.updateMydoJob(mydoJob); + } + } + } + + /* + 第四部分 + 取货无货,重新分配单据 + */ + if (ddj.getCzfs() == 5 && ddj.getSbzt() == 1053 && ddj.getJdbz() < 5) { + Long rwh = ddj.getRwh(); + //无货异常处理 + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueByRwh(rwh,ddj.getId()); + //找到对应任务 调用api接口 取货无货,重新分配单据 + if (ObjUtil.isNotNull(ddjDeviceExecutionQueue)) { + AjaxResult ajaxResult = apiService.reMaterial(new JobParam(rwh.toString(), null, null)); + if (ajaxResult.isSuccess()) { + //作废作业 + InvalidMydoJob(rwh, ddj.getId()); + } + } + } + + /* + 第五部分 + 堆垛机任务下发 + */ + if (ddj.getYxxf() == 1 && ddj.getCzfs() == 5 && ddj.getSbzt() == 1052) { + Long rwh = ddj.getRwh(); + //查询作业 + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(rwh); + if (ObjUtil.isNotNull(mydoJob) && "2".equals(mydoJob.getFaultFlag())) { + //判断任务是否重分完成 + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueByRwh(rwh,ddj.getId()); + if (ObjUtil.isNotNull(ddjDeviceExecutionQueue)) { + //重新下发堆垛机任务 + int i = issuedDDJTask(ddj.getId(), ddjDeviceExecutionQueue); + if (i == 1) { + //更新开始时间,修改ddjxf为已下发1 + ddjDeviceExecutionQueue.setXfsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setDdjxf("1"); + ddjDeviceExecutionQueueService.updateDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + mydoJob.setFaultFlag("0"); + mydoJobService.updateMydoJob(mydoJob); + log.info("托盘堆垛机重新下发任务成功 rwh:{}", rwh); + } else { + log.error("托盘堆垛机重新下发任务失败!!!!!!!"); + } + } + } + } + //正常下发 + if (ddj.getYxxf() == 1 && ddj.getCzfs() == 5 && ddj.getSbzt() == 0) { + Long rwh = ddj.getRwh(); + //对堆垛机执行队列中无已下发队列 + DdjDeviceExecutionQueue ddjDeviceExecutionQueueParam = new DdjDeviceExecutionQueue(); + ddjDeviceExecutionQueueParam.setDdjxf("1"); + ddjDeviceExecutionQueueParam.setDevId(ddj.getId()); + List ddjDeviceExecutionQueues = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueList(ddjDeviceExecutionQueueParam); + //取堆垛机队列中,生成时间最早的执行队列下发 + if (CollUtil.isEmpty(ddjDeviceExecutionQueues)) { + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = ddjDeviceExecutionQueues.get(0); + if (ddjDeviceExecutionQueue == null) { + return; + } + int i = issuedDDJTask(ddj.getId(), ddjDeviceExecutionQueue); + if (i == 1) { + //更新开始时间,修改ddjxf为已下发1 + ddjDeviceExecutionQueue.setXfsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setDdjxf("1"); + ddjDeviceExecutionQueueService.updateDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + log.info("托盘堆垛机下发任务成功 rwh:{}", rwh); + } else { + log.error("托盘堆垛机下发任务失败!!!!!!!"); + } + } + } + + } + + /** + * 作废作业 + * + * @param rwh 作业单号 + * @param id 作业单id + */ + private void InvalidMydoJob(Long rwh, Integer id) { + mydoJobService.InvalidMydoJob(rwh, id); + } + + /** + * 堆垛机任务完成 + * + * @param rwh 作业单号 + * @param id 作业单id + */ + private void ddjTaskFinish(Long rwh, Integer id) { + mydoJobService.ddjTaskFinish(rwh, id); + } + + + /** + * 给堆垛机下发任务 + * + * @param queue 堆垛机执行的队列 + */ + private int issuedDDJTask(Integer devId, DdjDeviceExecutionQueue queue) { + int taskNo = Convert.toInt(queue.getRwh()); + int yph = Convert.toInt(queue.getYph()); + int ylh = Convert.toInt(queue.getYlh()); + int ych = Convert.toInt(queue.getYch()); + int mbph = Convert.toInt(queue.getMbph()); + int mblh = Convert.toInt(queue.getMblh()); + int mbch = Convert.toInt(queue.getMbch()); + int mlfs = 5; + int xfwc = 1; + int jym = Tools.getJYM(taskNo, yph, ylh, ych, mbph, mblh, mbch, mlfs, xfwc); + byte[] sendData = Tools.sendDataToDevice(taskNo, null, yph, ylh, ych, mbph, mblh, mbch, mlfs, xfwc, jym); + if (devId==1){ + return PlcDdjRead.WriteToDDJ("DB61.0", sendData); + }else { + return PlcDdjRead.WriteToDDJ("DB61.0", sendData); + } + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java new file mode 100644 index 0000000..46db329 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/monit/MonitoringThread.java @@ -0,0 +1,48 @@ +package com.ruoyi.schedule.scheduleTask.monit; + +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ddj.service.IMsgDdjService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.device.ssj.service.IDeviceSsjService; +import com.ruoyi.schedule.plcRead.ddj.PlcDdjRead; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +/** + * 监控线程 + */ +@Slf4j +public class MonitoringThread implements Runnable { + + public boolean isRun = true; + + private IDeviceSsjService deviceSsjService; + private IMsgDdjService msgDdjService; + + + public void init() { + deviceSsjService = SpringUtils.getBean(IDeviceSsjService.class); + msgDdjService = SpringUtils.getBean(IMsgDdjService.class); + } + + @Override + public void run() { + init(); + while (isRun){ + for (String ssjCode : PlcSsjRead.deviceSsjMap.keySet()) { + DeviceSsj deviceSsj = PlcSsjRead.deviceSsjMap.get(ssjCode); + deviceSsjService.updateDeviceSsj(deviceSsj); + } + for (String ddjCode : PlcDdjRead.deviceDdjMap.keySet()) { + msgDdjService.updateMsgDdj(PlcDdjRead.deviceDdjMap.get(ddjCode)); + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayInSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayInSsjThread.java new file mode 100644 index 0000000..9aa8043 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayInSsjThread.java @@ -0,0 +1,230 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor1; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +import static com.ruoyi.common.TaskStatusCode.*; +import static com.ruoyi.common.TaskType.DDJ_RWLX_3; +import static com.ruoyi.schedule.plcRead.ssj.PlcSsjRead.writeToSsj; + +/** + * 一楼双向线体入库线程 条码+到位+无出库作业 下发去向+任务号 有出库作业就下发退回 + */ + +@Slf4j +public class Floor1BothWayInSsjThread implements Runnable { + + public HashMap ssjCodeList; + + private IApiService apiService; + private IMydoJobService mydoJobService; + private IBasCargoSpaceService basCargoSpaceService; + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + private final Map> roadwayMap = new HashMap<>(); + private final Map exitRoadwayMap = new HashMap<>(); + + + public boolean isRun = true; + + + public void init() { + ssjCodeList = new HashMap<>(); + ssjCodeList.put("roadway1", PlcSsjRead.deviceSsjMap.get("L2003")); + ssjCodeList.put("roadway2", PlcSsjRead.deviceSsjMap.get("L2005")); + ssjCodeList.put("in1", PlcSsjRead.deviceSsjMap.get("L2012")); + ssjCodeList.put("in2", PlcSsjRead.deviceSsjMap.get("L2013")); + + + apiService = SpringUtils.getBean(IApiService.class); + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + basCargoSpaceService = SpringUtils.getBean(IBasCargoSpaceService.class); + ddjDeviceExecutionQueueService = SpringUtils.getBean(IDdjDeviceExecutionQueueService.class); + + + //初始化巷道口 排 列 层 + roadwayMap.put("L2003", Arrays.asList(1, 1, 1)); + roadwayMap.put("L2005", Arrays.asList(1, 1, 1)); + + exitRoadwayMap.put("L2003", "L2012"); + exitRoadwayMap.put("L2005", "L2013"); + + } + + @Override + public void run() { + init(); + while (isRun) { + + for (String type : ssjCodeList.keySet()) { + switch (type) { + case "roadway1": + case "roadway2": + dispatch1(ssjCodeList.get(type)); + break; + case "in1": + case "in2": + dispatch2(ssjCodeList.get(type)); + break; + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + private void dispatch1(DeviceSsj ssj) { + if (ssj.getDw() == 1 && ssj.getTaskNo() > 0) { + int taskNo = ssj.getTaskNo(); + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(taskNo)); + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_SSJ)) { + mydoJob.setExecutionPhase(TASK_EP_ROADWAY_SSJ); + mydoJobService.updateMydoJob(mydoJob); + } + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_ROADWAY_SSJ)) { + List ddjDeviceExecutionQueues = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueListByRwlx(DDJ_RWLX_3, Convert.toInt(mydoJob.getRoadwayId())); + BasCargoSpace basCargoSpace = basCargoSpaceService.selectBasCargoSpaceByWmsCargoSpaceId(mydoJob.getCargoSpaceId()); + List pclList = roadwayMap.get(ssj.getDeviceCode()); + if (CollUtil.isEmpty(ddjDeviceExecutionQueues) && ObjUtil.isNotNull(basCargoSpace) && CollUtil.isNotEmpty(pclList)) { + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = new DdjDeviceExecutionQueue(); + ddjDeviceExecutionQueue.setUuid(IdUtil.simpleUUID()); + ddjDeviceExecutionQueue.setRwh(Convert.toLong(mydoJob.getTaskNo())); + ddjDeviceExecutionQueue.setDevName(mydoJob.getRoadwayId()); + ddjDeviceExecutionQueue.setDevId(Convert.toInt(mydoJob.getRoadwayId())); + ddjDeviceExecutionQueue.setRwzt(0); + ddjDeviceExecutionQueue.setYph(pclList.get(0)); + ddjDeviceExecutionQueue.setYlh(pclList.get(1)); + ddjDeviceExecutionQueue.setYch(pclList.get(2)); + ddjDeviceExecutionQueue.setMbph(Convert.toInt(basCargoSpace.getThePlatoon())); + ddjDeviceExecutionQueue.setMblh(Convert.toInt(basCargoSpace.getTheColumn())); + ddjDeviceExecutionQueue.setMbch(Convert.toInt(basCargoSpace.getTheFloor())); + ddjDeviceExecutionQueue.setDdjxf("0"); + ddjDeviceExecutionQueue.setMlfs(5); + ddjDeviceExecutionQueue.setScsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setRwlx(DDJ_RWLX_3); + ddjDeviceExecutionQueueService.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + + mydoJob.setExecutionPhase(TASK_EP_DDJ); + mydoJobService.updateMydoJob(mydoJob); + } + } + } + } + + private void dispatch2(DeviceSsj ssj) { + if (ssj.getDw() == 1 && ssj.getTaskNo() > 0 && ssj.getTaskNo() < 1000 && ssj.getTraySize() != 0) { + //先检测有没有出库作业,有出库作业直接退回 + MydoJob outMydoJob = mydoJobService.selectRunningMydoJobByExitList(Collections.singletonList(ssj.getDeviceCode())); + String l2012WriteAddress = "DB501.20"; + String l2013WriteAddress = "DB501.30"; + if (ObjUtil.isNotNull(outMydoJob) || StrUtil.isEmpty(ssj.getTrayCode())) { + int i = 0; + //下发去向 和任务号 + switch (ssj.getDeviceCode()) { + case "L2012": + i = writeToSsj(l2012WriteAddress,"0",2,1); + break; + case "L2013": + i = writeToSsj(l2013WriteAddress,"0",2,1); + break; + } + if (i == 1) { + log.info("未检测到托盘码下发回退 rwh:{},qx:{},trayCode:{}", ssj.getTaskNo(), 1, ssj.getTrayCode()); + } + } else { + if (StrUtil.isNotBlank(ssj.getTrayCode())) { + MydoJob param = new MydoJob(); + param.setEntranceId(ssj.getDeviceCode()); + List mydoJobs = mydoJobService.selectMydoJobList(param); + MydoJob inJob = null; + if (CollUtil.isNotEmpty(mydoJobs)) { + inJob = mydoJobs.get(0); + } else { + //根据托盘码请求入库作业 + HashMap paramMap = new HashMap<>(); + paramMap.put("trayCode", ssj.getTrayCode()); + paramMap.put("size", ssj.getTraySize()); + paramMap.put("floor", "1"); + paramMap.put("entranceId",ssj.getDeviceCode()); + AjaxResult result = apiService.selectMydoJobByTrayCodeAndEntranceId(paramMap); + if (result.isSuccess()) { + inJob = BeanUtil.toBean(result.get("data"), MydoJob.class); + inJob.setStatus(TASK_STATUS_CREATE); + mydoJobService.insertMydoJob(inJob); + } else { + log.info("未检测到托盘码请求入库作业 rwh:{},trayCode:{}", ssj.getTaskNo(), ssj.getTrayCode()); + } + } + if (inJob != null) { + int i = 0; + //下发去向 和任务号 + switch (ssj.getDeviceCode()) { + case "L2012": + i = writeToSsj(l2012WriteAddress,inJob.getTaskNo(),1,1); + break; + case "L2013": + i = writeToSsj(l2013WriteAddress,inJob.getTaskNo(),1,1); + break; + } + if (i == 1) { + inJob.setStatus(TASK_STATUS_RUNNING); + inJob.setExecutionPhase(TASK_EP_SSJ); + inJob.setStartTime(DateUtil.formatDateTime(DateUtil.date())); + inJob.setTargetRoadwayId(Convert.toStr(1)); + mydoJobService.updateMydoJob(inJob); + log.info("入库请求下发任务号加去向 rwh:{},qx:{},trayCode:{}", ssj.getTaskNo(), 1, ssj.getTrayCode()); + } + } + }else { + HashMap paramMap = new HashMap<>(); + paramMap.put("size", ssj.getTraySize()); + AjaxResult ajaxResult = apiService.selectMydoJobByNoTrayCode(paramMap); + if (ajaxResult.isSuccess()) { + MydoJob mydoJob2 = BeanUtil.toBean(ajaxResult.get("data"), MydoJob.class); + mydoJobService.insertMydoJob(mydoJob2); + int i = writeToSsj(ssj.getPlcDbWriteAddress(),mydoJob2.getTaskNo(),1,1); + if (i == 1) { + mydoJob2.setExecutionPhase("1"); + mydoJobService.updateMydoJob(mydoJob2); + log.info("{}下发任务号{}成功:",ssj.getDeviceCode(),mydoJob2.getTaskNo()); + } + } else if (ajaxResult.isError()) { + //超大,给plc下发错误信息,等待人工处理 + //托盘大小对不上,给plc下发错误信息,等待人工处理 + //码不对,给plc下发错误信息,等待人工处理 + int i = writeToSsj(ssj.getPlcDbWriteAddress(), "0", Integer.parseInt(ajaxResult.get("code").toString()), 2); + if (i == 1) { + log.info("{}下发错误信号{}成功:",ssj.getDeviceCode(),Integer.parseInt(ajaxResult.get("code").toString())); + } + } + } + } + + } + } + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayOutSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayOutSsjThread.java new file mode 100644 index 0000000..bc03980 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1BothWayOutSsjThread.java @@ -0,0 +1,182 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor1; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IJobCallBackService; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +import static com.ruoyi.common.TaskStatusCode.*; +import static com.ruoyi.common.TaskType.DDJ_RWLX_3; + +/** + * 一楼双向线体出库线程 循环盘点出库作业+巷道口空闲+出库线体全部空闲+无入库作业 生成堆垛机出库队列 + */ + +@Slf4j +public class Floor1BothWayOutSsjThread implements Runnable { + + public HashMap> ssjCodeList; + + private IMydoJobService mydoJobService; + private IBasCargoSpaceService basCargoSpaceService; + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + private final Map> roadwayMap = new HashMap<>(); + private IJobCallBackService jobCallBackService; + + + public boolean isRun = true; + + + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList roadway1 = new ArrayList<>(); + roadway1.add(PlcSsjRead.deviceSsjMap.get("L2003")); + roadway1.add(PlcSsjRead.deviceSsjMap.get("L2004")); + roadway1.add(PlcSsjRead.deviceSsjMap.get("L2012")); + ssjCodeList.put("roadway1", roadway1); + ArrayList roadway2 = new ArrayList<>(); + roadway2.add(PlcSsjRead.deviceSsjMap.get("L2005")); + roadway2.add(PlcSsjRead.deviceSsjMap.get("L2006")); + roadway2.add(PlcSsjRead.deviceSsjMap.get("L2013")); + ssjCodeList.put("roadway2", roadway2); + ArrayList ou1 = new ArrayList<>(); + ou1.add(PlcSsjRead.deviceSsjMap.get("L2012")); + ssjCodeList.put("ou1", ou1); + ArrayList out2 = new ArrayList<>(); + out2.add(PlcSsjRead.deviceSsjMap.get("L2013")); + ssjCodeList.put("out2", out2); + + + + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + basCargoSpaceService = SpringUtils.getBean(IBasCargoSpaceService.class); + ddjDeviceExecutionQueueService = SpringUtils.getBean(IDdjDeviceExecutionQueueService.class); + jobCallBackService = SpringUtils.getBean(IJobCallBackService.class); + + + //初始化巷道口 排 列 层 + roadwayMap.put("L2003",Arrays.asList(1,1,1)); + roadwayMap.put("L2005",Arrays.asList(1,1,1)); + } + + @Override + public void run() { + init(); + while (isRun){ + for (String type : ssjCodeList.keySet()) { + + switch (type){ + case "roadway1": + case "roadway2": + dispatch(ssjCodeList.get(type)); + break; + case "out1": + case "out2": + dispatch1(ssjCodeList.get(type)); + break; + } + + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + private void dispatch1(List ssjList) { + for (DeviceSsj ssj : ssjList) { + if (ssj.getDw()==1 && ssj.getTaskNo()>0){ + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(ssj.getTaskNo())); + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_ROADWAY_SSJ)) { + mydoJob.setExecutionPhase(TASK_EP_SSJ); + mydoJobService.updateMydoJob(mydoJob); + } + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_SSJ)) { + JobCallBack jobCallBack = new JobCallBack(); + jobCallBack.setType("2"); + jobCallBack.setMydojobId(mydoJob.getJobId()); + jobCallBack.setStatus("0"); + jobCallBack.setTaskNo(Convert.toLong(mydoJob.getTaskNo())); + jobCallBack.setCreateTime(new Date()); + jobCallBackService.insertJobCallBack(jobCallBack); + + log.info("完成任务 taskNo{} ", mydoJob.getTaskNo()); + mydoJob.setStatus(TASK_STATUS_FINISH); + mydoJob.setExecutionPhase(TASK_EP_WMS_FINISH); + mydoJob.setFinishTime(DateUtil.formatDateTime(new Date())); + mydoJobService.updateMydoJob(mydoJob); + mydoJobService.insertHistory(mydoJob.getJobId()); + mydoJobService.deleteMydoJobByJobId(mydoJob.getJobId()); + + } + } + } + } + + /** + * 出库堆垛机队列生成 + */ + public void dispatch(List ssjList) { + DeviceSsj deviceSsj = ssjList.get(ssjList.size() - 1); + MydoJob pdOutJob = mydoJobService.selectMydoJobByExitList(Collections.singletonList(deviceSsj.getDeviceCode())); + MydoJob inJob = mydoJobService.selectMydoJobByEntranceList(Collections.singletonList(deviceSsj.getDeviceCode())); + boolean allKx = ssjList.stream().allMatch(ssj -> ssj.getKx() == 1); + if (ObjUtil.isNotNull(pdOutJob) && allKx && ObjUtil.isNull(inJob)) { + //所有线体为空闲 并且存在出库作业 并且不存在入库作业 + BasCargoSpace basCargoSpace = basCargoSpaceService.selectBasCargoSpaceByWmsCargoSpaceId(pdOutJob.getCargoSpaceId()); + if (ObjUtil.isNull(basCargoSpace)){ + log.error("货位不存在 cargoSpaceId{}",pdOutJob.getCargoSpaceId()); + }else { + //生成队列 + List list = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueListByRwlx(DDJ_RWLX_3,Convert.toInt(pdOutJob.getRoadwayId())); + List pclList = roadwayMap.get(ssjList.get(0).getDeviceCode()); + if (CollUtil.isEmpty(list) && CollUtil.isNotEmpty(pclList)){ + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = new DdjDeviceExecutionQueue(); + ddjDeviceExecutionQueue.setUuid(IdUtil.simpleUUID()); + ddjDeviceExecutionQueue.setRwh(Convert.toLong(pdOutJob.getTaskNo())); + ddjDeviceExecutionQueue.setDevName(pdOutJob.getRoadwayId()); + ddjDeviceExecutionQueue.setDevId(Convert.toInt(pdOutJob.getRoadwayId())); + ddjDeviceExecutionQueue.setRwzt(0); + ddjDeviceExecutionQueue.setYph(Convert.toInt(basCargoSpace.getThePlatoon())); + ddjDeviceExecutionQueue.setYlh(Convert.toInt(basCargoSpace.getTheColumn())); + ddjDeviceExecutionQueue.setYch(Convert.toInt(basCargoSpace.getTheFloor())); + ddjDeviceExecutionQueue.setMbph(pclList.get(0)); + ddjDeviceExecutionQueue.setMblh(pclList.get(1)); + ddjDeviceExecutionQueue.setMbch(pclList.get(2)); + ddjDeviceExecutionQueue.setDdjxf("0"); + ddjDeviceExecutionQueue.setMlfs(5); + ddjDeviceExecutionQueue.setScsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setRwlx(DDJ_RWLX_3); + ddjDeviceExecutionQueueService.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + + pdOutJob.setStatus(TASK_STATUS_RUNNING); + pdOutJob.setExecutionPhase(TASK_EP_DDJ); + pdOutJob.setStartTime(DateUtil.formatDateTime(DateUtil.date())); + mydoJobService.updateMydoJob(pdOutJob); + } + } + + } + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1SingleSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1SingleSsjThread.java new file mode 100644 index 0000000..86b467e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor1/Floor1SingleSsjThread.java @@ -0,0 +1,194 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor1; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IJobCallBackService; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +import static com.ruoyi.common.TaskStatusCode.*; +import static com.ruoyi.common.TaskType.DDJ_RWLX_2; + +/** + * 一楼单向出库线程 循环出库作业+巷道口空闲 生成堆垛机出库队列 + */ +@Slf4j +public class Floor1SingleSsjThread implements Runnable { + + public HashMap> ssjCodeList; + private IMydoJobService mydoJobService; + public boolean isRun = true; + private final Map exitRoadwayMap = new HashMap<>(); + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + private IBasCargoSpaceService basCargoSpaceService; + private final Map> roadwayMap = new HashMap<>(); + private IJobCallBackService jobCallBackService; + + + public void init() { + //设置要循环的输送机 + ssjCodeList = new HashMap<>(); + ArrayList outRoadwaySsjList = new ArrayList<>(); + outRoadwaySsjList.add("L2001"); + outRoadwaySsjList.add("L2007"); + ssjCodeList.put("outRoadwaySsj", outRoadwaySsjList); + ArrayList outSsjList = new ArrayList<>(); + outSsjList.add("L2002"); + outSsjList.add("L2008"); + ssjCodeList.put("outSsj", outSsjList); + + + //初始化服务 + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + ddjDeviceExecutionQueueService = SpringUtils.getBean(IDdjDeviceExecutionQueueService.class); + basCargoSpaceService = SpringUtils.getBean(IBasCargoSpaceService.class); + jobCallBackService = SpringUtils.getBean(IJobCallBackService.class); + + + //初始化出口和巷道口映射关系 + exitRoadwayMap.put("L2001", "L2002"); + exitRoadwayMap.put("L2007", "L2008"); + + //初始化巷道口 排 列 层 + roadwayMap.put("L2001", Arrays.asList(1, 1, 1)); + roadwayMap.put("L2007", Arrays.asList(1, 1, 1)); + } + + @Override + public void run() { + init(); + while (isRun) { + for (String type : ssjCodeList.keySet()) { + //一楼单向巷道口 + if ("outRoadwaySsj".equals(type)) { + for (String ssjCode : ssjCodeList.get(type)) { + try { + if (PlcSsjRead.deviceSsjMap.containsKey(ssjCode)) { + dispatch(PlcSsjRead.deviceSsjMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } + + } + } + if ("outSsj".equals(type)) { + for (String ssjCode : ssjCodeList.get(type)) { + try { + if (PlcSsjRead.deviceSsjMap.containsKey(ssjCode)) { + dispatch2(PlcSsjRead.deviceSsjMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } + } + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + /** + * 一楼单向出库口调度 + * + * @param ssj 输送机 + */ + private void dispatch2(DeviceSsj ssj) { + if (ssj.getDw() == 1 && ssj.getTaskNo() > 0) { + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(ssj.getTaskNo())); + if (ObjUtil.isNotNull(mydoJob)) { + mydoJob.setExecutionPhase(TASK_EP_SSJ); + mydoJobService.updateMydoJob(mydoJob); + JobCallBack jobCallBack = new JobCallBack(); + jobCallBack.setType("2"); + jobCallBack.setMydojobId(mydoJob.getJobId()); + jobCallBack.setStatus("0"); + jobCallBack.setTaskNo(Convert.toLong(mydoJob.getTaskNo())); + jobCallBack.setCreateTime(new Date()); + jobCallBackService.insertJobCallBack(jobCallBack); + log.info("完成任务 taskNo{} ", mydoJob.getTaskNo()); + mydoJob.setStatus(TASK_STATUS_FINISH); + mydoJob.setFinishTime(DateUtil.formatDateTime(new Date())); + mydoJob.setExecutionPhase(TASK_EP_WMS_FINISH); + mydoJobService.updateMydoJob(mydoJob); + mydoJobService.insertHistory(mydoJob.getJobId()); + mydoJobService.deleteMydoJobByJobId(mydoJob.getJobId()); + } + } + } + + /** + * 一楼单向巷道口调度 + * + * @param ssj 输送机 + */ + public void dispatch(DeviceSsj ssj) { + //如果入库请求1 并且 托盘码不为空 + if (ssj.getKx() == 1) { + String exitId = exitRoadwayMap.get(ssj.getDeviceCode()); + List exitList = new ArrayList<>(); + exitList.add(exitId); + MydoJob mydoJob = mydoJobService.selectMydoJobByExitList(exitList); + if (ObjUtil.isNotNull(mydoJob)) { + BasCargoSpace basCargoSpace = basCargoSpaceService.selectBasCargoSpaceByWmsCargoSpaceId(mydoJob.getCargoSpaceId()); + if (ObjUtil.isNull(basCargoSpace)) { + log.error("货位不存在 cargoSpaceId{}", mydoJob.getCargoSpaceId()); + } else { + //生成队列 + List list = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueListByRwlx(DDJ_RWLX_2, Convert.toInt(mydoJob.getRoadwayId())); + List pclList = roadwayMap.get(ssj.getDeviceCode()); + if (CollUtil.isEmpty(list) && CollUtil.isNotEmpty(pclList)) { + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = new DdjDeviceExecutionQueue(); + ddjDeviceExecutionQueue.setUuid(IdUtil.simpleUUID()); + ddjDeviceExecutionQueue.setRwh(Convert.toLong(mydoJob.getTaskNo())); + ddjDeviceExecutionQueue.setDevName(mydoJob.getRoadwayId()); + ddjDeviceExecutionQueue.setDevId(Convert.toInt(mydoJob.getRoadwayId())); + ddjDeviceExecutionQueue.setRwzt(0); + ddjDeviceExecutionQueue.setYph(Convert.toInt(basCargoSpace.getThePlatoon())); + ddjDeviceExecutionQueue.setYlh(Convert.toInt(basCargoSpace.getTheColumn())); + ddjDeviceExecutionQueue.setYch(Convert.toInt(basCargoSpace.getTheFloor())); + ddjDeviceExecutionQueue.setMbph(pclList.get(0)); + ddjDeviceExecutionQueue.setMblh(pclList.get(1)); + ddjDeviceExecutionQueue.setMbch(pclList.get(2)); + ddjDeviceExecutionQueue.setDdjxf("0"); + ddjDeviceExecutionQueue.setMlfs(5); + ddjDeviceExecutionQueue.setScsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setRwlx(DDJ_RWLX_2); + ddjDeviceExecutionQueueService.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + + //修改作业状态 + mydoJob.setStatus(TASK_STATUS_RUNNING); + mydoJob.setExecutionPhase(TASK_EP_DDJ); + mydoJob.setStartTime(DateUtil.formatDateTime(DateUtil.date())); + mydoJobService.updateMydoJob(mydoJob); + } + } + } + } + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2InOutSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2InOutSsjThread.java new file mode 100644 index 0000000..4fb3c7d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2InOutSsjThread.java @@ -0,0 +1,155 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor2; + +import cn.hutool.core.convert.Convert; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IJobCallBackService; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import static com.ruoyi.common.TaskStatusCode.TASK_EP_SSJ; +import static com.ruoyi.schedule.plcRead.ssj.PlcSsjRead.writeToSsj; + +/** + * 二楼出入口输送线线程 + */ +@Slf4j +public class Floor2InOutSsjThread implements Runnable { + + public HashMap> ssjCodeList; + + private IApiService apiService; + private IAgvQueueService agvQueueService; + private IMydoJobService mydoJobService; + private IJobCallBackService jobCallBackService; + + public boolean isRun = true; + + + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("L1011"); + ssjCodes.add("L1012"); + ssjCodeList.put("inout", ssjCodes); + apiService = SpringUtils.getBean(IApiService.class); + agvQueueService = SpringUtils.getBean(IAgvQueueService.class); + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + jobCallBackService = SpringUtils.getBean(IJobCallBackService.class); + log.info("初始化服务 {},{}", apiService, agvQueueService); + } + + @Override + public void run() { + init(); + while (isRun) { + for (String type : ssjCodeList.keySet()) { + + switch (type) { + case "inout": + //生产线入库 + for (String ssjCode : ssjCodeList.get(type)) { + try { + if (PlcSsjRead.deviceSsjMap.containsKey(ssjCode)) { + inDispatch(PlcSsjRead.deviceSsjMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } + + } + break; + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + + public void inDispatch(DeviceSsj inSsjDevice) { + //如果输送机空闲 + if (inSsjDevice.getKx() == 1) { + AgvQueue agvQueue = new AgvQueue(); + agvQueue.setStatus("5");//请求放货 + agvQueue.setPositionCode2(inSsjDevice.getDeviceCode()); + List agvQueues = agvQueueService.selectAgvQueueList(agvQueue); + //不允许放货 + if (!agvQueues.isEmpty() && inSsjDevice.getAllowPut() == 0) { + //如果有这个输送机口的agv队列并请求放货(状态为6) + int i = writeToSsj(inSsjDevice.getPlcDbWriteAddress(), "0", Integer.parseInt(agvQueues.get(0).getCtnrType()),1); + if (i == 1) { + log.info(inSsjDevice.getDeviceCode() + "输送机下发请求放货成功"); + } + } + //允许放货 + if (!agvQueues.isEmpty() && inSsjDevice.getAllowPut() == 1) { + log.info("设备:{} 位置:{} 任务号:{} 请求放货成功", inSsjDevice.getDeviceCode(), "输送线", agvQueues.get(0).getTaskCode()); + AjaxResult ajaxResult = apiService.continueTask(agvQueues.get(0)); + if (ajaxResult.isSuccess()) { + log.info("下发agv允许放货成功" + agvQueues.get(0).getWmsTaskNo()); + agvQueues.get(0).setStatus("6"); + agvQueueService.updateAgvQueue(agvQueues.get(0)); + } + } + + } + //入库下发输送线 + if (inSsjDevice.getKx() == 0 && inSsjDevice.getTaskNo() == 0) { + AgvQueue agvQueue = new AgvQueue(); + agvQueue.setStatus("7"); //end + agvQueue.setPositionCode2(inSsjDevice.getDeviceCode()); + List agvQueues = agvQueueService.selectAgvQueueList(agvQueue); + if (!agvQueues.isEmpty()) { + int i = writeToSsj(inSsjDevice.getPlcDbWriteAddress(), agvQueues.get(0).getWmsTaskNo(), 1,1); + if (i == 1) { + log.info("输送机{}下发任务号{}成功", inSsjDevice.getDeviceCode(), agvQueues.get(0).getWmsTaskNo()); + MydoJob mydoJob = mydoJobService.selectMydoJobByJobId(agvQueues.get(0).getTaskCode()); + mydoJob.setExecutionPhase(TASK_EP_SSJ); + mydoJobService.updateMydoJob(mydoJob); + agvQueueService.insertHistory(agvQueues.get(0).getReqCode()); + agvQueueService.deleteAgvQueueByReqCode(agvQueues.get(0).getReqCode()); + } + } + + } + //出库到位 + if (inSsjDevice.getDw() == 1 && inSsjDevice.getTaskNo() > 0) { + + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(inSsjDevice.getTaskNo())); + if (mydoJob != null) { + log.info("设备:{} 位置:{} 任务号:{} 出库到位", "输送线", inSsjDevice.getDeviceCode(), mydoJob.getTaskNo()); + mydoJobService.insertHistory(mydoJob.getJobId()); + mydoJobService.deleteMydoJobByJobId(mydoJob.getJobId()); + JobCallBack jobCallBack = new JobCallBack(); + jobCallBack.setType("2"); + jobCallBack.setMydojobId(mydoJob.getJobId()); + jobCallBack.setStatus("0"); + jobCallBack.setTaskNo(Convert.toLong(mydoJob.getTaskNo())); + jobCallBack.setCreateTime(new Date()); + jobCallBackService.insertJobCallBack(jobCallBack); + } + } + } + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2RoadWaySsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2RoadWaySsjThread.java new file mode 100644 index 0000000..d1eacde --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/Floor2RoadWaySsjThread.java @@ -0,0 +1,139 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjUtil; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.bas.cargospace.domain.BasCargoSpace; +import com.ruoyi.bas.cargospace.service.IBasCargoSpaceService; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.job.domain.JobCallBack; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.queue.ddj.domain.DdjDeviceExecutionQueue; +import com.ruoyi.queue.ddj.service.IDdjDeviceExecutionQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +import static com.ruoyi.common.TaskStatusCode.*; +import static com.ruoyi.common.TaskType.DDJ_RWLX_1; +import static com.ruoyi.common.TaskType.DDJ_RWLX_3; + +/** + * 二楼出库线程 检测当前巷道有没有入库作业,没有作业,生成堆垛机出库队列 + */ +@Slf4j +public class Floor2RoadWaySsjThread implements Runnable { + + public HashMap> ssjCodeList; + private final Map> roadwayMap = new HashMap<>(); + private IApiService apiService; + private IAgvQueueService agvQueueService; + private IMydoJobService mydoJobService; + private IBasCargoSpaceService basCargoSpaceService; + private IDdjDeviceExecutionQueueService ddjDeviceExecutionQueueService; + public boolean isRun = true; + + + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("L1002"); + ssjCodes.add("L1004"); + ssjCodeList.put("roadwaySsj", ssjCodes); + apiService = SpringUtils.getBean(IApiService.class); + agvQueueService = SpringUtils.getBean(IAgvQueueService.class); + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + basCargoSpaceService = SpringUtils.getBean(IBasCargoSpaceService.class); + ddjDeviceExecutionQueueService = SpringUtils.getBean(IDdjDeviceExecutionQueueService.class); + //初始化巷道口 排 列 层 + roadwayMap.put("L1002", Arrays.asList(1, 1, 1)); + roadwayMap.put("L1004", Arrays.asList(1, 1, 1)); + log.info("初始化服务 {},{}", apiService, agvQueueService); + } + + @Override + public void run() { + init(); + while (isRun){ + for (String type : ssjCodeList.keySet()) { + + switch ( type){ + case "roadwaySsj": + //生产线入库 + for (String ssjCode : ssjCodeList.get(type)) { + try { + if (PlcSsjRead.deviceSsjMap.containsKey(ssjCode)) { + inDispatch(PlcSsjRead.deviceSsjMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(),e); + } + + } + break; + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + + public void inDispatch(DeviceSsj roadSsjDevice) { + //如果到位,且任务号大于0 + if (roadSsjDevice.getDw()==1 && roadSsjDevice.getTaskNo()>0) { + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(roadSsjDevice.getTaskNo())); + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_SSJ)) { + mydoJob.setExecutionPhase(TASK_EP_ROADWAY_SSJ); + mydoJobService.updateMydoJob(mydoJob); + } + if (ObjUtil.isNotNull(mydoJob) && mydoJob.getExecutionPhase().equals(TASK_EP_ROADWAY_SSJ)) { + List ddjDeviceExecutionQueues = ddjDeviceExecutionQueueService.selectDdjDeviceExecutionQueueListByRwlx(DDJ_RWLX_1, Convert.toInt(mydoJob.getRoadwayId())); + BasCargoSpace basCargoSpace = basCargoSpaceService.selectBasCargoSpaceByWmsCargoSpaceId(mydoJob.getCargoSpaceId()); + List pclList = roadwayMap.get(roadSsjDevice.getDeviceCode()); + if (CollUtil.isEmpty(ddjDeviceExecutionQueues) && ObjUtil.isNotNull(basCargoSpace) && CollUtil.isNotEmpty(pclList)) { + DdjDeviceExecutionQueue ddjDeviceExecutionQueue = new DdjDeviceExecutionQueue(); + ddjDeviceExecutionQueue.setUuid(IdUtil.simpleUUID()); + ddjDeviceExecutionQueue.setRwh(Convert.toLong(mydoJob.getTaskNo())); + ddjDeviceExecutionQueue.setDevName(mydoJob.getRoadwayId()); + ddjDeviceExecutionQueue.setDevId(Convert.toInt(mydoJob.getRoadwayId())); + ddjDeviceExecutionQueue.setRwzt(0); + ddjDeviceExecutionQueue.setYph(pclList.get(0)); + ddjDeviceExecutionQueue.setYlh(pclList.get(1)); + ddjDeviceExecutionQueue.setYch(pclList.get(2)); + ddjDeviceExecutionQueue.setMbph(Convert.toInt(basCargoSpace.getThePlatoon())); + ddjDeviceExecutionQueue.setMblh(Convert.toInt(basCargoSpace.getTheColumn())); + ddjDeviceExecutionQueue.setMbch(Convert.toInt(basCargoSpace.getTheFloor())); + ddjDeviceExecutionQueue.setDdjxf("0"); + ddjDeviceExecutionQueue.setMlfs(5); + ddjDeviceExecutionQueue.setScsj(DateUtil.formatDateTime(DateUtil.date())); + ddjDeviceExecutionQueue.setRwlx(DDJ_RWLX_1); + ddjDeviceExecutionQueueService.insertDdjDeviceExecutionQueue(ddjDeviceExecutionQueue); + + mydoJob.setExecutionPhase(TASK_EP_DDJ); + mydoJobService.updateMydoJob(mydoJob); + + + } + + } + + + + } + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/LineSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/LineSsjThread.java new file mode 100644 index 0000000..24bf02b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/floor2/LineSsjThread.java @@ -0,0 +1,258 @@ +package com.ruoyi.schedule.scheduleTask.ssj.floor2; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.domain.SendData; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.job.domain.MydoJob; +import com.ruoyi.job.service.IMydoJobService; +import com.ruoyi.queue.agv.domain.AgvQueue; +import com.ruoyi.queue.agv.service.IAgvQueueService; +import com.ruoyi.schedule.plcRead.ssj.PlcSsjRead; +import com.ruoyi.schedule.plcRead.util.Tools; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import static com.ruoyi.common.TaskStatusCode.TASK_EP_AGV; +import static com.ruoyi.schedule.plcRead.ssj.PlcSsjRead.deviceSsjMap; +import static com.ruoyi.schedule.plcRead.ssj.PlcSsjRead.writeToSsj; + +/** + * 生产线输送机 + * 生产线线程 检测到位+托盘条码+托盘大小 请求wms生成入库作业 。 循环作业表,检测有没有出库作业 ,生成agv搬运作业。 + */ +@Slf4j +public class LineSsjThread implements Runnable { + public HashMap> ssjCodeList; + + private IApiService apiService; + private IAgvQueueService agvQueueService; + private IMydoJobService mydoJobService; + + public boolean isRun = true; + + + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G1005"); + ssjCodeList.put("line", ssjCodes); + apiService = SpringUtils.getBean(IApiService.class); + agvQueueService = SpringUtils.getBean(IAgvQueueService.class); + mydoJobService = SpringUtils.getBean(IMydoJobService.class); + log.info("初始化服务 {},{}", apiService, agvQueueService); + } + + @Override + public void run() { + init(); + while (isRun) { + for (String type : ssjCodeList.keySet()) { + + switch (type) { + case "line": + //生产线入库 + for (String ssjCode : ssjCodeList.get(type)) { + try { + if (deviceSsjMap.containsKey(ssjCode)) { + inDispatch(deviceSsjMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } catch (RuntimeException e) { + log.error(e.getMessage(), e); + } + + } + break; + } + } + try { + Thread.sleep(1500); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + } + + + public void inDispatch(DeviceSsj SsjDevice) { + //如果到位,扫到条码,托盘大小,执行阶段卡控一下(下发输送机后改执行阶段) + if (SsjDevice.getDw() == 1 && StrUtil.isNotBlank(SsjDevice.getTrayCode()) && SsjDevice.getTraySize() != 0 && SsjDevice.getTaskNo() < 1000) { + log.info("设备:{} 位置:{} 条码:{} 托盘大小:{}", SsjDevice.getDeviceCode(), "产线到位", SsjDevice.getTrayCode(), SsjDevice.getTraySize()); + + //查作业,入如果已经有了,直接下发输送线 + MydoJob mydoJob = new MydoJob(); + mydoJob.setInOrOut("1"); + mydoJob.setStatus("1"); + mydoJob.setEntranceId("G1005"); + mydoJob.setExecutionPhase("0"); + List mydoJobs = mydoJobService.selectInOrOutMydoJobList(mydoJob); + //有作业,下发输送机任务号,覆盖任务号 + if (!mydoJobs.isEmpty()) { + + mydoJob = mydoJobs.get(0); + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(),mydoJob.getTaskNo(),1,1); + if (i == 1) { + //mydoJob.setExecutionPhase(TASK_EP_AGV); + mydoJobService.updateMydoJob(mydoJob); + log.info("G1005产线输送机下发任务号成功:" + mydoJob.getTaskNo()); + } + + + } else { + + //请求wms生成入库作业 + HashMap paramMap = new HashMap<>(); + paramMap.put("trayCode", SsjDevice.getTrayCode()); + paramMap.put("size", SsjDevice.getTraySize()); + paramMap.put("floor", "2"); + paramMap.put("entranceId", "G1005"); + AjaxResult ajaxResult = apiService.createInMydoJob(paramMap); + if (ajaxResult.isSuccess()) { + //拿到正常作业,插到wcs作业表,下发给输送机任务号,覆盖任务号 + MydoJob mydoJob2 = BeanUtil.toBean(ajaxResult.get("data"), MydoJob.class) ; + mydoJob2.setStatus("1"); + mydoJob2.setExecutionPhase("0"); + mydoJob2.setCreateTime(new Date()); + + mydoJobService.insertMydoJob(mydoJob2); + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(),mydoJob2.getTaskNo(),1,1); + if (i == 1) { + //mydoJob2.setExecutionPhase("1"); + mydoJobService.updateMydoJob(mydoJob2); + log.info("G1005产线输送机下发任务号成功:{}" , mydoJob2.getTaskNo()); + } + } else { + //超大,给plc下发错误信息,等待人工处理 + //托盘大小对不上,给plc下发错误信息,等待人工处理 + //码不对,给plc下发错误信息,等待人工处理 + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(), "0", Integer.parseInt(ajaxResult.get("code").toString()), 2); + if (i == 1) { + log.info("G1005产线输送机下发错误信息成功"); + } + + } + + + } + } + + if (SsjDevice.getDw() == 1 && StrUtil.isBlank(SsjDevice.getTrayCode()) && SsjDevice.getTraySize() != 0 && SsjDevice.getTaskNo() < 1000) { + + log.info("设备:{} 位置:{} 条码:{} 托盘大小:{}", SsjDevice.getDeviceCode(), "产线", SsjDevice.getTrayCode(), SsjDevice.getTraySize()); + //查wcs有没作业,有下发给输送机任务号,覆盖任务号 + MydoJob mydoJob = new MydoJob(); + mydoJob.setInOrOut("1"); + mydoJob.setStatus("1"); + mydoJob.setEntranceId("G1005"); + mydoJob.setExecutionPhase("0"); + List mydoJobs = mydoJobService.selectInOrOutMydoJobList(mydoJob); + if (!mydoJobs.isEmpty()) { + mydoJob = mydoJobs.get(0); + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(),mydoJob.getTaskNo(),1,1); + if (i == 1) { + mydoJobService.updateMydoJob(mydoJob); + log.info("G1005产线输送机下发任务号成功:" + mydoJob.getTaskNo()); + } + } else { + //没有,请求错误接口,找作业,返回成功,//拿到正常作业,插到wcs作业表,下发给输送机任务号,覆盖任务号,shibai... + HashMap paramMap = new HashMap<>(); + paramMap.put("size", SsjDevice.getTraySize()); + AjaxResult ajaxResult = apiService.selectMydoJobByNoTrayCode(paramMap); + if (ajaxResult.isSuccess()) { + MydoJob mydoJob2 = BeanUtil.toBean(ajaxResult.get("data"), MydoJob.class); + mydoJobService.insertMydoJob(mydoJob2); + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(),mydoJob2.getTaskNo(),1,1); + if (i == 1) { + mydoJob2.setExecutionPhase("1"); + mydoJobService.updateMydoJob(mydoJob2); + log.info("G1005产线输送机下发任务号成功:" + mydoJob2.getTaskNo()); + } + } else if (ajaxResult.isError()) { + //超大,给plc下发错误信息,等待人工处理 + //托盘大小对不上,给plc下发错误信息,等待人工处理 + //码不对,给plc下发错误信息,等待人工处理 + int i = writeToSsj(SsjDevice.getPlcDbWriteAddress(), "0", Integer.parseInt(ajaxResult.get("code").toString()), 2); + if (i == 1) { + log.info("G1005产线输送机下发错误信息成功"); + } + } + } + + } + + if (SsjDevice.getDw() == 1 && SsjDevice.getTaskNo() > 1000) { + + log.info("设备:{} 位置:{} 条码:{} 托盘大小:{}", SsjDevice.getDeviceCode(), "产线收到wcs任务号", SsjDevice.getTrayCode(), SsjDevice.getTraySize()); + //任务号查wcs作业,查巷道,看目标输送线是否空闲,查是否有出库作业,生成agv队列 + + MydoJob mydoJob = mydoJobService.selectMydoJobByTaskNo(Convert.toLong(SsjDevice.getTaskNo())); + String deviceCode = ""; + if (mydoJob != null) { + if ("1".equals(mydoJob.getRoadwayId())) { + deviceCode = "L1011"; + } else { + deviceCode = "L1012"; + } + int kx = deviceSsjMap.get(deviceCode).getKx(); + MydoJob mydoJobParam = new MydoJob(); + mydoJobParam.setInOut("2"); + mydoJobParam.setStatus("3"); + mydoJobParam.setExitId(deviceCode); + List outMydoJobList = mydoJobService.selectInOrOutMydoJobList(mydoJobParam); + + + if (outMydoJobList.isEmpty() && kx == 1) { + //查是否有队列,如果有了请求放货修改为允许放货 + AgvQueue agvQueue = agvQueueService.selectAgvQueueByTaskCode(mydoJob.getJobId()); + if (agvQueue != null){ + if ("3".equals(mydoJob.getStatus()) && "1".equals(agvQueue.getStatus())){ + AjaxResult ajaxResult = apiService.continueTask(agvQueue); + if (ajaxResult.isSuccess()) { + log.info("给agv下发允许取货继续任务成功"+agvQueue.getWmsTaskNo()); + agvQueue.setStatus("2"); + agvQueueService.updateAgvQueue(agvQueue); + } + } + }else { + if ("1".equals(mydoJob.getStatus())){ + HashMap paramMap = new HashMap<>(); + paramMap.put("trayCode", mydoJob.getTrayCode()); + paramMap.put("taskNo", mydoJob.getJobId()); + paramMap.put("startPoint", "G1005"); + paramMap.put("endPoint", deviceCode); + paramMap.put("traySize",String.valueOf(SsjDevice.getTraySize())); + paramMap.put("wmsTaskNo", mydoJob.getTaskNo()); + AjaxResult ajaxResult = apiService.receiveAgvMoveJob(paramMap); + if (ajaxResult.isSuccess()) { + mydoJob.setStatus("3"); + mydoJob.setExecutionPhase(TASK_EP_AGV); + mydoJobService.updateMydoJob(mydoJob); + } + } + } + } + + + } + + + } + + + } + + + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java new file mode 100644 index 0000000..d2d6e8c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java @@ -0,0 +1,94 @@ +package com.ruoyi.web.controller.common; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.FastByteArrayOutputStream; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.sign.Base64; +import com.ruoyi.common.utils.uuid.IdUtils; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 验证码操作处理 + * + * @author ruoyi + */ +@RestController +public class CaptchaController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysConfigService configService; + /** + * 生成验证码 + */ + @GetMapping("/captchaImage") + public AjaxResult getCode(HttpServletResponse response) throws IOException + { + AjaxResult ajax = AjaxResult.success(); + boolean captchaEnabled = configService.selectCaptchaEnabled(); + ajax.put("captchaEnabled", captchaEnabled); + if (!captchaEnabled) + { + return ajax; + } + + // 保存验证码信息 + String uuid = IdUtils.simpleUUID(); + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid; + + String capStr = null, code = null; + BufferedImage image = null; + + // 生成验证码 + String captchaType = RuoYiConfig.getCaptchaType(); + if ("math".equals(captchaType)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + image = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(captchaType)) + { + capStr = code = captchaProducer.createText(); + image = captchaProducer.createImage(capStr); + } + + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); + // 转换流信息写出 + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); + try + { + ImageIO.write(image, "jpg", os); + } + catch (IOException e) + { + return AjaxResult.error(e.getMessage()); + } + + ajax.put("uuid", uuid); + ajax.put("img", Base64.encode(os.toByteArray())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 0000000..cec5006 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,163 @@ +package com.ruoyi.web.controller.common; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.framework.config.ServerConfig; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/common") +public class CommonController +{ + private static final Logger log = LoggerFactory.getLogger(CommonController.class); + + @Autowired + private ServerConfig serverConfig; + + private static final String FILE_DELIMETER = ","; + + /** + * 通用下载请求 + * + * @param fileName 文件名称 + * @param delete 是否删除 + */ + @GetMapping("/download") + public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request) + { + try + { + if (!FileUtils.checkAllowDownload(fileName)) + { + throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName)); + } + String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1); + String filePath = RuoYiConfig.getDownloadPath() + fileName; + + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, realFileName); + FileUtils.writeBytes(filePath, response.getOutputStream()); + if (delete) + { + FileUtils.deleteFile(filePath); + } + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } + + /** + * 通用上传请求(单个) + */ + @PostMapping("/upload") + public AjaxResult uploadFile(MultipartFile file) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + AjaxResult ajax = AjaxResult.success(); + ajax.put("url", url); + ajax.put("fileName", fileName); + ajax.put("newFileName", FileUtils.getName(fileName)); + ajax.put("originalFilename", file.getOriginalFilename()); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/uploads") + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List urls = new ArrayList(); + List fileNames = new ArrayList(); + List newFileNames = new ArrayList(); + List originalFilenames = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + urls.add(url); + fileNames.add(fileName); + newFileNames.add(FileUtils.getName(fileName)); + originalFilenames.add(file.getOriginalFilename()); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER)); + ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/download/resource") + public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response) + throws Exception + { + try + { + if (!FileUtils.checkAllowDownload(resource)) + { + throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource)); + } + // 本地资源路径 + String localPath = RuoYiConfig.getProfile(); + // 数据库资源地址 + String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX); + // 下载名称 + String downloadName = StringUtils.substringAfterLast(downloadPath, "/"); + response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE); + FileUtils.setAttachmentResponseHeader(response, downloadName); + FileUtils.writeBytes(downloadPath, response.getOutputStream()); + } + catch (Exception e) + { + log.error("下载文件失败", e); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java new file mode 100644 index 0000000..c8c49c9 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,121 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisCallback; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysCache; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/cache") +public class CacheController +{ + @Autowired + private RedisTemplate redisTemplate; + + private final static List caches = new ArrayList(); + { + caches.add(new SysCache(CacheConstants.LOGIN_TOKEN_KEY, "用户信息")); + caches.add(new SysCache(CacheConstants.SYS_CONFIG_KEY, "配置信息")); + caches.add(new SysCache(CacheConstants.SYS_DICT_KEY, "数据字典")); + caches.add(new SysCache(CacheConstants.CAPTCHA_CODE_KEY, "验证码")); + caches.add(new SysCache(CacheConstants.REPEAT_SUBMIT_KEY, "防重提交")); + caches.add(new SysCache(CacheConstants.RATE_LIMIT_KEY, "限流处理")); + caches.add(new SysCache(CacheConstants.PWD_ERR_CNT_KEY, "密码错误次数")); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Properties info = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info()); + Properties commandStats = (Properties) redisTemplate.execute((RedisCallback) connection -> connection.info("commandstats")); + Object dbSize = redisTemplate.execute((RedisCallback) connection -> connection.dbSize()); + + Map result = new HashMap<>(3); + result.put("info", info); + result.put("dbSize", dbSize); + + List> pieList = new ArrayList<>(); + commandStats.stringPropertyNames().forEach(key -> { + Map data = new HashMap<>(2); + String property = commandStats.getProperty(key); + data.put("name", StringUtils.removeStart(key, "cmdstat_")); + data.put("value", StringUtils.substringBetween(property, "calls=", ",usec")); + pieList.add(data); + }); + result.put("commandStats", pieList); + return AjaxResult.success(result); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getNames") + public AjaxResult cache() + { + return AjaxResult.success(caches); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getKeys/{cacheName}") + public AjaxResult getCacheKeys(@PathVariable String cacheName) + { + Set cacheKeys = redisTemplate.keys(cacheName + "*"); + return AjaxResult.success(new TreeSet<>(cacheKeys)); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @GetMapping("/getValue/{cacheName}/{cacheKey}") + public AjaxResult getCacheValue(@PathVariable String cacheName, @PathVariable String cacheKey) + { + String cacheValue = redisTemplate.opsForValue().get(cacheKey); + SysCache sysCache = new SysCache(cacheName, cacheKey, cacheValue); + return AjaxResult.success(sysCache); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheName/{cacheName}") + public AjaxResult clearCacheName(@PathVariable String cacheName) + { + Collection cacheKeys = redisTemplate.keys(cacheName + "*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheKey/{cacheKey}") + public AjaxResult clearCacheKey(@PathVariable String cacheKey) + { + redisTemplate.delete(cacheKey); + return AjaxResult.success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:cache:list')") + @DeleteMapping("/clearCacheAll") + public AjaxResult clearCacheAll() + { + Collection cacheKeys = redisTemplate.keys("*"); + redisTemplate.delete(cacheKeys); + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java new file mode 100644 index 0000000..cc805ad --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,27 @@ +package com.ruoyi.web.controller.monitor; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/server") +public class ServerController +{ + @PreAuthorize("@ss.hasPermi('monitor:server:list')") + @GetMapping() + public AjaxResult getInfo() throws Exception + { + Server server = new Server(); + server.copyTo(); + return AjaxResult.success(server); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java new file mode 100644 index 0000000..e0175f4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,82 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPasswordService; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:list')") + @GetMapping("/list") + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:logininfor:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + util.exportExcel(response, list, "登录日志"); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{infoIds}") + public AjaxResult remove(@PathVariable Long[] infoIds) + { + return toAjax(logininforService.deleteLogininforByIds(infoIds)); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:remove')") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @PreAuthorize("@ss.hasPermi('monitor:logininfor:unlock')") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @GetMapping("/unlock/{userName}") + public AjaxResult unlock(@PathVariable("userName") String userName) + { + passwordService.clearLoginRecordCache(userName); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java new file mode 100644 index 0000000..6ca78cf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,69 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志记录 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + @Autowired + private ISysOperLogService operLogService; + + @PreAuthorize("@ss.hasPermi('monitor:operlog:list')") + @GetMapping("/list") + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('monitor:operlog:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + util.exportExcel(response, list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/{operIds}") + public AjaxResult remove(@PathVariable Long[] operIds) + { + return toAjax(operLogService.deleteOperLogByIds(operIds)); + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @PreAuthorize("@ss.hasPermi('monitor:operlog:remove')") + @DeleteMapping("/clean") + public AjaxResult clean() + { + operLogService.cleanOperLog(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java new file mode 100644 index 0000000..a442863 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,83 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private RedisCache redisCache; + + @PreAuthorize("@ss.hasPermi('monitor:online:list')") + @GetMapping("/list") + public TableDataInfo list(String ipaddr, String userName) + { + Collection keys = redisCache.keys(CacheConstants.LOGIN_TOKEN_KEY + "*"); + List userOnlineList = new ArrayList(); + for (String key : keys) + { + LoginUser user = redisCache.getCacheObject(key); + if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) + { + userOnlineList.add(userOnlineService.selectOnlineByInfo(ipaddr, userName, user)); + } + else if (StringUtils.isNotEmpty(ipaddr)) + { + userOnlineList.add(userOnlineService.selectOnlineByIpaddr(ipaddr, user)); + } + else if (StringUtils.isNotEmpty(userName) && StringUtils.isNotNull(user.getUser())) + { + userOnlineList.add(userOnlineService.selectOnlineByUserName(userName, user)); + } + else + { + userOnlineList.add(userOnlineService.loginUserToUserOnline(user)); + } + } + Collections.reverse(userOnlineList); + userOnlineList.removeAll(Collections.singleton(null)); + return getDataTable(userOnlineList); + } + + /** + * 强退用户 + */ + @PreAuthorize("@ss.hasPermi('monitor:online:forceLogout')") + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @DeleteMapping("/{tokenId}") + public AjaxResult forceLogout(@PathVariable String tokenId) + { + redisCache.deleteObject(CacheConstants.LOGIN_TOKEN_KEY + tokenId); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java new file mode 100644 index 0000000..ab4653d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,133 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + @Autowired + private ISysConfigService configService; + + /** + * 获取参数配置列表 + */ + @PreAuthorize("@ss.hasPermi('system:config:list')") + @GetMapping("/list") + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:config:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + util.exportExcel(response, list, "参数数据"); + } + + /** + * 根据参数编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:config:query')") + @GetMapping(value = "/{configId}") + public AjaxResult getInfo(@PathVariable Long configId) + { + return success(configService.selectConfigById(configId)); + } + + /** + * 根据参数键名查询参数值 + */ + @GetMapping(value = "/configKey/{configKey}") + public AjaxResult getConfigKey(@PathVariable String configKey) + { + return success(configService.selectConfigByKey(configKey)); + } + + /** + * 新增参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:add')") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getUsername()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:edit')") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysConfig config) + { + if (!configService.checkConfigKeyUnique(config)) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getUsername()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{configIds}") + public AjaxResult remove(@PathVariable Long[] configIds) + { + configService.deleteConfigByIds(configIds); + return success(); + } + + /** + * 刷新参数缓存 + */ + @PreAuthorize("@ss.hasPermi('system:config:remove')") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java new file mode 100644 index 0000000..59e7588 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,132 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + @Autowired + private ISysDeptService deptService; + + /** + * 获取部门列表 + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list") + public AjaxResult list(SysDept dept) + { + List depts = deptService.selectDeptList(dept); + return success(depts); + } + + /** + * 查询部门列表(排除节点) + */ + @PreAuthorize("@ss.hasPermi('system:dept:list')") + @GetMapping("/list/exclude/{deptId}") + public AjaxResult excludeChild(@PathVariable(value = "deptId", required = false) Long deptId) + { + List depts = deptService.selectDeptList(new SysDept()); + depts.removeIf(d -> d.getDeptId().intValue() == deptId || ArrayUtils.contains(StringUtils.split(d.getAncestors(), ","), deptId + "")); + return success(depts); + } + + /** + * 根据部门编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:dept:query')") + @GetMapping(value = "/{deptId}") + public AjaxResult getInfo(@PathVariable Long deptId) + { + deptService.checkDeptDataScope(deptId); + return success(deptService.selectDeptById(deptId)); + } + + /** + * 新增部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:add')") + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDept dept) + { + if (!deptService.checkDeptNameUnique(dept)) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getUsername()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:edit')") + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDept dept) + { + Long deptId = dept.getDeptId(); + deptService.checkDeptDataScope(deptId); + if (!deptService.checkDeptNameUnique(dept)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(deptId)) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0) + { + return error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getUsername()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除部门 + */ + @PreAuthorize("@ss.hasPermi('system:dept:remove')") + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deptId}") + public AjaxResult remove(@PathVariable Long deptId) + { + if (deptService.hasChildByDeptId(deptId)) + { + return warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return warn("部门存在用户,不允许删除"); + } + deptService.checkDeptDataScope(deptId); + return toAjax(deptService.deleteDeptById(deptId)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java new file mode 100644 index 0000000..59becaf --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,121 @@ +package com.ruoyi.web.controller.system; + +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + @Autowired + private ISysDictDataService dictDataService; + + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + util.exportExcel(response, list, "字典数据"); + } + + /** + * 查询字典数据详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictCode}") + public AjaxResult getInfo(@PathVariable Long dictCode) + { + return success(dictDataService.selectDictDataById(dictCode)); + } + + /** + * 根据字典类型查询字典数据信息 + */ + @GetMapping(value = "/type/{dictType}") + public AjaxResult dictType(@PathVariable String dictType) + { + List data = dictTypeService.selectDictDataByType(dictType); + if (StringUtils.isNull(data)) + { + data = new ArrayList(); + } + return success(data); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictData dict) + { + dict.setCreateBy(getUsername()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改保存字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictData dict) + { + dict.setUpdateBy(getUsername()); + return toAjax(dictDataService.updateDictData(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictCodes}") + public AjaxResult remove(@PathVariable Long[] dictCodes) + { + dictDataService.deleteDictDataByIds(dictCodes); + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java new file mode 100644 index 0000000..c53867c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,131 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/dict/type") +public class SysDictTypeController extends BaseController +{ + @Autowired + private ISysDictTypeService dictTypeService; + + @PreAuthorize("@ss.hasPermi('system:dict:list')") + @GetMapping("/list") + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:dict:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysDictType dictType) + { + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + util.exportExcel(response, list, "字典类型"); + } + + /** + * 查询字典类型详细 + */ + @PreAuthorize("@ss.hasPermi('system:dict:query')") + @GetMapping(value = "/{dictId}") + public AjaxResult getInfo(@PathVariable Long dictId) + { + return success(dictTypeService.selectDictTypeById(dictId)); + } + + /** + * 新增字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:add')") + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getUsername()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:edit')") + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysDictType dict) + { + if (!dictTypeService.checkDictTypeUnique(dict)) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getUsername()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + /** + * 删除字典类型 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{dictIds}") + public AjaxResult remove(@PathVariable Long[] dictIds) + { + dictTypeService.deleteDictTypeByIds(dictIds); + return success(); + } + + /** + * 刷新字典缓存 + */ + @PreAuthorize("@ss.hasPermi('system:dict:remove')") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @DeleteMapping("/refreshCache") + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 获取字典选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List dictTypes = dictTypeService.selectDictTypeAll(); + return success(dictTypes); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java new file mode 100644 index 0000000..13007eb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,29 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.StringUtils; + +/** + * 首页 + * + * @author ruoyi + */ +@RestController +public class SysIndexController +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** + * 访问首页,提示语 + */ + @RequestMapping("/") + public String index() + { + return StringUtils.format("欢迎使用{}后台管理框架,当前版本:v{},请通过前端地址访问。", ruoyiConfig.getName(), ruoyiConfig.getVersion()); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java new file mode 100644 index 0000000..39883a8 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -0,0 +1,104 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginBody; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.web.service.SysLoginService; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 登录验证 + * + * @author ruoyi + */ +@RestController +public class SysLoginController +{ + @Autowired + private SysLoginService loginService; + + @Autowired + private ISysMenuService menuService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private TokenService tokenService; + + /** + * 登录方法 + * + * @param loginBody 登录信息 + * @return 结果 + */ + @PostMapping("/login") + public AjaxResult login(@RequestBody LoginBody loginBody) + { + AjaxResult ajax = AjaxResult.success(); + // 生成令牌 + String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(), + loginBody.getUuid()); + ajax.put(Constants.TOKEN, token); + return ajax; + } + + /** + * 获取用户信息 + * + * @return 用户信息 + */ + @GetMapping("getInfo") + public AjaxResult getInfo() + { + LoginUser loginUser = SecurityUtils.getLoginUser(); + SysUser user = loginUser.getUser(); + // 角色集合 + Set roles = permissionService.getRolePermission(user); + // 权限集合 + Set permissions = permissionService.getMenuPermission(user); + if (!loginUser.getPermissions().equals(permissions)) + { + loginUser.setPermissions(permissions); + tokenService.refreshToken(loginUser); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("user", user); + ajax.put("roles", roles); + ajax.put("permissions", permissions); + return ajax; + } + + /** + * 获取路由信息 + * + * @return 路由信息 + */ + @GetMapping("getRouters") + public AjaxResult getRouters() + { + Long userId = SecurityUtils.getUserId(); + List menus = menuService.selectMenuTreeByUserId(userId); + return AjaxResult.success(menuService.buildMenus(menus)); + } + + @GetMapping("/changeLanguage") + public AjaxResult changeLanguage(String lang) + { + + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java new file mode 100644 index 0000000..03b6b65 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,142 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + /** + * 获取菜单列表 + */ + @PreAuthorize("@ss.hasPermi('system:menu:list')") + @GetMapping("/list") + public AjaxResult list(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menus); + } + + /** + * 根据菜单编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:menu:query')") + @GetMapping(value = "/{menuId}") + public AjaxResult getInfo(@PathVariable Long menuId) + { + return success(menuService.selectMenuById(menuId)); + } + + /** + * 获取菜单下拉树列表 + */ + @GetMapping("/treeselect") + public AjaxResult treeselect(SysMenu menu) + { + List menus = menuService.selectMenuList(menu, getUserId()); + return success(menuService.buildMenuTreeSelect(menus)); + } + + /** + * 加载对应角色菜单列表树 + */ + @GetMapping(value = "/roleMenuTreeselect/{roleId}") + public AjaxResult roleMenuTreeselect(@PathVariable("roleId") Long roleId) + { + List menus = menuService.selectMenuList(getUserId()); + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", menuService.selectMenuListByRoleId(roleId)); + ajax.put("menus", menuService.buildMenuTreeSelect(menus)); + return ajax; + } + + /** + * 新增菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:add')") + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + menu.setCreateBy(getUsername()); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:edit')") + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysMenu menu) + { + if (!menuService.checkMenuNameUnique(menu)) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + else if (UserConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,地址必须以http(s)://开头"); + } + else if (menu.getMenuId().equals(menu.getParentId())) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己"); + } + menu.setUpdateBy(getUsername()); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 删除菜单 + */ + @PreAuthorize("@ss.hasPermi('system:menu:remove')") + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{menuId}") + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.hasChildByMenuId(menuId)) + { + return warn("存在子菜单,不允许删除"); + } + if (menuService.checkMenuExistRole(menuId)) + { + return warn("菜单已分配,不允许删除"); + } + return toAjax(menuService.deleteMenuById(menuId)); + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java new file mode 100644 index 0000000..8622828 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,91 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + @Autowired + private ISysNoticeService noticeService; + + /** + * 获取通知公告列表 + */ + @PreAuthorize("@ss.hasPermi('system:notice:list')") + @GetMapping("/list") + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 根据通知公告编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:notice:query')") + @GetMapping(value = "/{noticeId}") + public AjaxResult getInfo(@PathVariable Long noticeId) + { + return success(noticeService.selectNoticeById(noticeId)); + } + + /** + * 新增通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:add')") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysNotice notice) + { + notice.setCreateBy(getUsername()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:edit')") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysNotice notice) + { + notice.setUpdateBy(getUsername()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除通知公告 + */ + @PreAuthorize("@ss.hasPermi('system:notice:remove')") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @DeleteMapping("/{noticeIds}") + public AjaxResult remove(@PathVariable Long[] noticeIds) + { + return toAjax(noticeService.deleteNoticeByIds(noticeIds)); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java new file mode 100644 index 0000000..c37a543 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,129 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + @Autowired + private ISysPostService postService; + + /** + * 获取岗位列表 + */ + @PreAuthorize("@ss.hasPermi('system:post:list')") + @GetMapping("/list") + public TableDataInfo list(SysPost post) + { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:post:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + util.exportExcel(response, list, "岗位数据"); + } + + /** + * 根据岗位编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:post:query')") + @GetMapping(value = "/{postId}") + public AjaxResult getInfo(@PathVariable Long postId) + { + return success(postService.selectPostById(postId)); + } + + /** + * 新增岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:add')") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getUsername()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:edit')") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysPost post) + { + if (!postService.checkPostNameUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (!postService.checkPostCodeUnique(post)) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getUsername()); + return toAjax(postService.updatePost(post)); + } + + /** + * 删除岗位 + */ + @PreAuthorize("@ss.hasPermi('system:post:remove')") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{postIds}") + public AjaxResult remove(@PathVariable Long[] postIds) + { + return toAjax(postService.deletePostByIds(postIds)); + } + + /** + * 获取岗位选择框列表 + */ + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + List posts = postService.selectPostAll(); + return success(posts); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java new file mode 100644 index 0000000..41b7621 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,140 @@ +package com.ruoyi.web.controller.system; + +import java.util.Map; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.common.utils.file.MimeTypeUtils; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private TokenService tokenService; + + /** + * 个人信息 + */ + @GetMapping + public AjaxResult profile() + { + LoginUser loginUser = getLoginUser(); + SysUser user = loginUser.getUser(); + AjaxResult ajax = AjaxResult.success(user); + ajax.put("roleGroup", userService.selectUserRoleGroup(loginUser.getUsername())); + ajax.put("postGroup", userService.selectUserPostGroup(loginUser.getUsername())); + return ajax; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult updateProfile(@RequestBody SysUser user) + { + LoginUser loginUser = getLoginUser(); + SysUser currentUser = loginUser.getUser(); + currentUser.setNickName(user.getNickName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,手机号码已存在"); + } + if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(currentUser)) + { + return error("修改用户'" + loginUser.getUsername() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserProfile(currentUser) > 0) + { + // 更新缓存用户信息 + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改个人信息异常,请联系管理员"); + } + + /** + * 重置密码 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PutMapping("/updatePwd") + public AjaxResult updatePwd(@RequestBody Map params) + { + String oldPassword = params.get("oldPassword"); + String newPassword = params.get("newPassword"); + LoginUser loginUser = getLoginUser(); + String userName = loginUser.getUsername(); + String password = loginUser.getPassword(); + if (!SecurityUtils.matchesPassword(oldPassword, password)) + { + return error("修改密码失败,旧密码错误"); + } + if (SecurityUtils.matchesPassword(newPassword, password)) + { + return error("新密码不能与旧密码相同"); + } + newPassword = SecurityUtils.encryptPassword(newPassword); + if (userService.resetUserPwd(userName, newPassword) > 0) + { + // 更新缓存用户密码 + loginUser.getUser().setPassword(newPassword); + tokenService.setLoginUser(loginUser); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 头像上传 + */ + @Log(title = "用户头像", businessType = BusinessType.UPDATE) + @PostMapping("/avatar") + public AjaxResult avatar(@RequestParam("avatarfile") MultipartFile file) throws Exception + { + if (!file.isEmpty()) + { + LoginUser loginUser = getLoginUser(); + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file, MimeTypeUtils.IMAGE_EXTENSION); + if (userService.updateUserAvatar(loginUser.getUsername(), avatar)) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("imgUrl", avatar); + // 更新缓存用户头像 + loginUser.getUser().setAvatar(avatar); + tokenService.setLoginUser(loginUser); + return ajax; + } + } + return error("上传图片异常,请联系管理员"); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java new file mode 100644 index 0000000..fe19249 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,38 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@RestController +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @PostMapping("/register") + public AjaxResult register(@RequestBody RegisterBody user) + { + if (!("true".equals(configService.selectConfigByKey("sys.account.registerUser")))) + { + return error("当前系统没有开启注册功能!"); + } + String msg = registerService.register(user); + return StringUtils.isEmpty(msg) ? success() : error(msg); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java new file mode 100644 index 0000000..42d9e8f --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,262 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.web.service.SysPermissionService; +import com.ruoyi.framework.web.service.TokenService; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private TokenService tokenService; + + @Autowired + private SysPermissionService permissionService; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysDeptService deptService; + + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/list") + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:role:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + util.exportExcel(response, list, "角色数据"); + } + + /** + * 根据角色编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/{roleId}") + public AjaxResult getInfo(@PathVariable Long roleId) + { + roleService.checkRoleDataScope(roleId); + return success(roleService.selectRoleById(roleId)); + } + + /** + * 新增角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:add')") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysRole role) + { + if (!roleService.checkRoleNameUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getUsername()); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改保存角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + if (!roleService.checkRoleNameUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (!roleService.checkRoleKeyUnique(role)) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getUsername()); + + if (roleService.updateRole(role) > 0) + { + // 更新缓存用户权限 + LoginUser loginUser = getLoginUser(); + if (StringUtils.isNotNull(loginUser.getUser()) && !loginUser.getUser().isAdmin()) + { + loginUser.setUser(userService.selectUserByUserName(loginUser.getUser().getUserName())); + loginUser.setPermissions(permissionService.getMenuPermission(loginUser.getUser())); + tokenService.setLoginUser(loginUser); + } + return success(); + } + return error("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); + } + + /** + * 修改保存数据权限 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/dataScope") + public AjaxResult dataScope(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + return toAjax(roleService.authDataScope(role)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysRole role) + { + roleService.checkRoleAllowed(role); + roleService.checkRoleDataScope(role.getRoleId()); + role.setUpdateBy(getUsername()); + return toAjax(roleService.updateRoleStatus(role)); + } + + /** + * 删除角色 + */ + @PreAuthorize("@ss.hasPermi('system:role:remove')") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{roleIds}") + public AjaxResult remove(@PathVariable Long[] roleIds) + { + return toAjax(roleService.deleteRoleByIds(roleIds)); + } + + /** + * 获取角色选择框列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping("/optionselect") + public AjaxResult optionselect() + { + return success(roleService.selectRoleAll()); + } + + /** + * 查询已分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/allocatedList") + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 查询未分配用户角色列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:list')") + @GetMapping("/authUser/unallocatedList") + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancel") + public AjaxResult cancelAuthUser(@RequestBody SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权用户 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/cancelAll") + public AjaxResult cancelAuthUserAll(Long roleId, Long[] userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 批量选择用户授权 + */ + @PreAuthorize("@ss.hasPermi('system:role:edit')") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PutMapping("/authUser/selectAll") + public AjaxResult selectAuthUserAll(Long roleId, Long[] userIds) + { + roleService.checkRoleDataScope(roleId); + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } + + /** + * 获取对应角色部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:role:query')") + @GetMapping(value = "/deptTree/{roleId}") + public AjaxResult deptTree(@PathVariable("roleId") Long roleId) + { + AjaxResult ajax = AjaxResult.success(); + ajax.put("checkedKeys", deptService.selectDeptListByRoleId(roleId)); + ajax.put("depts", deptService.selectDeptTreeList(new SysDept())); + return ajax; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java new file mode 100644 index 0000000..8a85b6a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,258 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysPostService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + private ISysPostService postService; + + /** + * 获取用户列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/list") + public TableDataInfo list(SysUser user) + { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @PreAuthorize("@ss.hasPermi('system:user:export')") + @PostMapping("/export") + public void export(HttpServletResponse response, SysUser user) + { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + util.exportExcel(response, list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @PreAuthorize("@ss.hasPermi('system:user:import')") + @PostMapping("/importData") + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = getUsername(); + String message = userService.importUser(userList, updateSupport, operName); + return success(message); + } + + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) + { + ExcelUtil util = new ExcelUtil(SysUser.class); + util.importTemplateExcel(response, "用户数据"); + } + + /** + * 根据用户编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping(value = { "/", "/{userId}" }) + public AjaxResult getInfo(@PathVariable(value = "userId", required = false) Long userId) + { + AjaxResult ajax = AjaxResult.success(); + if (StringUtils.isNotNull(userId)) + { + userService.checkUserDataScope(userId); + SysUser sysUser = userService.selectUserById(userId); + ajax.put(AjaxResult.DATA_TAG, sysUser); + ajax.put("postIds", postService.selectPostListByUserId(userId)); + ajax.put("roleIds", sysUser.getRoles().stream().map(SysRole::getRoleId).collect(Collectors.toList())); + } + List roles = roleService.selectRoleAll(); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + ajax.put("posts", postService.selectPostAll()); + return ajax; + } + + /** + * 新增用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:add')") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@Validated @RequestBody SysUser user) + { + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); + if (!userService.checkUserNameUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("新增用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setCreateBy(getUsername()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@Validated @RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + roleService.checkRoleDataScope(user.getRoleIds()); + if (!userService.checkUserNameUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) && !userService.checkPhoneUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) && !userService.checkEmailUnique(user)) + { + return error("修改用户'" + user.getUserName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUser(user)); + } + + /** + * 删除用户 + */ + @PreAuthorize("@ss.hasPermi('system:user:remove')") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{userIds}") + public AjaxResult remove(@PathVariable Long[] userIds) + { + if (ArrayUtils.contains(userIds, getUserId())) + { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(userIds)); + } + + /** + * 重置密码 + */ + @PreAuthorize("@ss.hasPermi('system:user:resetPwd')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/resetPwd") + public AjaxResult resetPwd(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setPassword(SecurityUtils.encryptPassword(user.getPassword())); + user.setUpdateBy(getUsername()); + return toAjax(userService.resetPwd(user)); + } + + /** + * 状态修改 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysUser user) + { + userService.checkUserAllowed(user); + userService.checkUserDataScope(user.getUserId()); + user.setUpdateBy(getUsername()); + return toAjax(userService.updateUserStatus(user)); + } + + /** + * 根据用户编号获取授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:query')") + @GetMapping("/authRole/{userId}") + public AjaxResult authRole(@PathVariable("userId") Long userId) + { + AjaxResult ajax = AjaxResult.success(); + SysUser user = userService.selectUserById(userId); + List roles = roleService.selectRolesByUserId(userId); + ajax.put("user", user); + ajax.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return ajax; + } + + /** + * 用户授权角色 + */ + @PreAuthorize("@ss.hasPermi('system:user:edit')") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PutMapping("/authRole") + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.checkUserDataScope(userId); + roleService.checkRoleDataScope(roleIds); + userService.insertUserAuth(userId, roleIds); + return success(); + } + + /** + * 获取部门树列表 + */ + @PreAuthorize("@ss.hasPermi('system:user:list')") + @GetMapping("/deptTree") + public AjaxResult deptTree(SysDept dept) + { + return success(deptService.selectDeptTreeList(dept)); + } + + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java new file mode 100644 index 0000000..b4f6bac --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,183 @@ +package com.ruoyi.web.controller.tool; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.annotations.ApiOperation; + +/** + * swagger 用户测试方法 + * + * @author ruoyi + */ +@Api("用户信息管理") +@RestController +@RequestMapping("/test/user") +public class TestController extends BaseController +{ + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserEntity(1, "admin", "admin123", "15888888888")); + users.put(2, new UserEntity(2, "ry", "admin123", "15666666666")); + } + + @ApiOperation("获取用户列表") + @GetMapping("/list") + public R> userList() + { + List userList = new ArrayList(users.values()); + return R.ok(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @GetMapping("/{userId}") + public R getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return R.ok(users.get(userId)); + } + else + { + return R.fail("用户不存在"); + } + } + + @ApiOperation("新增用户") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id", dataType = "Integer", dataTypeClass = Integer.class), + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "mobile", value = "用户手机", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("/save") + public R save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("更新用户") + @PutMapping("/update") + public R update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return R.fail("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return R.fail("用户不存在"); + } + users.remove(user.getUserId()); + users.put(user.getUserId(), user); + return R.ok(); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public R delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return R.ok(); + } + else + { + return R.fail("用户不存在"); + } + } +} + +@ApiModel(value = "UserEntity", description = "用户实体") +class UserEntity +{ + @ApiModelProperty("用户ID") + private Integer userId; + + @ApiModelProperty("用户名称") + private String username; + + @ApiModelProperty("用户密码") + private String password; + + @ApiModelProperty("用户手机") + private String mobile; + + public UserEntity() + { + + } + + public UserEntity(Integer userId, String username, String password, String mobile) + { + this.userId = userId; + this.username = username; + this.password = password; + this.mobile = mobile; + } + + public Integer getUserId() + { + return userId; + } + + public void setUserId(Integer userId) + { + this.userId = userId; + } + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getMobile() + { + return mobile; + } + + public void setMobile(String mobile) + { + this.mobile = mobile; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java new file mode 100644 index 0000000..ae1c3ec --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,125 @@ +package com.ruoyi.web.core.config; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.config.RuoYiConfig; +import io.swagger.annotations.ApiOperation; +import io.swagger.models.auth.In; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.ApiKey; +import springfox.documentation.service.AuthorizationScope; +import springfox.documentation.service.Contact; +import springfox.documentation.service.SecurityReference; +import springfox.documentation.service.SecurityScheme; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spi.service.contexts.SecurityContext; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 系统基础配置 */ + @Autowired + private RuoYiConfig ruoyiConfig; + + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** 设置请求的统一前缀 */ + @Value("${swagger.pathMapping}") + private String pathMapping; + + /** + * 创建API + */ + @Bean + public Docket createRestApi() + { + return new Docket(DocumentationType.OAS_30) + // 是否启用Swagger + .enable(enabled) + // 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息) + .apiInfo(apiInfo()) + // 设置哪些接口暴露给Swagger展示 + .select() + // 扫描所有有注解的api,用这种方式更灵活 + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + // 扫描指定包中的swagger注解 + // .apis(RequestHandlerSelectors.basePackage("com.ruoyi.project.tool.swagger")) + // 扫描所有 .apis(RequestHandlerSelectors.any()) + .paths(PathSelectors.any()) + .build() + /* 设置安全模式,swagger可以设置访问token */ + .securitySchemes(securitySchemes()) + .securityContexts(securityContexts()) + .pathMapping(pathMapping); + } + + /** + * 安全模式,这里指定token通过Authorization头请求头传递 + */ + private List securitySchemes() + { + List apiKeyList = new ArrayList(); + apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue())); + return apiKeyList; + } + + /** + * 安全上下文 + */ + private List securityContexts() + { + List securityContexts = new ArrayList<>(); + securityContexts.add( + SecurityContext.builder() + .securityReferences(defaultAuth()) + .operationSelector(o -> o.requestMappingPattern().matches("/.*")) + .build()); + return securityContexts; + } + + /** + * 默认的安全上引用 + */ + private List defaultAuth() + { + AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); + AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; + authorizationScopes[0] = authorizationScope; + List securityReferences = new ArrayList<>(); + securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); + return securityReferences; + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("标题:若依管理系统_接口文档") + // 描述 + .description("描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...") + // 作者信息 + .contact(new Contact(ruoyiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + ruoyiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties new file mode 100644 index 0000000..37e7b58 --- /dev/null +++ b/ruoyi-admin/src/main/resources/META-INF/spring-devtools.properties @@ -0,0 +1 @@ +restart.include.json=/com.alibaba.fastjson2.*.jar \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/application-druid.yml b/ruoyi-admin/src/main/resources/application-druid.yml new file mode 100644 index 0000000..ec5d406 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application-druid.yml @@ -0,0 +1,61 @@ +# 数据源配置 +spring: + datasource: + type: com.alibaba.druid.pool.DruidDataSource + driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver + druid: + #主数据信息 + master: + url: jdbc:sqlserver://172.16.2.170:1433;databaseName=FT517-WCS; + username: sa + password: Flrd0914. + # 从库数据源 + slave: + # 从数据源开关/默认关闭 + enabled: false + url: + username: + password: + # 初始连接数 + initialSize: 5 + # 最小连接池数量 + minIdle: 10 + # 最大连接池数量 + maxActive: 20 + # 配置获取连接等待超时的时间 + maxWait: 60000 + # 配置连接超时时间 + connectTimeout: 30000 + # 配置网络超时时间 + socketTimeout: 60000 + # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 + timeBetweenEvictionRunsMillis: 60000 + # 配置一个连接在池中最小生存的时间,单位是毫秒 + minEvictableIdleTimeMillis: 300000 + # 配置一个连接在池中最大生存的时间,单位是毫秒 + maxEvictableIdleTimeMillis: 900000 + # 配置检测连接是否有效 + validationQuery: SELECT 1 FROM sys_oper_log + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + webStatFilter: + enabled: true + statViewServlet: + enabled: true + # 设置白名单,不填则允许所有访问 + allow: + url-pattern: /druid/* + # 控制台管理用户名和密码 + login-username: ruoyi + login-password: 123456 + filter: + stat: + enabled: true + # 慢SQL记录 + log-slow-sql: true + slow-sql-millis: 1000 + merge-sql: true + wall: + config: + multi-statement-allow: true diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 0000000..5d450d9 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,129 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 3.8.9 + # 版权年份 + copyrightYear: 2025 + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: D:/ruoyi/uploadPath + # 获取ip地址开关 + addressEnabled: false + # 验证码类型 math 数字计算 char 字符验证 + captchaType: math + +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为8080 + port: 8081 + servlet: + # 应用的访问路径 + context-path: / + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # 连接数满后的排队数,默认为100 + accept-count: 1000 + threads: + # tomcat最大线程数,默认为200 + max: 800 + # Tomcat启动初始化的线程数,默认值10 + min-spare: 100 + +# 日志配置 +logging: + level: + com.ruoyi: info + org.springframework: warn + +# 用户配置 +user: + password: + # 密码最大错误次数 + maxRetryCount: 5 + # 密码锁定时间(默认10分钟) + lockTime: 10 + +# Spring配置 +spring: + # 资源信息 + messages: + # 国际化资源文件路径 + basename: i18n/messages + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + # redis 配置 + redis: + # 地址 + host: 172.16.2.170 + # 端口,默认为6379 + port: 6379 + # 数据库索引 + database: 1 + # 密码 + password: 123456 + # 连接超时时间 + timeout: 10s + lettuce: + pool: + # 连接池中的最小空闲连接 + min-idle: 0 + # 连接池中的最大空闲连接 + max-idle: 8 + # 连接池的最大数据库连接数 + max-active: 8 + # #连接池最大阻塞等待时间(使用负值表示没有限制) + max-wait: -1ms + +# token配置 +token: + # 令牌自定义标识 + header: Authorization + # 令牌密钥 + secret: abcdefghijklmnopqrstuvwxyz + # 令牌有效期(默认30分钟) + expireTime: 30 + +# MyBatis配置 +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: sqlserver + supportMethodsArguments: true + params: count=countSql + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + # 请求前缀 + pathMapping: /dev-api + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 0000000..0931cb8 --- /dev/null +++ b/ruoyi-admin/src/main/resources/banner.txt @@ -0,0 +1,24 @@ +Application Version: ${ruoyi.version} +Spring Boot Version: ${spring-boot.version} +//////////////////////////////////////////////////////////////////// +// _ooOoo_ // +// o8888888o // +// 88" . "88 // +// (| ^_^ |) // +// O\ = /O // +// ____/`---'\____ // +// .' \\| |// `. // +// / \\||| : |||// \ // +// / _||||| -:- |||||- \ // +// | | \\\ - /// | | // +// | \_| ''\---/'' | | // +// \ .-\__ `-` ___/-. / // +// ___`. .' /--.--\ `. . ___ // +// ."" '< `.___\_<|>_/___.' >'"". // +// | | : `- \`.;`\ _ /`;.`/ - ` : | | // +// \ \ `-. \_ __\ /__ _/ .-` / / // +// ========`-.____`-.___\_____/___.-`____.-'======== // +// `=---=' // +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ // +// 佛祖保佑 永不宕机 永无BUG // +//////////////////////////////////////////////////////////////////// \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/i18n/messages.properties b/ruoyi-admin/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..1b49085 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages.properties @@ -0,0 +1,38 @@ +#???? +not.null=* ???? +user.jcaptcha.error=????? +user.jcaptcha.expire=?????? +user.not.exists=?????/???? +user.password.not.match=?????/???? +user.password.retry.limit.count=??????{0}? +user.password.retry.limit.exceed=??????{0}??????{1}?? +user.password.delete=???????????? +user.blocked=???????????? +role.blocked=???????????? +login.blocked=??????IP????????? +user.logout.success=???? + +length.not.valid=?????{min}?{max}????? + +user.username.not.valid=* 2?20????????????????????????? +user.password.not.valid=* 5-50??? + +user.email.not.valid=?????? +user.mobile.phone.number.not.valid=??????? +user.login.success=???? +user.register.success=???? +user.notfound=????? +user.forcelogout=????????????? +user.unknown.error=?????????? + +##?????? +upload.exceed.maxSize=?????????????????
???????????{0}MB? +upload.filename.exceed.length=????????{0}??? + +##?? +no.permission=??????????????????? [{0}] +no.create.permission=????????????????????? [{0}] +no.update.permission=????????????????????? [{0}] +no.delete.permission=????????????????????? [{0}] +no.export.permission=????????????????????? [{0}] +no.view.permission=????????????????????? [{0}] diff --git a/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..1b49085 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,38 @@ +#???? +not.null=* ???? +user.jcaptcha.error=????? +user.jcaptcha.expire=?????? +user.not.exists=?????/???? +user.password.not.match=?????/???? +user.password.retry.limit.count=??????{0}? +user.password.retry.limit.exceed=??????{0}??????{1}?? +user.password.delete=???????????? +user.blocked=???????????? +role.blocked=???????????? +login.blocked=??????IP????????? +user.logout.success=???? + +length.not.valid=?????{min}?{max}????? + +user.username.not.valid=* 2?20????????????????????????? +user.password.not.valid=* 5-50??? + +user.email.not.valid=?????? +user.mobile.phone.number.not.valid=??????? +user.login.success=???? +user.register.success=???? +user.notfound=????? +user.forcelogout=????????????? +user.unknown.error=?????????? + +##?????? +upload.exceed.maxSize=?????????????????
???????????{0}MB? +upload.filename.exceed.length=????????{0}??? + +##?? +no.permission=??????????????????? [{0}] +no.create.permission=????????????????????? [{0}] +no.update.permission=????????????????????? [{0}] +no.delete.permission=????????????????????? [{0}] +no.export.permission=????????????????????? [{0}] +no.view.permission=????????????????????? [{0}] diff --git a/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..1b49085 --- /dev/null +++ b/ruoyi-admin/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,38 @@ +#???? +not.null=* ???? +user.jcaptcha.error=????? +user.jcaptcha.expire=?????? +user.not.exists=?????/???? +user.password.not.match=?????/???? +user.password.retry.limit.count=??????{0}? +user.password.retry.limit.exceed=??????{0}??????{1}?? +user.password.delete=???????????? +user.blocked=???????????? +role.blocked=???????????? +login.blocked=??????IP????????? +user.logout.success=???? + +length.not.valid=?????{min}?{max}????? + +user.username.not.valid=* 2?20????????????????????????? +user.password.not.valid=* 5-50??? + +user.email.not.valid=?????? +user.mobile.phone.number.not.valid=??????? +user.login.success=???? +user.register.success=???? +user.notfound=????? +user.forcelogout=????????????? +user.unknown.error=?????????? + +##?????? +upload.exceed.maxSize=?????????????????
???????????{0}MB? +upload.filename.exceed.length=????????{0}??? + +##?? +no.permission=??????????????????? [{0}] +no.create.permission=????????????????????? [{0}] +no.update.permission=????????????????????? [{0}] +no.delete.permission=????????????????????? [{0}] +no.export.permission=????????????????????? [{0}] +no.view.permission=????????????????????? [{0}] diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml new file mode 100644 index 0000000..a360583 --- /dev/null +++ b/ruoyi-admin/src/main/resources/logback.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + ${log.pattern} + + + + + + ${log.path}/sys-info.log + + + + ${log.path}/sys-info.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + INFO + + ACCEPT + + DENY + + + + + ${log.path}/sys-error.log + + + + ${log.path}/sys-error.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + ERROR + + ACCEPT + + DENY + + + + + + ${log.path}/sys-user.log + + + ${log.path}/sys-user.%d{yyyy-MM-dd}.log + + 60 + + + ${log.pattern} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mapper/agv/AgvQueueFinishMapper.xml b/ruoyi-admin/src/main/resources/mapper/agv/AgvQueueFinishMapper.xml new file mode 100644 index 0000000..74818a1 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/agv/AgvQueueFinishMapper.xml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + select req_code, req_time, position_code_1, position_code_2, task_type, ctnr_type, ctnr_code, ctnr_num, task_mode, priority, agv_type, request, finish_time, wms_traycode, wms_task_no, task_code, status, wms_task_type from agv_queue_finish + + + + + + + + insert into agv_queue_finish + + req_code, + req_time, + position_code_1, + position_code_2, + task_type, + ctnr_type, + ctnr_code, + ctnr_num, + task_mode, + priority, + agv_type, + request, + finish_time, + wms_traycode, + wms_task_no, + task_code, + status, + wms_task_type, + + + #{reqCode}, + #{reqTime}, + #{positionCode1}, + #{positionCode2}, + #{taskType}, + #{ctnrType}, + #{ctnrCode}, + #{ctnrNum}, + #{taskMode}, + #{priority}, + #{agvType}, + #{request}, + #{finishTime}, + #{wmsTraycode}, + #{wmsTaskNo}, + #{taskCode}, + #{status}, + #{wmsTaskType}, + + + + + update agv_queue_finish + + req_time = #{reqTime}, + position_code_1 = #{positionCode1}, + position_code_2 = #{positionCode2}, + task_type = #{taskType}, + ctnr_type = #{ctnrType}, + ctnr_code = #{ctnrCode}, + ctnr_num = #{ctnrNum}, + task_mode = #{taskMode}, + priority = #{priority}, + agv_type = #{agvType}, + request = #{request}, + finish_time = #{finishTime}, + wms_traycode = #{wmsTraycode}, + wms_task_no = #{wmsTaskNo}, + task_code = #{taskCode}, + status = #{status}, + wms_task_type = #{wmsTaskType}, + + where req_code = #{reqCode} + + + + delete from agv_queue_finish where req_code = #{reqCode} + + + + delete from agv_queue_finish where req_code in + + #{reqCode} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/bas/BasCargoSpaceMapper.xml b/ruoyi-admin/src/main/resources/mapper/bas/BasCargoSpaceMapper.xml new file mode 100644 index 0000000..e7f0503 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/bas/BasCargoSpaceMapper.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select cargo_space_id, warehouse_id, roadway_id, cargo_area_id, the_type, the_type_name, cargo_space_name, the_platoon, the_column, the_floor, status, locked, tray_code, cargo_hight, iss_double, group_coding_no, group_coding, cargo_space_logo, this_cargo_area, wms_cargo_space_id from bas_cargo_space + + + + + + + + + insert into bas_cargo_space + + cargo_space_id, + warehouse_id, + roadway_id, + cargo_area_id, + the_type, + the_type_name, + cargo_space_name, + the_platoon, + the_column, + the_floor, + status, + locked, + tray_code, + cargo_hight, + iss_double, + group_coding_no, + group_coding, + cargo_space_logo, + this_cargo_area, + wms_cargo_space_id, + + + #{cargoSpaceId}, + #{warehouseId}, + #{roadwayId}, + #{cargoAreaId}, + #{theType}, + #{theTypeName}, + #{cargoSpaceName}, + #{thePlatoon}, + #{theColumn}, + #{theFloor}, + #{status}, + #{locked}, + #{trayCode}, + #{cargoHight}, + #{issDouble}, + #{groupCodingNo}, + #{groupCoding}, + #{cargoSpaceLogo}, + #{thisCargoArea}, + #{wmsCargoSpaceId}, + + + + + update bas_cargo_space + + warehouse_id = #{warehouseId}, + roadway_id = #{roadwayId}, + cargo_area_id = #{cargoAreaId}, + the_type = #{theType}, + the_type_name = #{theTypeName}, + cargo_space_name = #{cargoSpaceName}, + the_platoon = #{thePlatoon}, + the_column = #{theColumn}, + the_floor = #{theFloor}, + status = #{status}, + locked = #{locked}, + tray_code = #{trayCode}, + cargo_hight = #{cargoHight}, + iss_double = #{issDouble}, + group_coding_no = #{groupCodingNo}, + group_coding = #{groupCoding}, + cargo_space_logo = #{cargoSpaceLogo}, + this_cargo_area = #{thisCargoArea}, + wms_cargo_space_id = #{wmsCargoSpaceId}, + + where cargo_space_id = #{cargoSpaceId} + + + + delete from bas_cargo_space where cargo_space_id = #{cargoSpaceId} + + + + delete from bas_cargo_space where cargo_space_id in + + #{cargoSpaceId} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/bas/IDynamicMonitoringService.xml b/ruoyi-admin/src/main/resources/mapper/bas/IDynamicMonitoringService.xml new file mode 100644 index 0000000..3dc6be6 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/bas/IDynamicMonitoringService.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + select id, location_name, type, frame_id, box_num, tray_code, group_id, allot_num from bas_fluent_frame + + + + + + + + + insert into bas_fluent_frame + + location_name, + type, + frame_id, + box_num, + tray_code, + group_id, + allot_num, + + + #{locationName}, + #{type}, + #{frameId}, + #{boxNum}, + #{trayCode}, + #{groupId}, + #{allotNum}, + + + + + update bas_fluent_frame + + location_name = #{locationName}, + type = #{type}, + frame_id = #{frameId}, + box_num = #{boxNum}, + tray_code = #{trayCode}, + group_id = #{groupId}, + allot_num = #{allotNum}, + + where id = #{id} + + + + delete from bas_fluent_frame where id = #{id} + + + + delete from bas_fluent_frame where id in + + #{id} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/bas/WcsPortMapper.xml b/ruoyi-admin/src/main/resources/mapper/bas/WcsPortMapper.xml new file mode 100644 index 0000000..f8f8ac6 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/bas/WcsPortMapper.xml @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + select id, port_code, port_name, port_type, port_region, spare1, spare2, spare3, status from wcs_port + + + + + + + + + insert into wcs_port + + port_code, + port_name, + port_type, + port_region, + spare1, + spare2, + spare3, + status, + + + #{portCode}, + #{portName}, + #{portType}, + #{portRegion}, + #{spare1}, + #{spare2}, + #{spare3}, + #{status}, + + + + + update wcs_port + + port_code = #{portCode}, + port_name = #{portName}, + port_type = #{portType}, + port_region = #{portRegion}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + status = #{status}, + + where id = #{id} + + + + delete from wcs_port where id = #{id} + + + + delete from wcs_port where id in + + #{id} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/device/AgvQueueMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/AgvQueueMapper.xml new file mode 100644 index 0000000..a362e8b --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/device/AgvQueueMapper.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + select req_code, req_time, position_code_1, position_code_2, task_type, ctnr_type, ctnr_code, ctnr_num, task_mode, priority, agv_type, request, finish_time, wms_traycode, wms_task_no,status,task_code,wms_task_type from agv_queue + + + + + + + + + insert into agv_queue + + req_code, + req_time, + position_code_1, + position_code_2, + task_type, + ctnr_type, + ctnr_code, + ctnr_num, + task_mode, + priority, + agv_type, + request, + finish_time, + wms_traycode, + wms_task_no, + status, + task_code, + wms_task_type, + + + #{reqCode}, + #{reqTime}, + #{positionCode1}, + #{positionCode2}, + #{taskType}, + #{ctnrType}, + #{ctnrCode}, + #{ctnrNum}, + #{taskMode}, + #{priority}, + #{agvType}, + #{request}, + #{finishTime}, + #{wmsTraycode}, + #{wmsTaskNo}, + #{status}, + #{taskCode}, + #{wmsTaskType}, + + + + insert into agv_queue_finish + select * + from agv_queue + where req_code = #{reqCode} + + + + update agv_queue + + req_time = #{reqTime}, + position_code_1 = #{positionCode1}, + position_code_2 = #{positionCode2}, + task_type = #{taskType}, + ctnr_type = #{ctnrType}, + ctnr_code = #{ctnrCode}, + ctnr_num = #{ctnrNum}, + task_mode = #{taskMode}, + priority = #{priority}, + agv_type = #{agvType}, + request = #{request}, + finish_time = #{finishTime}, + wms_traycode = #{wmsTraycode}, + wms_task_no = #{wmsTaskNo}, + status = #{status}, + + where req_code = #{reqCode} + + + + delete from agv_queue where req_code = #{reqCode} + + + + delete from agv_queue where req_code in + + #{reqCode} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/device/DeviceCarMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/DeviceCarMapper.xml new file mode 100644 index 0000000..9a5c3f4 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/device/DeviceCarMapper.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + select id, device_code , name , type, ip_address, buffer_area_id , is_active, create_time, status, spare1, spare2, spare3, spare4, task_no, zxjd from device_car + + + + + + + + insert into device_car + + device_code , + name , + type, + ip_address, + buffer_area_id , + is_active, + create_time, + status, + spare1, + spare2, + spare3, + spare4, + task_no, + zxjd, + + + #{deviceCode }, + #{name }, + #{type}, + #{ipAddress}, + #{bufferAreaId }, + #{isActive}, + #{createTime}, + #{status}, + #{spare1}, + #{spare2}, + #{spare3}, + #{spare4}, + #{taskNo}, + #{zxjd}, + + + + + update device_car + + device_code = #{deviceCode }, + name = #{name }, + type = #{type}, + ip_address = #{ipAddress}, + buffer_area_id = #{bufferAreaId }, + is_active = #{isActive}, + create_time = #{createTime}, + status = #{status}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + spare4 = #{spare4}, + task_no = #{taskNo}, + zxjd = #{zxjd}, + + where id = #{id} + + + + delete from device_car where id = #{id} + + + + delete from device_car where id in + + #{id} + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mapper/device/DeviceSsjMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/DeviceSsjMapper.xml new file mode 100644 index 0000000..ab6e231 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/device/DeviceSsjMapper.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select id, device_code , name , type, ip_address, plc_db_read_address, is_active, create_time, plc_db_write_address, rkqq, kx, dw, tray_code, task_no,exception,tray_size,allow_put from device_ssj + + + + + + + + insert into device_ssj + + device_code , + name , + type, + ip_address, + plc_db_read_address, + is_active, + create_time, + plc_db_write_address, + rkqq, + kx, + dw, + tray_code, + task_no, + + + #{deviceCode }, + #{name }, + #{type}, + #{ipAddress}, + #{plcDbReadAddress}, + #{isActive}, + #{createTime}, + #{plcDbWriteAddress}, + #{rkqq}, + #{kx}, + #{dw}, + #{trayCode}, + #{taskNo}, + + + + + update device_ssj + + rkqq = #{rkqq}, + kx = #{kx}, + dw = #{dw}, + tray_code = #{trayCode}, + task_no = #{taskNo}, + exception = #{exception}, + tray_size = #{traySize}, + allow_put = #{allowPut}, + + where id = #{id} + + + + delete from device_ssj where id = #{id} + + + + delete from device_ssj where id in + + #{id} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/device/MsgDdjMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/MsgDdjMapper.xml new file mode 100644 index 0000000..dbc474a --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/device/MsgDdjMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, name, rwh, jdbz, sbzt, czfs, yxxf, yph, ylh, ych, mbph, mblh, mbch, xtxh, dqxd, hczt, spare1, spare2, spare3 from msg_ddj + + + + + + + + insert into msg_ddj + + id, + name, + rwh, + jdbz, + sbzt, + czfs, + yxxf, + yph, + ylh, + ych, + mbph, + mblh, + mbch, + xtxh, + dqxd, + hczt, + spare1, + spare2, + spare3, + + + #{id}, + #{name}, + #{rwh}, + #{jdbz}, + #{sbzt}, + #{czfs}, + #{yxxf}, + #{yph}, + #{ylh}, + #{ych}, + #{mbph}, + #{mblh}, + #{mbch}, + #{xtxh}, + #{dqxd}, + #{hczt}, + #{spare1}, + #{spare2}, + #{spare3}, + + + + + update msg_ddj + + name = #{name}, + rwh = #{rwh}, + jdbz = #{jdbz}, + sbzt = #{sbzt}, + czfs = #{czfs}, + yxxf = #{yxxf}, + yph = #{yph}, + ylh = #{ylh}, + ych = #{ych}, + mbph = #{mbph}, + mblh = #{mblh}, + mbch = #{mbch}, + xtxh = #{xtxh}, + dqxd = #{dqxd}, + hczt = #{hczt}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + + where id = #{id} + + + + delete from msg_ddj where id = #{id} + + + + delete from msg_ddj where id in + + #{id} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/device/RequestLogMapper.xml b/ruoyi-admin/src/main/resources/mapper/device/RequestLogMapper.xml new file mode 100644 index 0000000..8f845d5 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/device/RequestLogMapper.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + select request_id, request_time, source_system, target_system, endpoint, request_url, request_method, request_headers, request_body, response_time, response_body, response_status, error_details, duration, Is_successful from request_log + + + + + + + + insert into request_log + + request_id, + request_time, + source_system, + target_system, + endpoint, + request_url, + request_method, + request_headers, + request_body, + response_time, + response_body, + response_status, + error_details, + duration, + Is_successful, + + + #{requestId}, + #{requestTime}, + #{sourceSystem}, + #{targetSystem}, + #{endpoint}, + #{requestUrl}, + #{requestMethod}, + #{requestHeaders}, + #{requestBody}, + #{responseTime}, + #{responseBody}, + #{responseStatus}, + #{errorDetails}, + #{duration}, + #{isSuccessful}, + + + + + update request_log + + request_time = #{requestTime}, + source_system = #{sourceSystem}, + target_system = #{targetSystem}, + endpoint = #{endpoint}, + request_url = #{requestUrl}, + request_method = #{requestMethod}, + request_headers = #{requestHeaders}, + request_body = #{requestBody}, + response_time = #{responseTime}, + response_body = #{responseBody}, + response_status = #{responseStatus}, + error_details = #{errorDetails}, + duration = #{duration}, + Is_successful = #{isSuccessful}, + + where request_id = #{requestId} + + + + delete from request_log where request_id = #{requestId} + + + + delete from request_log where request_id in + + #{requestId} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/job/JobCallBackMapper.xml b/ruoyi-admin/src/main/resources/mapper/job/JobCallBackMapper.xml new file mode 100644 index 0000000..4023f7d --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/job/JobCallBackMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + select id, type, task_no, mydojob_id, status, create_time, spare1, spare2, spare3 from job_call_back + + + + + + + + + insert into job_call_back + + type, + task_no, + mydojob_id, + status, + create_time, + spare1, + spare2, + spare3, + + + #{type}, + #{taskNo}, + #{mydojobId}, + #{status}, + #{createTime}, + #{spare1}, + #{spare2}, + #{spare3}, + + + + + update job_call_back + + type = #{type}, + task_no = #{taskNo}, + mydojob_id = #{mydojobId}, + status = #{status}, + create_time = #{createTime}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + + where id = #{id} + + + + delete from job_call_back where id = #{id} + + + + delete from job_call_back where id in + + #{id} + + + diff --git a/ruoyi-admin/src/main/resources/mapper/job/MydoJobMapper.xml b/ruoyi-admin/src/main/resources/mapper/job/MydoJobMapper.xml new file mode 100644 index 0000000..eca6b61 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/job/MydoJobMapper.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select job_id, status, task_no, roadway_id, cargo_space_id, target_roadway_id, target_cargo_space_id, priority, execution_phase, in_or_out, create_time, start_time, finish_time, entrance_id, exit_id, warehouse_id, fault_flag from mydo_job_wcs + + + select job_id, status, task_no, roadway_id, cargo_space_id, target_roadway_id, target_cargo_space_id, priority, execution_phase, in_or_out, create_time, start_time, finish_time, entrance_id, exit_id, warehouse_id, fault_flag from mydo_job_wcs_finish + + + + + + + + + + + + + insert into mydo_job_wcs + + job_id, + status, + task_no, + roadway_id, + cargo_space_id, + target_roadway_id, + target_cargo_space_id, + priority, + execution_phase, + in_or_out, + create_time, + start_time, + finish_time, + entrance_id, + exit_id, + warehouse_id, + fault_flag, + + + #{jobId}, + #{status}, + #{taskNo}, + #{roadwayId}, + #{cargoSpaceId}, + #{targetRoadwayId}, + #{targetCargoSpaceId}, + #{priority}, + #{executionPhase}, + #{inOrOut}, + #{createTime}, + #{startTime}, + #{finishTime}, + #{entranceId}, + #{exitId}, + #{warehouseId}, + #{faultFlag}, + + + + insert into mydo_job_wcs_finish + select * from mydo_job_wcs where job_id = #{jobId} + + + + update mydo_job_wcs + + status = #{status}, + task_no = #{taskNo}, + roadway_id = #{roadwayId}, + cargo_space_id = #{cargoSpaceId}, + target_roadway_id = #{targetRoadwayId}, + target_cargo_space_id = #{targetCargoSpaceId}, + priority = #{priority}, + execution_phase = #{executionPhase}, + in_or_out = #{inOrOut}, + create_time = #{createTime}, + start_time = #{startTime}, + finish_time = #{finishTime}, + entrance_id = #{entranceId}, + exit_id = #{exitId}, + warehouse_id = #{warehouseId}, + fault_flag = #{faultFlag}, + + where job_id = #{jobId} + + + + delete from mydo_job_wcs where job_id = #{jobId} + + + + delete from mydo_job_wcs where job_id in + + #{jobId} + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueFinishMapper.xml b/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueFinishMapper.xml new file mode 100644 index 0000000..97f49a9 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueFinishMapper.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select uuid, rwh, dev_name, dev_id, rwzt, yph, ylh, ych, mbph, mblh, mbch, ddjxf, rwlx, scsj, xfsj, jssj, mlfs, spare1, spare2, spare3 from ddj_device_execution_queue_finish + + + + + + + + insert into ddj_device_execution_queue_finish + + uuid, + rwh, + dev_name, + dev_id, + rwzt, + yph, + ylh, + ych, + mbph, + mblh, + mbch, + ddjxf, + rwlx, + scsj, + xfsj, + jssj, + mlfs, + spare1, + spare2, + spare3, + + + #{uuid}, + #{rwh}, + #{devName}, + #{devId}, + #{rwzt}, + #{yph}, + #{ylh}, + #{ych}, + #{mbph}, + #{mblh}, + #{mbch}, + #{ddjxf}, + #{rwlx}, + #{scsj}, + #{xfsj}, + #{jssj}, + #{mlfs}, + #{spare1}, + #{spare2}, + #{spare3}, + + + + + update ddj_device_execution_queue_finish + + rwh = #{rwh}, + dev_name = #{devName}, + dev_id = #{devId}, + rwzt = #{rwzt}, + yph = #{yph}, + ylh = #{ylh}, + ych = #{ych}, + mbph = #{mbph}, + mblh = #{mblh}, + mbch = #{mbch}, + ddjxf = #{ddjxf}, + rwlx = #{rwlx}, + scsj = #{scsj}, + xfsj = #{xfsj}, + jssj = #{jssj}, + mlfs = #{mlfs}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + + where uuid = #{uuid} + + + + delete from ddj_device_execution_queue_finish where uuid = #{uuid} + + + + delete from ddj_device_execution_queue_finish where uuid in + + #{uuid} + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueMapper.xml b/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueMapper.xml new file mode 100644 index 0000000..42bb04b --- /dev/null +++ b/ruoyi-admin/src/main/resources/mapper/queue/DdjDeviceExecutionQueueMapper.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select uuid, rwh, dev_name, dev_id, rwzt, yph, ylh, ych, mbph, mblh, mbch, ddjxf, rwlx, scsj, xfsj, jssj, mlfs, spare1, spare2, spare3 from ddj_device_execution_queue + + + + + + + + + + + insert into ddj_device_execution_queue + + uuid, + rwh, + dev_name, + dev_id, + rwzt, + yph, + ylh, + ych, + mbph, + mblh, + mbch, + ddjxf, + rwlx, + scsj, + xfsj, + jssj, + mlfs, + spare1, + spare2, + spare3, + + + #{uuid}, + #{rwh}, + #{devName}, + #{devId}, + #{rwzt}, + #{yph}, + #{ylh}, + #{ych}, + #{mbph}, + #{mblh}, + #{mbch}, + #{ddjxf}, + #{rwlx}, + #{scsj}, + #{xfsj}, + #{jssj}, + #{mlfs}, + #{spare1}, + #{spare2}, + #{spare3}, + + + + + update ddj_device_execution_queue + + rwh = #{rwh}, + dev_name = #{devName}, + dev_id = #{devId}, + rwzt = #{rwzt}, + yph = #{yph}, + ylh = #{ylh}, + ych = #{ych}, + mbph = #{mbph}, + mblh = #{mblh}, + mbch = #{mbch}, + ddjxf = #{ddjxf}, + rwlx = #{rwlx}, + scsj = #{scsj}, + xfsj = #{xfsj}, + jssj = #{jssj}, + mlfs = #{mlfs}, + spare1 = #{spare1}, + spare2 = #{spare2}, + spare3 = #{spare3}, + + where uuid = #{uuid} + + + + delete from ddj_device_execution_queue where uuid = #{uuid} + + + + delete from ddj_device_execution_queue where uuid in + + #{uuid} + + + diff --git a/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 0000000..ac47c03 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml new file mode 100644 index 0000000..fb10ebb --- /dev/null +++ b/ruoyi-common/pom.xml @@ -0,0 +1,134 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + + ruoyi-common + + + common通用工具 + + + + + + + org.springframework + spring-context-support + + + + + org.springframework + spring-web + + + + + org.springframework.boot + spring-boot-starter-security + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + org.apache.commons + commons-lang3 + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.alibaba.fastjson2 + fastjson2 + + + + + commons-io + commons-io + + + + + org.apache.poi + poi-ooxml + + + + + org.yaml + snakeyaml + + + + + io.jsonwebtoken + jjwt + + + + + javax.xml.bind + jaxb-api + + + + + org.springframework.boot + spring-boot-starter-data-redis + + + + + org.apache.commons + commons-pool2 + + + + + eu.bitwalker + UserAgentUtils + + + + + javax.servlet + javax.servlet-api + + + + + cn.hutool + hutool-all + + + + org.projectlombok + lombok + + + + diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java new file mode 100644 index 0000000..1d6d4f4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Anonymous.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 匿名访问不鉴权注解 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Anonymous +{ +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java new file mode 100644 index 0000000..be49c80 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataScope.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 数据权限过滤注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataScope +{ + /** + * 部门表的别名 + */ + public String deptAlias() default ""; + + /** + * 用户表的别名 + */ + public String userAlias() default ""; + + /** + * 权限字符(用于多个角色匹配符合要求的权限)默认根据权限注解@ss获取,多个权限用逗号分隔开来 + */ + public String permission() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java new file mode 100644 index 0000000..79cd191 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DataSource.java @@ -0,0 +1,28 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.DataSourceType; + +/** + * 自定义多数据源切换注解 + * + * 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 + * + * @author ruoyi + */ +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Inherited +public @interface DataSource +{ + /** + * 切换数据源名称 + */ + public DataSourceType value() default DataSourceType.MASTER; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java new file mode 100644 index 0000000..765d8e3 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excel.java @@ -0,0 +1,197 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.math.BigDecimal; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import com.ruoyi.common.utils.poi.ExcelHandlerAdapter; + +/** + * 自定义导出Excel数据注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Excel +{ + /** + * 导出时在excel中排序 + */ + public int sort() default Integer.MAX_VALUE; + + /** + * 导出到Excel中的名字. + */ + public String name() default ""; + + /** + * 日期格式, 如: yyyy-MM-dd + */ + public String dateFormat() default ""; + + /** + * 如果是字典类型,请设置字典的type值 (如: sys_user_sex) + */ + public String dictType() default ""; + + /** + * 读取内容转表达式 (如: 0=男,1=女,2=未知) + */ + public String readConverterExp() default ""; + + /** + * 分隔符,读取字符串组内容 + */ + public String separator() default ","; + + /** + * BigDecimal 精度 默认:-1(默认不开启BigDecimal格式化) + */ + public int scale() default -1; + + /** + * BigDecimal 舍入规则 默认:BigDecimal.ROUND_HALF_EVEN + */ + public int roundingMode() default BigDecimal.ROUND_HALF_EVEN; + + /** + * 导出时在excel中每个列的高度 + */ + public double height() default 14; + + /** + * 导出时在excel中每个列的宽度 + */ + public double width() default 16; + + /** + * 文字后缀,如% 90 变成90% + */ + public String suffix() default ""; + + /** + * 当值为空时,字段的默认值 + */ + public String defaultValue() default ""; + + /** + * 提示信息 + */ + public String prompt() default ""; + + /** + * 是否允许内容换行 + */ + public boolean wrapText() default false; + + /** + * 设置只能选择不能输入的列内容. + */ + public String[] combo() default {}; + + /** + * 是否从字典读数据到combo,默认不读取,如读取需要设置dictType注解. + */ + public boolean comboReadDict() default false; + + /** + * 是否需要纵向合并单元格,应对需求:含有list集合单元格) + */ + public boolean needMerge() default false; + + /** + * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. + */ + public boolean isExport() default true; + + /** + * 另一个类中的属性名称,支持多级获取,以小数点隔开 + */ + public String targetAttr() default ""; + + /** + * 是否自动统计数据,在最后追加一行统计数据总和 + */ + public boolean isStatistics() default false; + + /** + * 导出类型(0数字 1字符串 2图片) + */ + public ColumnType cellType() default ColumnType.STRING; + + /** + * 导出列头背景颜色 + */ + public IndexedColors headerBackgroundColor() default IndexedColors.GREY_50_PERCENT; + + /** + * 导出列头字体颜色 + */ + public IndexedColors headerColor() default IndexedColors.WHITE; + + /** + * 导出单元格背景颜色 + */ + public IndexedColors backgroundColor() default IndexedColors.WHITE; + + /** + * 导出单元格字体颜色 + */ + public IndexedColors color() default IndexedColors.BLACK; + + /** + * 导出字段对齐方式 + */ + public HorizontalAlignment align() default HorizontalAlignment.CENTER; + + /** + * 自定义数据处理器 + */ + public Class handler() default ExcelHandlerAdapter.class; + + /** + * 自定义数据处理器参数 + */ + public String[] args() default {}; + + /** + * 字段类型(0:导出导入;1:仅导出;2:仅导入) + */ + Type type() default Type.ALL; + + public enum Type + { + ALL(0), EXPORT(1), IMPORT(2); + private final int value; + + Type(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } + + public enum ColumnType + { + NUMERIC(0), STRING(1), IMAGE(2), TEXT(3); + private final int value; + + ColumnType(int value) + { + this.value = value; + } + + public int value() + { + return this.value; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java new file mode 100644 index 0000000..1f1cc81 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Excels.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Excel注解集 + * + * @author ruoyi + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface Excels +{ + public Excel[] value(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java new file mode 100644 index 0000000..1eb8e49 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Log.java @@ -0,0 +1,51 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +/** + * 自定义操作日志记录注解 + * + * @author ruoyi + * + */ +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface Log +{ + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java new file mode 100644 index 0000000..0f024c7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RateLimiter.java @@ -0,0 +1,40 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.enums.LimitType; + +/** + * 限流注解 + * + * @author ruoyi + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RateLimiter +{ + /** + * 限流key + */ + public String key() default CacheConstants.RATE_LIMIT_KEY; + + /** + * 限流时间,单位秒 + */ + public int time() default 60; + + /** + * 限流次数 + */ + public int count() default 100; + + /** + * 限流类型 + */ + public LimitType limitType() default LimitType.DEFAULT; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java new file mode 100644 index 0000000..b769748 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RepeatSubmit.java @@ -0,0 +1,31 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义注解防止表单重复提交 + * + * @author ruoyi + * + */ +@Inherited +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RepeatSubmit +{ + /** + * 间隔时间(ms),小于此时间视为重复提交 + */ + public int interval() default 5000; + + /** + * 提示消息 + */ + public String message() default "不允许重复提交,请稍候再试"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RequestLog.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RequestLog.java new file mode 100644 index 0000000..506bdc2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/RequestLog.java @@ -0,0 +1,42 @@ +package com.ruoyi.common.annotation; + +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.OperatorType; + +import java.lang.annotation.*; + +@Target({ ElementType.PARAMETER, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface RequestLog { + + /** + * 模块 + */ + public String title() default ""; + + /** + * 功能 + */ + public BusinessType businessType() default BusinessType.OTHER; + + /** + * 操作人类别 + */ + public OperatorType operatorType() default OperatorType.MANAGE; + + /** + * 是否保存请求的参数 + */ + public boolean isSaveRequestData() default true; + + /** + * 是否保存响应的参数 + */ + public boolean isSaveResponseData() default true; + + /** + * 排除指定的请求参数 + */ + public String[] excludeParamNames() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java new file mode 100644 index 0000000..c0621e9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/Sensitive.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import com.fasterxml.jackson.annotation.JacksonAnnotationsInside; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.ruoyi.common.config.serializer.SensitiveJsonSerializer; +import com.ruoyi.common.enums.DesensitizedType; + +/** + * 数据脱敏注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +@JacksonAnnotationsInside +@JsonSerialize(using = SensitiveJsonSerializer.class) +public @interface Sensitive +{ + DesensitizedType desensitizedType(); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java new file mode 100644 index 0000000..29281cf --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/RuoYiConfig.java @@ -0,0 +1,122 @@ +package com.ruoyi.common.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * 读取项目相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "ruoyi") +public class RuoYiConfig +{ + /** 项目名称 */ + private String name; + + /** 版本 */ + private String version; + + /** 版权年份 */ + private String copyrightYear; + + /** 上传路径 */ + private static String profile; + + /** 获取地址开关 */ + private static boolean addressEnabled; + + /** 验证码类型 */ + private static String captchaType; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getCopyrightYear() + { + return copyrightYear; + } + + public void setCopyrightYear(String copyrightYear) + { + this.copyrightYear = copyrightYear; + } + + public static String getProfile() + { + return profile; + } + + public void setProfile(String profile) + { + RuoYiConfig.profile = profile; + } + + public static boolean isAddressEnabled() + { + return addressEnabled; + } + + public void setAddressEnabled(boolean addressEnabled) + { + RuoYiConfig.addressEnabled = addressEnabled; + } + + public static String getCaptchaType() { + return captchaType; + } + + public void setCaptchaType(String captchaType) { + RuoYiConfig.captchaType = captchaType; + } + + /** + * 获取导入上传路径 + */ + public static String getImportPath() + { + return getProfile() + "/import"; + } + + /** + * 获取头像上传路径 + */ + public static String getAvatarPath() + { + return getProfile() + "/avatar"; + } + + /** + * 获取下载路径 + */ + public static String getDownloadPath() + { + return getProfile() + "/download/"; + } + + /** + * 获取上传路径 + */ + public static String getUploadPath() + { + return getProfile() + "/upload"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java new file mode 100644 index 0000000..e819a1d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/serializer/SensitiveJsonSerializer.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.config.serializer; + +import java.io.IOException; +import java.util.Objects; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.BeanProperty; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.ContextualSerializer; +import com.ruoyi.common.annotation.Sensitive; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.DesensitizedType; +import com.ruoyi.common.utils.SecurityUtils; + +/** + * 数据脱敏序列化过滤 + * + * @author ruoyi + */ +public class SensitiveJsonSerializer extends JsonSerializer implements ContextualSerializer +{ + private DesensitizedType desensitizedType; + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException + { + if (desensitization()) + { + gen.writeString(desensitizedType.desensitizer().apply(value)); + } + else + { + gen.writeString(value); + } + } + + @Override + public JsonSerializer createContextual(SerializerProvider prov, BeanProperty property) + throws JsonMappingException + { + Sensitive annotation = property.getAnnotation(Sensitive.class); + if (Objects.nonNull(annotation) && Objects.equals(String.class, property.getType().getRawClass())) + { + this.desensitizedType = annotation.desensitizedType(); + return this; + } + return prov.findValueSerializer(property.getType(), property); + } + + /** + * 是否需要脱敏处理 + */ + private boolean desensitization() + { + try + { + LoginUser securityUser = SecurityUtils.getLoginUser(); + // 管理员不脱敏 + return !securityUser.getUser().isAdmin(); + } + catch (Exception e) + { + return true; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java new file mode 100644 index 0000000..0080343 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -0,0 +1,44 @@ +package com.ruoyi.common.constant; + +/** + * 缓存的key 常量 + * + * @author ruoyi + */ +public class CacheConstants +{ + /** + * 登录用户 redis key + */ + public static final String LOGIN_TOKEN_KEY = "login_tokens:"; + + /** + * 验证码 redis key + */ + public static final String CAPTCHA_CODE_KEY = "captcha_codes:"; + + /** + * 参数管理 cache key + */ + public static final String SYS_CONFIG_KEY = "sys_config:"; + + /** + * 字典管理 cache key + */ + public static final String SYS_DICT_KEY = "sys_dict:"; + + /** + * 防重提交 redis key + */ + public static final String REPEAT_SUBMIT_KEY = "repeat_submit:"; + + /** + * 限流 redis key + */ + public static final String RATE_LIMIT_KEY = "rate_limit:"; + + /** + * 登录账户密码错误次数 redis key + */ + public static final String PWD_ERR_CNT_KEY = "pwd_err_cnt:"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java new file mode 100644 index 0000000..0c384c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/Constants.java @@ -0,0 +1,173 @@ +package com.ruoyi.common.constant; + +import java.util.Locale; +import io.jsonwebtoken.Claims; + +/** + * 通用常量信息 + * + * @author ruoyi + */ +public class Constants +{ + /** + * UTF-8 字符集 + */ + public static final String UTF8 = "UTF-8"; + + /** + * GBK 字符集 + */ + public static final String GBK = "GBK"; + + /** + * 系统语言 + */ + public static final Locale DEFAULT_LOCALE = Locale.SIMPLIFIED_CHINESE; + + /** + * www主域 + */ + public static final String WWW = "www."; + + /** + * http请求 + */ + public static final String HTTP = "http://"; + + /** + * https请求 + */ + public static final String HTTPS = "https://"; + + /** + * 通用成功标识 + */ + public static final String SUCCESS = "0"; + + /** + * 通用失败标识 + */ + public static final String FAIL = "1"; + + /** + * 登录成功 + */ + public static final String LOGIN_SUCCESS = "Success"; + + /** + * 注销 + */ + public static final String LOGOUT = "Logout"; + + /** + * 注册 + */ + public static final String REGISTER = "Register"; + + /** + * 登录失败 + */ + public static final String LOGIN_FAIL = "Error"; + + /** + * 所有权限标识 + */ + public static final String ALL_PERMISSION = "*:*:*"; + + /** + * 管理员角色权限标识 + */ + public static final String SUPER_ADMIN = "admin"; + + /** + * 角色权限分隔符 + */ + public static final String ROLE_DELIMETER = ","; + + /** + * 权限标识分隔符 + */ + public static final String PERMISSION_DELIMETER = ","; + + /** + * 验证码有效期(分钟) + */ + public static final Integer CAPTCHA_EXPIRATION = 2; + + /** + * 令牌 + */ + public static final String TOKEN = "token"; + + /** + * 令牌前缀 + */ + public static final String TOKEN_PREFIX = "Bearer "; + + /** + * 令牌前缀 + */ + public static final String LOGIN_USER_KEY = "login_user_key"; + + /** + * 用户ID + */ + public static final String JWT_USERID = "userid"; + + /** + * 用户名称 + */ + public static final String JWT_USERNAME = Claims.SUBJECT; + + /** + * 用户头像 + */ + public static final String JWT_AVATAR = "avatar"; + + /** + * 创建时间 + */ + public static final String JWT_CREATED = "created"; + + /** + * 用户权限 + */ + public static final String JWT_AUTHORITIES = "authorities"; + + /** + * 资源映射路径 前缀 + */ + public static final String RESOURCE_PREFIX = "/profile"; + + /** + * RMI 远程方法调用 + */ + public static final String LOOKUP_RMI = "rmi:"; + + /** + * LDAP 远程方法调用 + */ + public static final String LOOKUP_LDAP = "ldap:"; + + /** + * LDAPS 远程方法调用 + */ + public static final String LOOKUP_LDAPS = "ldaps:"; + + /** + * 自动识别json对象白名单配置(仅允许解析的包名,范围越小越安全) + */ + public static final String[] JSON_WHITELIST_STR = { "org.springframework", "com.ruoyi" }; + + /** + * 定时任务白名单配置(仅允许访问的包名,如其他需要可以自行添加) + */ + public static final String[] JOB_WHITELIST_STR = { "com.ruoyi.quartz.task" }; + + /** + * 定时任务违规的字符 + */ + public static final String[] JOB_ERROR_STR = { "java.net.URL", "javax.naming.InitialContext", "org.yaml.snakeyaml", + "org.springframework", "org.apache", "com.ruoyi.common.utils.file", "com.ruoyi.common.config", "com.ruoyi.generator" }; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java new file mode 100644 index 0000000..7d899d4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/GenConstants.java @@ -0,0 +1,117 @@ +package com.ruoyi.common.constant; + +/** + * 代码生成通用常量 + * + * @author ruoyi + */ +public class GenConstants +{ + /** 单表(增删改查) */ + public static final String TPL_CRUD = "crud"; + + /** 树表(增删改查) */ + public static final String TPL_TREE = "tree"; + + /** 主子表(增删改查) */ + public static final String TPL_SUB = "sub"; + + /** 树编码字段 */ + public static final String TREE_CODE = "treeCode"; + + /** 树父编码字段 */ + public static final String TREE_PARENT_CODE = "treeParentCode"; + + /** 树名称字段 */ + public static final String TREE_NAME = "treeName"; + + /** 上级菜单ID字段 */ + public static final String PARENT_MENU_ID = "parentMenuId"; + + /** 上级菜单名称字段 */ + public static final String PARENT_MENU_NAME = "parentMenuName"; + + /** 数据库字符串类型 */ + public static final String[] COLUMNTYPE_STR = { "char", "varchar", "nvarchar", "varchar2" }; + + /** 数据库文本类型 */ + public static final String[] COLUMNTYPE_TEXT = { "tinytext", "text", "mediumtext", "longtext" }; + + /** 数据库时间类型 */ + public static final String[] COLUMNTYPE_TIME = { "datetime", "time", "date", "timestamp" }; + + /** 数据库数字类型 */ + public static final String[] COLUMNTYPE_NUMBER = { "tinyint", "smallint", "mediumint", "int", "number", "integer", + "bit", "bigint", "float", "double", "decimal" }; + + /** 页面不需要编辑字段 */ + public static final String[] COLUMNNAME_NOT_EDIT = { "id", "create_by", "create_time", "del_flag" }; + + /** 页面不需要显示的列表字段 */ + public static final String[] COLUMNNAME_NOT_LIST = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time" }; + + /** 页面不需要查询字段 */ + public static final String[] COLUMNNAME_NOT_QUERY = { "id", "create_by", "create_time", "del_flag", "update_by", + "update_time", "remark" }; + + /** Entity基类字段 */ + public static final String[] BASE_ENTITY = { "createBy", "createTime", "updateBy", "updateTime", "remark" }; + + /** Tree基类字段 */ + public static final String[] TREE_ENTITY = { "parentName", "parentId", "orderNum", "ancestors", "children" }; + + /** 文本框 */ + public static final String HTML_INPUT = "input"; + + /** 文本域 */ + public static final String HTML_TEXTAREA = "textarea"; + + /** 下拉框 */ + public static final String HTML_SELECT = "select"; + + /** 单选框 */ + public static final String HTML_RADIO = "radio"; + + /** 复选框 */ + public static final String HTML_CHECKBOX = "checkbox"; + + /** 日期控件 */ + public static final String HTML_DATETIME = "datetime"; + + /** 图片上传控件 */ + public static final String HTML_IMAGE_UPLOAD = "imageUpload"; + + /** 文件上传控件 */ + public static final String HTML_FILE_UPLOAD = "fileUpload"; + + /** 富文本控件 */ + public static final String HTML_EDITOR = "editor"; + + /** 字符串类型 */ + public static final String TYPE_STRING = "String"; + + /** 整型 */ + public static final String TYPE_INTEGER = "Integer"; + + /** 长整型 */ + public static final String TYPE_LONG = "Long"; + + /** 浮点型 */ + public static final String TYPE_DOUBLE = "Double"; + + /** 高精度计算类型 */ + public static final String TYPE_BIGDECIMAL = "BigDecimal"; + + /** 时间类型 */ + public static final String TYPE_DATE = "Date"; + + /** 模糊查询 */ + public static final String QUERY_LIKE = "LIKE"; + + /** 相等查询 */ + public static final String QUERY_EQ = "EQ"; + + /** 需要 */ + public static final String REQUIRE = "1"; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java new file mode 100644 index 0000000..a983c77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/HttpStatus.java @@ -0,0 +1,94 @@ +package com.ruoyi.common.constant; + +/** + * 返回状态码 + * + * @author ruoyi + */ +public class HttpStatus +{ + /** + * 操作成功 + */ + public static final int SUCCESS = 200; + + /** + * 对象创建成功 + */ + public static final int CREATED = 201; + + /** + * 请求已经被接受 + */ + public static final int ACCEPTED = 202; + + /** + * 操作已经执行成功,但是没有返回数据 + */ + public static final int NO_CONTENT = 204; + + /** + * 资源已被移除 + */ + public static final int MOVED_PERM = 301; + + /** + * 重定向 + */ + public static final int SEE_OTHER = 303; + + /** + * 资源没有被修改 + */ + public static final int NOT_MODIFIED = 304; + + /** + * 参数列表错误(缺少,格式不匹配) + */ + public static final int BAD_REQUEST = 400; + + /** + * 未授权 + */ + public static final int UNAUTHORIZED = 401; + + /** + * 访问受限,授权过期 + */ + public static final int FORBIDDEN = 403; + + /** + * 资源,服务未找到 + */ + public static final int NOT_FOUND = 404; + + /** + * 不允许的http方法 + */ + public static final int BAD_METHOD = 405; + + /** + * 资源冲突,或者资源被锁 + */ + public static final int CONFLICT = 409; + + /** + * 不支持的数据,媒体类型 + */ + public static final int UNSUPPORTED_TYPE = 415; + + /** + * 系统内部错误 + */ + public static final int ERROR = 500; + + /** + * 接口未实现 + */ + public static final int NOT_IMPLEMENTED = 501; + + /** + * 系统警告消息 + */ + public static final int WARN = 601; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java new file mode 100644 index 0000000..62ad815 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java @@ -0,0 +1,50 @@ +package com.ruoyi.common.constant; + +/** + * 任务调度通用常量 + * + * @author ruoyi + */ +public class ScheduleConstants +{ + public static final String TASK_CLASS_NAME = "TASK_CLASS_NAME"; + + /** 执行目标key */ + public static final String TASK_PROPERTIES = "TASK_PROPERTIES"; + + /** 默认 */ + public static final String MISFIRE_DEFAULT = "0"; + + /** 立即触发执行 */ + public static final String MISFIRE_IGNORE_MISFIRES = "1"; + + /** 触发一次执行 */ + public static final String MISFIRE_FIRE_AND_PROCEED = "2"; + + /** 不触发立即执行 */ + public static final String MISFIRE_DO_NOTHING = "3"; + + public enum Status + { + /** + * 正常 + */ + NORMAL("0"), + /** + * 暂停 + */ + PAUSE("1"); + + private String value; + + private Status(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java new file mode 100644 index 0000000..8dc7faa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/UserConstants.java @@ -0,0 +1,81 @@ +package com.ruoyi.common.constant; + +/** + * 用户常量信息 + * + * @author ruoyi + */ +public class UserConstants +{ + /** + * 平台内系统用户的唯一标志 + */ + public static final String SYS_USER = "SYS_USER"; + + /** 正常状态 */ + public static final String NORMAL = "0"; + + /** 异常状态 */ + public static final String EXCEPTION = "1"; + + /** 用户封禁状态 */ + public static final String USER_DISABLE = "1"; + + /** 角色正常状态 */ + public static final String ROLE_NORMAL = "0"; + + /** 角色封禁状态 */ + public static final String ROLE_DISABLE = "1"; + + /** 部门正常状态 */ + public static final String DEPT_NORMAL = "0"; + + /** 部门停用状态 */ + public static final String DEPT_DISABLE = "1"; + + /** 字典正常状态 */ + public static final String DICT_NORMAL = "0"; + + /** 是否为系统默认(是) */ + public static final String YES = "Y"; + + /** 是否菜单外链(是) */ + public static final String YES_FRAME = "0"; + + /** 是否菜单外链(否) */ + public static final String NO_FRAME = "1"; + + /** 菜单类型(目录) */ + public static final String TYPE_DIR = "M"; + + /** 菜单类型(菜单) */ + public static final String TYPE_MENU = "C"; + + /** 菜单类型(按钮) */ + public static final String TYPE_BUTTON = "F"; + + /** Layout组件标识 */ + public final static String LAYOUT = "Layout"; + + /** ParentView组件标识 */ + public final static String PARENT_VIEW = "ParentView"; + + /** InnerLink组件标识 */ + public final static String INNER_LINK = "InnerLink"; + + /** 校验是否唯一的返回标识 */ + public final static boolean UNIQUE = true; + public final static boolean NOT_UNIQUE = false; + + /** + * 用户名长度限制 + */ + public static final int USERNAME_MIN_LENGTH = 2; + public static final int USERNAME_MAX_LENGTH = 20; + + /** + * 密码长度限制 + */ + public static final int PASSWORD_MIN_LENGTH = 5; + public static final int PASSWORD_MAX_LENGTH = 20; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java new file mode 100644 index 0000000..a685e06 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/controller/BaseController.java @@ -0,0 +1,202 @@ +package com.ruoyi.common.core.controller; + +import java.beans.PropertyEditorSupport; +import java.util.Date; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.WebDataBinder; +import org.springframework.web.bind.annotation.InitBinder; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.PageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.sql.SqlUtil; + +/** + * web层通用数据处理 + * + * @author ruoyi + */ +public class BaseController +{ + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + /** + * 将前台传递过来的日期格式的字符串,自动转化为Date类型 + */ + @InitBinder + public void initBinder(WebDataBinder binder) + { + // Date 类型转换 + binder.registerCustomEditor(Date.class, new PropertyEditorSupport() + { + @Override + public void setAsText(String text) + { + setValue(DateUtils.parseDate(text)); + } + }); + } + + /** + * 设置请求分页数据 + */ + protected void startPage() + { + PageUtils.startPage(); + } + + /** + * 设置请求排序数据 + */ + protected void startOrderBy() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (StringUtils.isNotEmpty(pageDomain.getOrderBy())) + { + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + PageHelper.orderBy(orderBy); + } + } + + /** + * 清理分页的线程变量 + */ + protected void clearPage() + { + PageUtils.clearPage(); + } + + /** + * 响应请求分页数据 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + protected TableDataInfo getDataTable(List list) + { + TableDataInfo rspData = new TableDataInfo(); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + /** + * 返回成功 + */ + public AjaxResult success() + { + return AjaxResult.success(); + } + + /** + * 返回失败消息 + */ + public AjaxResult error() + { + return AjaxResult.error(); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(String message) + { + return AjaxResult.success(message); + } + + /** + * 返回成功消息 + */ + public AjaxResult success(Object data) + { + return AjaxResult.success(data); + } + + /** + * 返回失败消息 + */ + public AjaxResult error(String message) + { + return AjaxResult.error(message); + } + + /** + * 返回警告消息 + */ + public AjaxResult warn(String message) + { + return AjaxResult.warn(message); + } + + /** + * 响应返回结果 + * + * @param rows 影响行数 + * @return 操作结果 + */ + protected AjaxResult toAjax(int rows) + { + return rows > 0 ? AjaxResult.success() : AjaxResult.error(); + } + + /** + * 响应返回结果 + * + * @param result 结果 + * @return 操作结果 + */ + protected AjaxResult toAjax(boolean result) + { + return result ? success() : error(); + } + + /** + * 页面跳转 + */ + public String redirect(String url) + { + return StringUtils.format("redirect:{}", url); + } + + /** + * 获取用户缓存信息 + */ + public LoginUser getLoginUser() + { + return SecurityUtils.getLoginUser(); + } + + /** + * 获取登录用户id + */ + public Long getUserId() + { + return getLoginUser().getUserId(); + } + + /** + * 获取登录部门id + */ + public Long getDeptId() + { + return getLoginUser().getDeptId(); + } + + /** + * 获取登录用户名 + */ + public String getUsername() + { + return getLoginUser().getUsername(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java new file mode 100644 index 0000000..a7abfe4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/AjaxResult.java @@ -0,0 +1,216 @@ +package com.ruoyi.common.core.domain; + +import java.util.HashMap; +import java.util.Objects; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.utils.StringUtils; + +/** + * 操作消息提醒 + * + * @author ruoyi + */ +public class AjaxResult extends HashMap +{ + private static final long serialVersionUID = 1L; + + /** 状态码 */ + public static final String CODE_TAG = "code"; + + /** 返回内容 */ + public static final String MSG_TAG = "msg"; + + /** 数据对象 */ + public static final String DATA_TAG = "data"; + + /** + * 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。 + */ + public AjaxResult() + { + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + */ + public AjaxResult(int code, String msg) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + } + + /** + * 初始化一个新创建的 AjaxResult 对象 + * + * @param code 状态码 + * @param msg 返回内容 + * @param data 数据对象 + */ + public AjaxResult(int code, String msg, Object data) + { + super.put(CODE_TAG, code); + super.put(MSG_TAG, msg); + if (StringUtils.isNotNull(data)) + { + super.put(DATA_TAG, data); + } + } + + /** + * 返回成功消息 + * + * @return 成功消息 + */ + public static AjaxResult success() + { + return AjaxResult.success("操作成功"); + } + + /** + * 返回成功数据 + * + * @return 成功消息 + */ + public static AjaxResult success(Object data) + { + return AjaxResult.success("操作成功", data); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @return 成功消息 + */ + public static AjaxResult success(String msg) + { + return AjaxResult.success(msg, null); + } + + /** + * 返回成功消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 成功消息 + */ + public static AjaxResult success(String msg, Object data) + { + return new AjaxResult(HttpStatus.SUCCESS, msg, data); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @return 警告消息 + */ + public static AjaxResult warn(String msg) + { + return AjaxResult.warn(msg, null); + } + + /** + * 返回警告消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 警告消息 + */ + public static AjaxResult warn(String msg, Object data) + { + return new AjaxResult(HttpStatus.WARN, msg, data); + } + + /** + * 返回错误消息 + * + * @return 错误消息 + */ + public static AjaxResult error() + { + return AjaxResult.error("操作失败"); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(String msg) + { + return AjaxResult.error(msg, null); + } + + /** + * 返回错误消息 + * + * @param msg 返回内容 + * @param data 数据对象 + * @return 错误消息 + */ + public static AjaxResult error(String msg, Object data) + { + return new AjaxResult(HttpStatus.ERROR, msg, data); + } + + /** + * 返回错误消息 + * + * @param code 状态码 + * @param msg 返回内容 + * @return 错误消息 + */ + public static AjaxResult error(int code, String msg) + { + return new AjaxResult(code, msg, null); + } + + /** + * 是否为成功消息 + * + * @return 结果 + */ + public boolean isSuccess() + { + return Objects.equals(HttpStatus.SUCCESS, this.get(CODE_TAG)); + } + + /** + * 是否为警告消息 + * + * @return 结果 + */ + public boolean isWarn() + { + return Objects.equals(HttpStatus.WARN, this.get(CODE_TAG)); + } + + /** + * 是否为错误消息 + * + * @return 结果 + */ + public boolean isError() + { + return Objects.equals(HttpStatus.ERROR, this.get(CODE_TAG)); + } + + /** + * 方便链式调用 + * + * @param key 键 + * @param value 值 + * @return 数据对象 + */ + @Override + public AjaxResult put(String key, Object value) + { + super.put(key, value); + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java new file mode 100644 index 0000000..15bf66b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/BaseEntity.java @@ -0,0 +1,118 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * Entity基类 + * + * @author ruoyi + */ +public class BaseEntity implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 搜索值 */ + @JsonIgnore + private String searchValue; + + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** 备注 */ + private String remark; + + /** 请求参数 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private Map params; + + public String getSearchValue() + { + return searchValue; + } + + public void setSearchValue(String searchValue) + { + this.searchValue = searchValue; + } + + public String getCreateBy() + { + return createBy; + } + + public void setCreateBy(String createBy) + { + this.createBy = createBy; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } + + public String getUpdateBy() + { + return updateBy; + } + + public void setUpdateBy(String updateBy) + { + this.updateBy = updateBy; + } + + public Date getUpdateTime() + { + return updateTime; + } + + public void setUpdateTime(Date updateTime) + { + this.updateTime = updateTime; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public Map getParams() + { + if (params == null) + { + params = new HashMap<>(); + } + return params; + } + + public void setParams(Map params) + { + this.params = params; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java new file mode 100644 index 0000000..ef15802 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/R.java @@ -0,0 +1,115 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import com.ruoyi.common.constant.HttpStatus; + +/** + * 响应信息主体 + * + * @author ruoyi + */ +public class R implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 成功 */ + public static final int SUCCESS = HttpStatus.SUCCESS; + + /** 失败 */ + public static final int FAIL = HttpStatus.ERROR; + + private int code; + + private String msg; + + private T data; + + public static R ok() + { + return restResult(null, SUCCESS, "操作成功"); + } + + public static R ok(T data) + { + return restResult(data, SUCCESS, "操作成功"); + } + + public static R ok(T data, String msg) + { + return restResult(data, SUCCESS, msg); + } + + public static R fail() + { + return restResult(null, FAIL, "操作失败"); + } + + public static R fail(String msg) + { + return restResult(null, FAIL, msg); + } + + public static R fail(T data) + { + return restResult(data, FAIL, "操作失败"); + } + + public static R fail(T data, String msg) + { + return restResult(data, FAIL, msg); + } + + public static R fail(int code, String msg) + { + return restResult(null, code, msg); + } + + private static R restResult(T data, int code, String msg) + { + R apiResult = new R<>(); + apiResult.setCode(code); + apiResult.setData(data); + apiResult.setMsg(msg); + return apiResult; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public T getData() + { + return data; + } + + public void setData(T data) + { + this.data = data; + } + + public static Boolean isError(R ret) + { + return !isSuccess(ret); + } + + public static Boolean isSuccess(R ret) + { + return R.SUCCESS == ret.getCode(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java new file mode 100644 index 0000000..f6b1e94 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeEntity.java @@ -0,0 +1,83 @@ +package com.ruoyi.common.core.domain; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Tree基类 + * + * @author ruoyi + */ +public class TreeEntity extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 祖级列表 */ + private String ancestors; + + /** 子部门 */ + private List children = new ArrayList<>(); + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java new file mode 100644 index 0000000..e1edf69 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/TreeSelect.java @@ -0,0 +1,96 @@ +package com.ruoyi.common.core.domain; + +import java.io.Serializable; +import java.util.List; +import java.util.stream.Collectors; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.utils.StringUtils; + +/** + * Treeselect树结构实体类 + * + * @author ruoyi + */ +public class TreeSelect implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 节点ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + + /** 节点名称 */ + private String label; + + /** 节点禁用 */ + private boolean disabled = false; + + /** 子节点 */ + @JsonInclude(JsonInclude.Include.NON_EMPTY) + private List children; + + public TreeSelect() + { + + } + + public TreeSelect(SysDept dept) + { + this.id = dept.getDeptId(); + this.label = dept.getDeptName(); + this.disabled = StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()); + this.children = dept.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public TreeSelect(SysMenu menu) + { + this.id = menu.getMenuId(); + this.label = menu.getMenuName(); + this.children = menu.getChildren().stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + public Long getId() + { + return id; + } + + public void setId(Long id) + { + this.id = id; + } + + public String getLabel() + { + return label; + } + + public void setLabel(String label) + { + this.label = label; + } + + public boolean isDisabled() + { + return disabled; + } + + public void setDisabled(boolean disabled) + { + this.disabled = disabled; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java new file mode 100644 index 0000000..63e5089 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDept.java @@ -0,0 +1,208 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 部门表 sys_dept + * + * @author ruoyi + */ +public class SysDept extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 部门ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + /** 父部门ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long parentId; + + /** 祖级列表 */ + private String ancestors; + + /** 部门名称 */ + private String deptName; + + /** 显示顺序 */ + private Integer orderNum; + + /** 负责人 */ + private String leader; + + /** 联系电话 */ + private String phone; + + /** 邮箱 */ + private String email; + + /** 部门状态:0正常,1停用 */ + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 父部门名称 */ + private String parentName; + + /** 子部门 */ + private List children = new ArrayList(); + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + public String getAncestors() + { + return ancestors; + } + + public void setAncestors(String ancestors) + { + this.ancestors = ancestors; + } + + @NotBlank(message = "部门名称不能为空") + @Size(min = 0, max = 30, message = "部门名称长度不能超过30个字符") + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + public String getLeader() + { + return leader; + } + + public void setLeader(String leader) + { + this.leader = leader; + } + + @Size(min = 0, max = 11, message = "联系电话长度不能超过11个字符") + public String getPhone() + { + return phone; + } + + public void setPhone(String phone) + { + this.phone = phone; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("deptId", getDeptId()) + .append("parentId", getParentId()) + .append("ancestors", getAncestors()) + .append("deptName", getDeptName()) + .append("orderNum", getOrderNum()) + .append("leader", getLeader()) + .append("phone", getPhone()) + .append("email", getEmail()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java new file mode 100644 index 0000000..cf4b981 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictData.java @@ -0,0 +1,181 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典数据表 sys_dict_data + * + * @author ruoyi + */ +public class SysDictData extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典编码 */ + @Excel(name = "字典编码", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long dictCode; + + /** 字典排序 */ + @Excel(name = "字典排序", cellType = ColumnType.NUMERIC) + private Long dictSort; + + /** 字典标签 */ + @Excel(name = "字典标签") + private String dictLabel; + + /** 字典键值 */ + @Excel(name = "字典键值") + private String dictValue; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 样式属性(其他样式扩展) */ + private String cssClass; + + /** 表格字典样式 */ + private String listClass; + + /** 是否默认(Y是 N否) */ + @Excel(name = "是否默认", readConverterExp = "Y=是,N=否") + private String isDefault; + + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictCode() + { + return dictCode; + } + + public void setDictCode(Long dictCode) + { + this.dictCode = dictCode; + } + + public Long getDictSort() + { + return dictSort; + } + + public void setDictSort(Long dictSort) + { + this.dictSort = dictSort; + } + + @NotBlank(message = "字典标签不能为空") + @Size(min = 0, max = 100, message = "字典标签长度不能超过100个字符") + public String getDictLabel() + { + return dictLabel; + } + + public void setDictLabel(String dictLabel) + { + this.dictLabel = dictLabel; + } + + @NotBlank(message = "字典键值不能为空") + @Size(min = 0, max = 100, message = "字典键值长度不能超过100个字符") + public String getDictValue() + { + return dictValue; + } + + public void setDictValue(String dictValue) + { + this.dictValue = dictValue; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型长度不能超过100个字符") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + @Size(min = 0, max = 100, message = "样式属性长度不能超过100个字符") + public String getCssClass() + { + return cssClass; + } + + public void setCssClass(String cssClass) + { + this.cssClass = cssClass; + } + + public String getListClass() + { + return listClass; + } + + public void setListClass(String listClass) + { + this.listClass = listClass; + } + + public boolean getDefault() + { + return UserConstants.YES.equals(this.isDefault); + } + + public String getIsDefault() + { + return isDefault; + } + + public void setIsDefault(String isDefault) + { + this.isDefault = isDefault; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictCode", getDictCode()) + .append("dictSort", getDictSort()) + .append("dictLabel", getDictLabel()) + .append("dictValue", getDictValue()) + .append("dictType", getDictType()) + .append("cssClass", getCssClass()) + .append("listClass", getListClass()) + .append("isDefault", getIsDefault()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java new file mode 100644 index 0000000..cafdeef --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysDictType.java @@ -0,0 +1,100 @@ +package com.ruoyi.common.core.domain.entity; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 字典类型表 sys_dict_type + * + * @author ruoyi + */ +public class SysDictType extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 字典主键 */ + @Excel(name = "字典主键", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long dictId; + + /** 字典名称 */ + @Excel(name = "字典名称") + private String dictName; + + /** 字典类型 */ + @Excel(name = "字典类型") + private String dictType; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + public Long getDictId() + { + return dictId; + } + + public void setDictId(Long dictId) + { + this.dictId = dictId; + } + + @NotBlank(message = "字典名称不能为空") + @Size(min = 0, max = 100, message = "字典类型名称长度不能超过100个字符") + public String getDictName() + { + return dictName; + } + + public void setDictName(String dictName) + { + this.dictName = dictName; + } + + @NotBlank(message = "字典类型不能为空") + @Size(min = 0, max = 100, message = "字典类型类型长度不能超过100个字符") + @Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)") + public String getDictType() + { + return dictType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("dictId", getDictId()) + .append("dictName", getDictName()) + .append("dictType", getDictType()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java new file mode 100644 index 0000000..ed9c38c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysMenu.java @@ -0,0 +1,279 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.ArrayList; +import java.util.List; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 菜单权限表 sys_menu + * + * @author ruoyi + */ +public class SysMenu extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 菜单ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long menuId; + + /** 菜单名称 */ + private String menuName; + + /** 父菜单名称 */ + private String parentName; + + /** 父菜单ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long parentId; + + /** 显示顺序 */ + private Integer orderNum; + + /** 路由地址 */ + private String path; + + /** 组件路径 */ + private String component; + + /** 路由参数 */ + private String query; + + /** 路由名称,默认和路由地址相同的驼峰格式(注意:因为vue3版本的router会删除名称相同路由,为避免名字的冲突,特殊情况可以自定义) */ + private String routeName; + + /** 是否为外链(0是 1否) */ + private String isFrame; + + /** 是否缓存(0缓存 1不缓存) */ + private String isCache; + + /** 类型(M目录 C菜单 F按钮) */ + private String menuType; + + /** 显示状态(0显示 1隐藏) */ + private String visible; + + /** 菜单状态(0正常 1停用) */ + private String status; + + /** 权限字符串 */ + private String perms; + + /** 菜单图标 */ + private String icon; + + /** 子菜单 */ + private List children = new ArrayList(); + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @NotBlank(message = "菜单名称不能为空") + @Size(min = 0, max = 50, message = "菜单名称长度不能超过50个字符") + public String getMenuName() + { + return menuName; + } + + public void setMenuName(String menuName) + { + this.menuName = menuName; + } + + public String getParentName() + { + return parentName; + } + + public void setParentName(String parentName) + { + this.parentName = parentName; + } + + public Long getParentId() + { + return parentId; + } + + public void setParentId(Long parentId) + { + this.parentId = parentId; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getOrderNum() + { + return orderNum; + } + + public void setOrderNum(Integer orderNum) + { + this.orderNum = orderNum; + } + + @Size(min = 0, max = 200, message = "路由地址不能超过200个字符") + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + @Size(min = 0, max = 200, message = "组件路径不能超过255个字符") + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public String getRouteName() + { + return routeName; + } + + public void setRouteName(String routeName) + { + this.routeName = routeName; + } + + public String getIsFrame() + { + return isFrame; + } + + public void setIsFrame(String isFrame) + { + this.isFrame = isFrame; + } + + public String getIsCache() + { + return isCache; + } + + public void setIsCache(String isCache) + { + this.isCache = isCache; + } + + @NotBlank(message = "菜单类型不能为空") + public String getMenuType() + { + return menuType; + } + + public void setMenuType(String menuType) + { + this.menuType = menuType; + } + + public String getVisible() + { + return visible; + } + + public void setVisible(String visible) + { + this.visible = visible; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Size(min = 0, max = 100, message = "权限标识长度不能超过100个字符") + public String getPerms() + { + return perms; + } + + public void setPerms(String perms) + { + this.perms = perms; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("menuId", getMenuId()) + .append("menuName", getMenuName()) + .append("parentId", getParentId()) + .append("orderNum", getOrderNum()) + .append("path", getPath()) + .append("component", getComponent()) + .append("query", getQuery()) + .append("routeName", getRouteName()) + .append("isFrame", getIsFrame()) + .append("IsCache", getIsCache()) + .append("menuType", getMenuType()) + .append("visible", getVisible()) + .append("status ", getStatus()) + .append("perms", getPerms()) + .append("icon", getIcon()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java new file mode 100644 index 0000000..07a8476 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysRole.java @@ -0,0 +1,249 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Set; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 角色表 sys_role + * + * @author ruoyi + */ +public class SysRole extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 角色ID */ + @Excel(name = "角色序号", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long roleId; + + /** 角色名称 */ + @Excel(name = "角色名称") + private String roleName; + + /** 角色权限 */ + @Excel(name = "角色权限") + private String roleKey; + + /** 角色排序 */ + @Excel(name = "角色排序") + private Integer roleSort; + + /** 数据范围(1:所有数据权限;2:自定义数据权限;3:本部门数据权限;4:本部门及以下数据权限;5:仅本人数据权限) */ + @Excel(name = "数据范围", readConverterExp = "1=所有数据权限,2=自定义数据权限,3=本部门数据权限,4=本部门及以下数据权限,5=仅本人数据权限") + private String dataScope; + + /** 菜单树选择项是否关联显示( 0:父子不互相关联显示 1:父子互相关联显示) */ + private boolean menuCheckStrictly; + + /** 部门树选择项是否关联显示(0:父子不互相关联显示 1:父子互相关联显示 ) */ + private boolean deptCheckStrictly; + + /** 角色状态(0正常 1停用) */ + @Excel(name = "角色状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 用户是否存在此角色标识 默认不存在 */ + private boolean flag = false; + + /** 菜单组 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long[] menuIds; + + /** 部门组(数据权限) */ + @JsonSerialize(using = ToStringSerializer.class) + private Long[] deptIds; + + /** 角色菜单权限 */ + private Set permissions; + + private boolean admin; + + public SysRole() + { + + } + + public SysRole(Long roleId) + { + this.roleId = roleId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public boolean isAdmin() + { + return isAdmin(this.roleId); + } + + public static boolean isAdmin(Long roleId) + { + return roleId != null && 1L == roleId; + } + + @NotBlank(message = "角色名称不能为空") + @Size(min = 0, max = 30, message = "角色名称长度不能超过30个字符") + public String getRoleName() + { + return roleName; + } + + public void setRoleName(String roleName) + { + this.roleName = roleName; + } + + @NotBlank(message = "权限字符不能为空") + @Size(min = 0, max = 100, message = "权限字符长度不能超过100个字符") + public String getRoleKey() + { + return roleKey; + } + + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getRoleSort() + { + return roleSort; + } + + public void setRoleSort(Integer roleSort) + { + this.roleSort = roleSort; + } + + public String getDataScope() + { + return dataScope; + } + + public void setDataScope(String dataScope) + { + this.dataScope = dataScope; + } + + public boolean isMenuCheckStrictly() + { + return menuCheckStrictly; + } + + public void setMenuCheckStrictly(boolean menuCheckStrictly) + { + this.menuCheckStrictly = menuCheckStrictly; + } + + public boolean isDeptCheckStrictly() + { + return deptCheckStrictly; + } + + public void setDeptCheckStrictly(boolean deptCheckStrictly) + { + this.deptCheckStrictly = deptCheckStrictly; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + public Long[] getMenuIds() + { + return menuIds; + } + + public void setMenuIds(Long[] menuIds) + { + this.menuIds = menuIds; + } + + public Long[] getDeptIds() + { + return deptIds; + } + + public void setDeptIds(Long[] deptIds) + { + this.deptIds = deptIds; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("roleName", getRoleName()) + .append("roleKey", getRoleKey()) + .append("roleSort", getRoleSort()) + .append("dataScope", getDataScope()) + .append("menuCheckStrictly", isMenuCheckStrictly()) + .append("deptCheckStrictly", isDeptCheckStrictly()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java new file mode 100644 index 0000000..11302e9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -0,0 +1,339 @@ +package com.ruoyi.common.core.domain.entity; + +import java.util.Date; +import java.util.List; +import javax.validation.constraints.*; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 用户对象 sys_user + * + * @author ruoyi + */ +public class SysUser extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @Excel(name = "用户序号", type = Type.EXPORT, cellType = ColumnType.NUMERIC, prompt = "用户编号") + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + + /** 部门ID */ + @Excel(name = "部门编号", type = Type.IMPORT) + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + /** 用户账号 */ + @Excel(name = "登录名称") + private String userName; + + /** 用户昵称 */ + @Excel(name = "用户名称") + private String nickName; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String email; + + /** 手机号码 */ + @Excel(name = "手机号码", cellType = ColumnType.TEXT) + private String phonenumber; + + /** 用户性别 */ + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String sex; + + /** 用户头像 */ + private String avatar; + + /** 密码 */ + private String password; + + /** 账号状态(0正常 1停用) */ + @Excel(name = "账号状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 删除标志(0代表存在 2代表删除) */ + private String delFlag; + + /** 最后登录IP */ + @Excel(name = "最后登录IP", type = Type.EXPORT) + private String loginIp; + + /** 最后登录时间 */ + @Excel(name = "最后登录时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date loginDate; + + /** 部门对象 */ + @Excels({ + @Excel(name = "部门名称", targetAttr = "deptName", type = Type.EXPORT), + @Excel(name = "部门负责人", targetAttr = "leader", type = Type.EXPORT) + }) + private SysDept dept; + + /** 角色对象 */ + private List roles; + + /** 角色组 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long[] roleIds; + + /** 岗位组 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long[] postIds; + + /** 角色ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long roleId; + + private boolean admin; + + + + public SysUser() + { + + } + + public SysUser(Long userId) + { + this.userId = userId; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public boolean isAdmin() + { + return isAdmin(this.userId); + } + + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Xss(message = "用户昵称不能包含脚本字符") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过30个字符") + public String getNickName() + { + return nickName; + } + + public void setNickName(String nickName) + { + this.nickName = nickName; + } + + @Xss(message = "用户账号不能包含脚本字符") + @NotBlank(message = "用户账号不能为空") + @Size(min = 0, max = 30, message = "用户账号长度不能超过30个字符") + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + @Email(message = "邮箱格式不正确") + @Size(min = 0, max = 50, message = "邮箱长度不能超过50个字符") + public String getEmail() + { + return email; + } + + public void setEmail(String email) + { + this.email = email; + } + + @Size(min = 0, max = 11, message = "手机号码长度不能超过11个字符") + public String getPhonenumber() + { + return phonenumber; + } + + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getSex() + { + return sex; + } + + public void setSex(String sex) + { + this.sex = sex; + } + + public String getAvatar() + { + return avatar; + } + + public void setAvatar(String avatar) + { + this.avatar = avatar; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getDelFlag() + { + return delFlag; + } + + public void setDelFlag(String delFlag) + { + this.delFlag = delFlag; + } + + public String getLoginIp() + { + return loginIp; + } + + public void setLoginIp(String loginIp) + { + this.loginIp = loginIp; + } + + public Date getLoginDate() + { + return loginDate; + } + + public void setLoginDate(Date loginDate) + { + this.loginDate = loginDate; + } + + public SysDept getDept() + { + return dept; + } + + public void setDept(SysDept dept) + { + this.dept = dept; + } + + public List getRoles() + { + return roles; + } + + public void setRoles(List roles) + { + this.roles = roles; + } + + public Long[] getRoleIds() + { + return roleIds; + } + + public void setRoleIds(Long[] roleIds) + { + this.roleIds = roleIds; + } + + public Long[] getPostIds() + { + return postIds; + } + + public void setPostIds(Long[] postIds) + { + this.postIds = postIds; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("deptId", getDeptId()) + .append("userName", getUserName()) + .append("nickName", getNickName()) + .append("email", getEmail()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("avatar", getAvatar()) + .append("password", getPassword()) + .append("status", getStatus()) + .append("delFlag", getDelFlag()) + .append("loginIp", getLoginIp()) + .append("loginDate", getLoginDate()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .append("dept", getDept()) + .toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java new file mode 100644 index 0000000..b5bc8c8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginBody.java @@ -0,0 +1,69 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户登录对象 + * + * @author ruoyi + */ +public class LoginBody +{ + /** + * 用户名 + */ + private String username; + + /** + * 用户密码 + */ + private String password; + + /** + * 验证码 + */ + private String code; + + /** + * 唯一标识 + */ + private String uuid; + + public String getUsername() + { + return username; + } + + public void setUsername(String username) + { + this.username = username; + } + + public String getPassword() + { + return password; + } + + public void setPassword(String password) + { + this.password = password; + } + + public String getCode() + { + return code; + } + + public void setCode(String code) + { + this.code = code; + } + + public String getUuid() + { + return uuid; + } + + public void setUuid(String uuid) + { + this.uuid = uuid; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java new file mode 100644 index 0000000..e128f23 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/LoginUser.java @@ -0,0 +1,270 @@ +package com.ruoyi.common.core.domain.model; + +import com.alibaba.fastjson2.annotation.JSONField; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.core.domain.entity.SysUser; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import java.util.Collection; +import java.util.Set; + +/** + * 登录用户身份权限 + * + * @author ruoyi + */ +public class LoginUser implements UserDetails +{ + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + /** + * 部门ID + */ + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + /** + * 用户唯一标识 + */ + private String token; + + /** + * 登录时间 + */ + private Long loginTime; + + /** + * 过期时间 + */ + private Long expireTime; + + /** + * 登录IP地址 + */ + private String ipaddr; + + /** + * 登录地点 + */ + private String loginLocation; + + /** + * 浏览器类型 + */ + private String browser; + + /** + * 操作系统 + */ + private String os; + + /** + * 权限列表 + */ + private Set permissions; + + /** + * 用户信息 + */ + private SysUser user; + + public LoginUser() + { + } + + public LoginUser(SysUser user, Set permissions) + { + this.user = user; + this.permissions = permissions; + } + + public LoginUser(Long userId, Long deptId, SysUser user, Set permissions) + { + this.userId = userId; + this.deptId = deptId; + this.user = user; + this.permissions = permissions; + } + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + public String getToken() + { + return token; + } + + public void setToken(String token) + { + this.token = token; + } + + @JSONField(serialize = false) + @Override + public String getPassword() + { + return user.getPassword(); + } + + @Override + public String getUsername() + { + return user.getUserName(); + } + + /** + * 账户是否未过期,过期无法验证 + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonExpired() + { + return true; + } + + /** + * 指定用户是否解锁,锁定的用户无法进行身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isAccountNonLocked() + { + return true; + } + + /** + * 指示是否已过期的用户的凭据(密码),过期的凭据防止认证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isCredentialsNonExpired() + { + return true; + } + + /** + * 是否可用 ,禁用的用户不能身份验证 + * + * @return + */ + @JSONField(serialize = false) + @Override + public boolean isEnabled() + { + return true; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getExpireTime() + { + return expireTime; + } + + public void setExpireTime(Long expireTime) + { + this.expireTime = expireTime; + } + + public Set getPermissions() + { + return permissions; + } + + public void setPermissions(Set permissions) + { + this.permissions = permissions; + } + + public SysUser getUser() + { + return user; + } + + public void setUser(SysUser user) + { + this.user = user; + } + + @Override + public Collection getAuthorities() + { + return null; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java new file mode 100644 index 0000000..868a1fc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/model/RegisterBody.java @@ -0,0 +1,11 @@ +package com.ruoyi.common.core.domain.model; + +/** + * 用户注册对象 + * + * @author ruoyi + */ +public class RegisterBody extends LoginBody +{ + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java new file mode 100644 index 0000000..8966cb4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/PageDomain.java @@ -0,0 +1,101 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 分页数据 + * + * @author ruoyi + */ +public class PageDomain +{ + /** 当前记录起始索引 */ + private Integer pageNum; + + /** 每页显示记录数 */ + private Integer pageSize; + + /** 排序列 */ + private String orderByColumn; + + /** 排序的方向desc或者asc */ + private String isAsc = "asc"; + + /** 分页参数合理化 */ + private Boolean reasonable = true; + + public String getOrderBy() + { + if (StringUtils.isEmpty(orderByColumn)) + { + return ""; + } + return StringUtils.toUnderScoreCase(orderByColumn) + " " + isAsc; + } + + public Integer getPageNum() + { + return pageNum; + } + + public void setPageNum(Integer pageNum) + { + this.pageNum = pageNum; + } + + public Integer getPageSize() + { + return pageSize; + } + + public void setPageSize(Integer pageSize) + { + this.pageSize = pageSize; + } + + public String getOrderByColumn() + { + return orderByColumn; + } + + public void setOrderByColumn(String orderByColumn) + { + this.orderByColumn = orderByColumn; + } + + public String getIsAsc() + { + return isAsc; + } + + public void setIsAsc(String isAsc) + { + if (StringUtils.isNotEmpty(isAsc)) + { + // 兼容前端排序类型 + if ("ascending".equals(isAsc)) + { + isAsc = "asc"; + } + else if ("descending".equals(isAsc)) + { + isAsc = "desc"; + } + this.isAsc = isAsc; + } + } + + public Boolean getReasonable() + { + if (StringUtils.isNull(reasonable)) + { + return Boolean.TRUE; + } + return reasonable; + } + + public void setReasonable(Boolean reasonable) + { + this.reasonable = reasonable; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java new file mode 100644 index 0000000..2fff93e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableDataInfo.java @@ -0,0 +1,85 @@ +package com.ruoyi.common.core.page; + +import java.io.Serializable; +import java.util.List; + +/** + * 表格分页数据对象 + * + * @author ruoyi + */ +public class TableDataInfo implements Serializable +{ + private static final long serialVersionUID = 1L; + + /** 总记录数 */ + private long total; + + /** 列表数据 */ + private List rows; + + /** 消息状态码 */ + private int code; + + /** 消息内容 */ + private String msg; + + /** + * 表格数据对象 + */ + public TableDataInfo() + { + } + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public TableDataInfo(List list, long total) + { + this.rows = list; + this.total = total; + } + + public long getTotal() + { + return total; + } + + public void setTotal(long total) + { + this.total = total; + } + + public List getRows() + { + return rows; + } + + public void setRows(List rows) + { + this.rows = rows; + } + + public int getCode() + { + return code; + } + + public void setCode(int code) + { + this.code = code; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java new file mode 100644 index 0000000..a120c30 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/page/TableSupport.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.core.page; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 表格数据处理 + * + * @author ruoyi + */ +public class TableSupport +{ + /** + * 当前记录起始索引 + */ + public static final String PAGE_NUM = "pageNum"; + + /** + * 每页显示记录数 + */ + public static final String PAGE_SIZE = "pageSize"; + + /** + * 排序列 + */ + public static final String ORDER_BY_COLUMN = "orderByColumn"; + + /** + * 排序的方向 "desc" 或者 "asc". + */ + public static final String IS_ASC = "isAsc"; + + /** + * 分页参数合理化 + */ + public static final String REASONABLE = "reasonable"; + + /** + * 封装分页对象 + */ + public static PageDomain getPageDomain() + { + PageDomain pageDomain = new PageDomain(); + pageDomain.setPageNum(Convert.toInt(ServletUtils.getParameter(PAGE_NUM), 1)); + pageDomain.setPageSize(Convert.toInt(ServletUtils.getParameter(PAGE_SIZE), 10)); + pageDomain.setOrderByColumn(ServletUtils.getParameter(ORDER_BY_COLUMN)); + pageDomain.setIsAsc(ServletUtils.getParameter(IS_ASC)); + pageDomain.setReasonable(ServletUtils.getParameterToBool(REASONABLE)); + return pageDomain; + } + + public static PageDomain buildPageRequest() + { + return getPageDomain(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java new file mode 100644 index 0000000..44e80d8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/redis/RedisCache.java @@ -0,0 +1,268 @@ +package com.ruoyi.common.core.redis; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundSetOperations; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ValueOperations; +import org.springframework.stereotype.Component; + +/** + * spring redis 工具类 + * + * @author ruoyi + **/ +@SuppressWarnings(value = { "unchecked", "rawtypes" }) +@Component +public class RedisCache +{ + @Autowired + public RedisTemplate redisTemplate; + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + */ + public void setCacheObject(final String key, final T value) + { + redisTemplate.opsForValue().set(key, value); + } + + /** + * 缓存基本的对象,Integer、String、实体类等 + * + * @param key 缓存的键值 + * @param value 缓存的值 + * @param timeout 时间 + * @param timeUnit 时间颗粒度 + */ + public void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) + { + redisTemplate.opsForValue().set(key, value, timeout, timeUnit); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout) + { + return expire(key, timeout, TimeUnit.SECONDS); + } + + /** + * 设置有效时间 + * + * @param key Redis键 + * @param timeout 超时时间 + * @param unit 时间单位 + * @return true=设置成功;false=设置失败 + */ + public boolean expire(final String key, final long timeout, final TimeUnit unit) + { + return redisTemplate.expire(key, timeout, unit); + } + + /** + * 获取有效时间 + * + * @param key Redis键 + * @return 有效时间 + */ + public long getExpire(final String key) + { + return redisTemplate.getExpire(key); + } + + /** + * 判断 key是否存在 + * + * @param key 键 + * @return true 存在 false不存在 + */ + public Boolean hasKey(String key) + { + return redisTemplate.hasKey(key); + } + + /** + * 获得缓存的基本对象。 + * + * @param key 缓存键值 + * @return 缓存键值对应的数据 + */ + public T getCacheObject(final String key) + { + ValueOperations operation = redisTemplate.opsForValue(); + return operation.get(key); + } + + /** + * 删除单个对象 + * + * @param key + */ + public boolean deleteObject(final String key) + { + return redisTemplate.delete(key); + } + + /** + * 删除集合对象 + * + * @param collection 多个对象 + * @return + */ + public boolean deleteObject(final Collection collection) + { + return redisTemplate.delete(collection) > 0; + } + + /** + * 缓存List数据 + * + * @param key 缓存的键值 + * @param dataList 待缓存的List数据 + * @return 缓存的对象 + */ + public long setCacheList(final String key, final List dataList) + { + Long count = redisTemplate.opsForList().rightPushAll(key, dataList); + return count == null ? 0 : count; + } + + /** + * 获得缓存的list对象 + * + * @param key 缓存的键值 + * @return 缓存键值对应的数据 + */ + public List getCacheList(final String key) + { + return redisTemplate.opsForList().range(key, 0, -1); + } + + /** + * 缓存Set + * + * @param key 缓存键值 + * @param dataSet 缓存的数据 + * @return 缓存数据的对象 + */ + public BoundSetOperations setCacheSet(final String key, final Set dataSet) + { + BoundSetOperations setOperation = redisTemplate.boundSetOps(key); + Iterator it = dataSet.iterator(); + while (it.hasNext()) + { + setOperation.add(it.next()); + } + return setOperation; + } + + /** + * 获得缓存的set + * + * @param key + * @return + */ + public Set getCacheSet(final String key) + { + return redisTemplate.opsForSet().members(key); + } + + /** + * 缓存Map + * + * @param key + * @param dataMap + */ + public void setCacheMap(final String key, final Map dataMap) + { + if (dataMap != null) { + redisTemplate.opsForHash().putAll(key, dataMap); + } + } + + /** + * 获得缓存的Map + * + * @param key + * @return + */ + public Map getCacheMap(final String key) + { + return redisTemplate.opsForHash().entries(key); + } + + /** + * 往Hash中存入数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @param value 值 + */ + public void setCacheMapValue(final String key, final String hKey, final T value) + { + redisTemplate.opsForHash().put(key, hKey, value); + } + + /** + * 获取Hash中的数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return Hash中的对象 + */ + public T getCacheMapValue(final String key, final String hKey) + { + HashOperations opsForHash = redisTemplate.opsForHash(); + return opsForHash.get(key, hKey); + } + + /** + * 获取多个Hash中的数据 + * + * @param key Redis键 + * @param hKeys Hash键集合 + * @return Hash对象集合 + */ + public List getMultiCacheMapValue(final String key, final Collection hKeys) + { + return redisTemplate.opsForHash().multiGet(key, hKeys); + } + + /** + * 删除Hash中的某条数据 + * + * @param key Redis键 + * @param hKey Hash键 + * @return 是否成功 + */ + public boolean deleteCacheMapValue(final String key, final String hKey) + { + return redisTemplate.opsForHash().delete(key, hKey) > 0; + } + + /** + * 获得缓存的基本对象列表 + * + * @param pattern 字符串前缀 + * @return 对象列表 + */ + public Collection keys(final String pattern) + { + return redisTemplate.keys(pattern); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java new file mode 100644 index 0000000..84124aa --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/CharsetKit.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.core.text; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符集工具类 + * + * @author ruoyi + */ +public class CharsetKit +{ + /** ISO-8859-1 */ + public static final String ISO_8859_1 = "ISO-8859-1"; + /** UTF-8 */ + public static final String UTF_8 = "UTF-8"; + /** GBK */ + public static final String GBK = "GBK"; + + /** ISO-8859-1 */ + public static final Charset CHARSET_ISO_8859_1 = Charset.forName(ISO_8859_1); + /** UTF-8 */ + public static final Charset CHARSET_UTF_8 = Charset.forName(UTF_8); + /** GBK */ + public static final Charset CHARSET_GBK = Charset.forName(GBK); + + /** + * 转换为Charset对象 + * + * @param charset 字符集,为空则返回默认字符集 + * @return Charset + */ + public static Charset charset(String charset) + { + return StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, String srcCharset, String destCharset) + { + return convert(source, Charset.forName(srcCharset), Charset.forName(destCharset)); + } + + /** + * 转换字符串的字符集编码 + * + * @param source 字符串 + * @param srcCharset 源字符集,默认ISO-8859-1 + * @param destCharset 目标字符集,默认UTF-8 + * @return 转换后的字符集 + */ + public static String convert(String source, Charset srcCharset, Charset destCharset) + { + if (null == srcCharset) + { + srcCharset = StandardCharsets.ISO_8859_1; + } + + if (null == destCharset) + { + destCharset = StandardCharsets.UTF_8; + } + + if (StringUtils.isEmpty(source) || srcCharset.equals(destCharset)) + { + return source; + } + return new String(source.getBytes(srcCharset), destCharset); + } + + /** + * @return 系统字符集编码 + */ + public static String systemCharset() + { + return Charset.defaultCharset().name(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java new file mode 100644 index 0000000..a87828a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/Convert.java @@ -0,0 +1,1018 @@ +package com.ruoyi.common.core.text; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.text.NumberFormat; +import java.util.Set; +import com.ruoyi.common.utils.StringUtils; + +/** + * 类型转换器 + * + * @author ruoyi + */ +public class Convert +{ + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static String toStr(Object value, String defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof String) + { + return (String) value; + } + return value.toString(); + } + + /** + * 转换为字符串
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static String toStr(Object value) + { + return toStr(value, null); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Character toChar(Object value, Character defaultValue) + { + if (null == value) + { + return defaultValue; + } + if (value instanceof Character) + { + return (Character) value; + } + + final String valueStr = toStr(value, null); + return StringUtils.isEmpty(valueStr) ? defaultValue : valueStr.charAt(0); + } + + /** + * 转换为字符
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Character toChar(Object value) + { + return toChar(value, null); + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Byte toByte(Object value, Byte defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Byte) + { + return (Byte) value; + } + if (value instanceof Number) + { + return ((Number) value).byteValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Byte.parseByte(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为byte
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Byte toByte(Object value) + { + return toByte(value, null); + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Short toShort(Object value, Short defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Short) + { + return (Short) value; + } + if (value instanceof Number) + { + return ((Number) value).shortValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Short.parseShort(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Short
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Short toShort(Object value) + { + return toShort(value, null); + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Number toNumber(Object value, Number defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Number) + { + return (Number) value; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return NumberFormat.getInstance().parse(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Number
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Number toNumber(Object value) + { + return toNumber(value, null); + } + + /** + * 转换为int
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Integer toInt(Object value, Integer defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Integer) + { + return (Integer) value; + } + if (value instanceof Number) + { + return ((Number) value).intValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Integer.parseInt(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为int
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Integer toInt(Object value) + { + return toInt(value, null); + } + + /** + * 转换为Integer数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String str) + { + return toIntArray(",", str); + } + + /** + * 转换为Long数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String str) + { + return toLongArray(",", str); + } + + /** + * 转换为Integer数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static Integer[] toIntArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Integer[] {}; + } + String[] arr = str.split(split); + final Integer[] ints = new Integer[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Integer v = toInt(arr[i], 0); + ints[i] = v; + } + return ints; + } + + /** + * 转换为Long数组
+ * + * @param split 分隔符 + * @param str 被转换的值 + * @return 结果 + */ + public static Long[] toLongArray(String split, String str) + { + if (StringUtils.isEmpty(str)) + { + return new Long[] {}; + } + String[] arr = str.split(split); + final Long[] longs = new Long[arr.length]; + for (int i = 0; i < arr.length; i++) + { + final Long v = toLong(arr[i], null); + longs[i] = v; + } + return longs; + } + + /** + * 转换为String数组
+ * + * @param str 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String str) + { + if (StringUtils.isEmpty(str)) + { + return new String[] {}; + } + return toStrArray(",", str); + } + + /** + * 转换为String数组
+ * + * @param split 分隔符 + * @param split 被转换的值 + * @return 结果 + */ + public static String[] toStrArray(String split, String str) + { + return str.split(split); + } + + /** + * 转换为long
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Long toLong(Object value, Long defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Long) + { + return (Long) value; + } + if (value instanceof Number) + { + return ((Number) value).longValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).longValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为long
+ * 如果给定的值为null,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Long toLong(Object value) + { + return toLong(value, null); + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Double toDouble(Object value, Double defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Double) + { + return (Double) value; + } + if (value instanceof Number) + { + return ((Number) value).doubleValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + // 支持科学计数法 + return new BigDecimal(valueStr.trim()).doubleValue(); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为double
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Double toDouble(Object value) + { + return toDouble(value, null); + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Float toFloat(Object value, Float defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Float) + { + return (Float) value; + } + if (value instanceof Number) + { + return ((Number) value).floatValue(); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Float.parseFloat(valueStr.trim()); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Float
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Float toFloat(Object value) + { + return toFloat(value, null); + } + + /** + * 转换为boolean
+ * String支持的值为:true、false、yes、ok、no、1、0、是、否, 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static Boolean toBool(Object value, Boolean defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof Boolean) + { + return (Boolean) value; + } + String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + valueStr = valueStr.trim().toLowerCase(); + switch (valueStr) + { + case "true": + case "yes": + case "ok": + case "1": + case "是": + return true; + case "false": + case "no": + case "0": + case "否": + return false; + default: + return defaultValue; + } + } + + /** + * 转换为boolean
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static Boolean toBool(Object value) + { + return toBool(value, null); + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * + * @param clazz Enum的Class + * @param value 值 + * @param defaultValue 默认值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value, E defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (clazz.isAssignableFrom(value.getClass())) + { + @SuppressWarnings("unchecked") + E myE = (E) value; + return myE; + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return Enum.valueOf(clazz, valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为Enum对象
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * + * @param clazz Enum的Class + * @param value 值 + * @return Enum + */ + public static > E toEnum(Class clazz, Object value) + { + return toEnum(clazz, value, null); + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value, BigInteger defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigInteger) + { + return (BigInteger) value; + } + if (value instanceof Long) + { + return BigInteger.valueOf((Long) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigInteger(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigInteger
+ * 如果给定的值为空,或者转换失败,返回默认值null
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigInteger toBigInteger(Object value) + { + return toBigInteger(value, null); + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @param defaultValue 转换错误时的默认值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value, BigDecimal defaultValue) + { + if (value == null) + { + return defaultValue; + } + if (value instanceof BigDecimal) + { + return (BigDecimal) value; + } + if (value instanceof Long) + { + return new BigDecimal((Long) value); + } + if (value instanceof Double) + { + return BigDecimal.valueOf((Double) value); + } + if (value instanceof Integer) + { + return new BigDecimal((Integer) value); + } + final String valueStr = toStr(value, null); + if (StringUtils.isEmpty(valueStr)) + { + return defaultValue; + } + try + { + return new BigDecimal(valueStr); + } + catch (Exception e) + { + return defaultValue; + } + } + + /** + * 转换为BigDecimal
+ * 如果给定的值为空,或者转换失败,返回默认值
+ * 转换失败不会报错 + * + * @param value 被转换的值 + * @return 结果 + */ + public static BigDecimal toBigDecimal(Object value) + { + return toBigDecimal(value, null); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) + { + return str(obj, CharsetKit.CHARSET_UTF_8); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charsetName 字符集 + * @return 字符串 + */ + public static String str(Object obj, String charsetName) + { + return str(obj, Charset.forName(charsetName)); + } + + /** + * 将对象转为字符串
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 2、对象数组会调用Arrays.toString方法 + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) + { + if (null == obj) + { + return null; + } + + if (obj instanceof String) + { + return (String) obj; + } + else if (obj instanceof byte[] || obj instanceof Byte[]) + { + if (obj instanceof byte[]) + { + return str((byte[]) obj, charset); + } + else + { + Byte[] bytes = (Byte[]) obj; + int length = bytes.length; + byte[] dest = new byte[length]; + for (int i = 0; i < length; i++) + { + dest[i] = bytes[i]; + } + return str(dest, charset); + } + } + else if (obj instanceof ByteBuffer) + { + return str((ByteBuffer) obj, charset); + } + return obj.toString(); + } + + /** + * 将byte数组转为字符串 + * + * @param bytes byte数组 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(byte[] bytes, String charset) + { + return str(bytes, StringUtils.isEmpty(charset) ? Charset.defaultCharset() : Charset.forName(charset)); + } + + /** + * 解码字节码 + * + * @param data 字符串 + * @param charset 字符集,如果此字段为空,则解码的结果取决于平台 + * @return 解码后的字符串 + */ + public static String str(byte[] data, Charset charset) + { + if (data == null) + { + return null; + } + + if (null == charset) + { + return new String(data); + } + return new String(data, charset); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, String charset) + { + if (data == null) + { + return null; + } + + return str(data, Charset.forName(charset)); + } + + /** + * 将编码的byteBuffer数据转换为字符串 + * + * @param data 数据 + * @param charset 字符集,如果为空使用当前系统字符集 + * @return 字符串 + */ + public static String str(ByteBuffer data, Charset charset) + { + if (null == charset) + { + charset = Charset.defaultCharset(); + } + return charset.decode(data).toString(); + } + + // ----------------------------------------------------------------------- 全角半角转换 + /** + * 半角转全角 + * + * @param input String. + * @return 全角字符串. + */ + public static String toSBC(String input) + { + return toSBC(input, null); + } + + /** + * 半角转全角 + * + * @param input String + * @param notConvertSet 不替换的字符集合 + * @return 全角字符串. + */ + public static String toSBC(String input, Set notConvertSet) + { + char[] c = input.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == ' ') + { + c[i] = '\u3000'; + } + else if (c[i] < '\177') + { + c[i] = (char) (c[i] + 65248); + + } + } + return new String(c); + } + + /** + * 全角转半角 + * + * @param input String. + * @return 半角字符串 + */ + public static String toDBC(String input) + { + return toDBC(input, null); + } + + /** + * 替换全角为半角 + * + * @param text 文本 + * @param notConvertSet 不替换的字符集合 + * @return 替换后的字符 + */ + public static String toDBC(String text, Set notConvertSet) + { + char[] c = text.toCharArray(); + for (int i = 0; i < c.length; i++) + { + if (null != notConvertSet && notConvertSet.contains(c[i])) + { + // 跳过不替换的字符 + continue; + } + + if (c[i] == '\u3000') + { + c[i] = ' '; + } + else if (c[i] > '\uFF00' && c[i] < '\uFF5F') + { + c[i] = (char) (c[i] - 65248); + } + } + return new String(c); + } + + /** + * 数字金额大写转换 先写个完整的然后将如零拾替换成零 + * + * @param n 数字 + * @return 中文大写数字 + */ + public static String digitUppercase(double n) + { + String[] fraction = { "角", "分" }; + String[] digit = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" }; + String[][] unit = { { "元", "万", "亿" }, { "", "拾", "佰", "仟" } }; + + String head = n < 0 ? "负" : ""; + n = Math.abs(n); + + String s = ""; + for (int i = 0; i < fraction.length; i++) + { + // 优化double计算精度丢失问题 + BigDecimal nNum = new BigDecimal(n); + BigDecimal decimal = new BigDecimal(10); + BigDecimal scale = nNum.multiply(decimal).setScale(2, RoundingMode.HALF_EVEN); + double d = scale.doubleValue(); + s += (digit[(int) (Math.floor(d * Math.pow(10, i)) % 10)] + fraction[i]).replaceAll("(零.)+", ""); + } + if (s.length() < 1) + { + s = "整"; + } + int integerPart = (int) Math.floor(n); + + for (int i = 0; i < unit[0].length && integerPart > 0; i++) + { + String p = ""; + for (int j = 0; j < unit[1].length && n > 0; j++) + { + p = digit[integerPart % 10] + unit[1][j] + p; + integerPart = integerPart / 10; + } + s = p.replaceAll("(零.)*零$", "").replaceAll("^$", "零") + unit[0][i] + s; + } + return head + s.replaceAll("(零.)*零元", "元").replaceFirst("(零.)+", "").replaceAll("(零.)+", "零").replaceAll("^整$", "零元整"); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java new file mode 100644 index 0000000..c78ac77 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/text/StrFormatter.java @@ -0,0 +1,92 @@ +package com.ruoyi.common.core.text; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 字符串格式化 + * + * @author ruoyi + */ +public class StrFormatter +{ + public static final String EMPTY_JSON = "{}"; + public static final char C_BACKSLASH = '\\'; + public static final char C_DELIM_START = '{'; + public static final char C_DELIM_END = '}'; + + /** + * 格式化字符串
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param strPattern 字符串模板 + * @param argArray 参数列表 + * @return 结果 + */ + public static String format(final String strPattern, final Object... argArray) + { + if (StringUtils.isEmpty(strPattern) || StringUtils.isEmpty(argArray)) + { + return strPattern; + } + final int strPatternLength = strPattern.length(); + + // 初始化定义好的长度以获得更好的性能 + StringBuilder sbuf = new StringBuilder(strPatternLength + 50); + + int handledPosition = 0; + int delimIndex;// 占位符所在位置 + for (int argIndex = 0; argIndex < argArray.length; argIndex++) + { + delimIndex = strPattern.indexOf(EMPTY_JSON, handledPosition); + if (delimIndex == -1) + { + if (handledPosition == 0) + { + return strPattern; + } + else + { // 字符串模板剩余部分不再包含占位符,加入剩余部分后返回结果 + sbuf.append(strPattern, handledPosition, strPatternLength); + return sbuf.toString(); + } + } + else + { + if (delimIndex > 0 && strPattern.charAt(delimIndex - 1) == C_BACKSLASH) + { + if (delimIndex > 1 && strPattern.charAt(delimIndex - 2) == C_BACKSLASH) + { + // 转义符之前还有一个转义符,占位符依旧有效 + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + else + { + // 占位符被转义 + argIndex--; + sbuf.append(strPattern, handledPosition, delimIndex - 1); + sbuf.append(C_DELIM_START); + handledPosition = delimIndex + 1; + } + } + else + { + // 正常占位符 + sbuf.append(strPattern, handledPosition, delimIndex); + sbuf.append(Convert.utf8Str(argArray[argIndex])); + handledPosition = delimIndex + 2; + } + } + } + // 加入最后一个占位符后所有的字符 + sbuf.append(strPattern, handledPosition, strPattern.length()); + + return sbuf.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/domain/ExtraField.java b/ruoyi-common/src/main/java/com/ruoyi/common/domain/ExtraField.java new file mode 100644 index 0000000..6cee605 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/domain/ExtraField.java @@ -0,0 +1,78 @@ +package com.ruoyi.common.domain; + +import com.ruoyi.common.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotEmpty; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ExtraField { + @Excel(name = "序号") + private String seqNo; + + @Excel(name = "子件") + private String subItem; + + @Excel(name = "物料编码") + @NotEmpty(message = "物料GUID不能为空") + private String productId; + + @Excel(name = "物料描述") + private String materialDesc; + + @Excel(name = "英文描述") + private String englishDesc; + + @Excel(name = "数量") + @NotEmpty(message = "物料数量不能为空") + private String quantity; + + @Excel(name = "单位") + private String unit; + + @Excel(name = "单件净重Unit Net Weight") + private String unitNetWeight; + + @Excel(name = "总净重Total Net Weight") + private String totalNetWeight; + + @Excel(name = "箱号Carton NO.") + private String cartonNo; + + @Excel(name = "包装方式") + private String packingMethod; + + @Excel(name = "长(MM)") + private String length; + + @Excel(name = "宽(MM)") + private String width; + + @Excel(name = "高(MM)") + private String height; + + @Excel(name = "体积m³") + private String volume; + + @Excel(name = "净重(KG)NET WEIGHT") + private String netWeight; + + @Excel(name = "毛重(KG)GROSS WEIGHT") + private String grossWeight; + + @Excel(name = "单价") + private String unitPrice; + + @Excel(name = "总价") + private String totalPrice; + + @Excel(name = "备注") + private String remark; + + @Excel(name = "批次号") + private String lotNo; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java new file mode 100644 index 0000000..10b7306 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessStatus.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 操作状态 + * + * @author ruoyi + * + */ +public enum BusinessStatus +{ + /** + * 成功 + */ + SUCCESS, + + /** + * 失败 + */ + FAIL, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java new file mode 100644 index 0000000..2e17c4a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/BusinessType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.enums; + +/** + * 业务操作类型 + * + * @author ruoyi + */ +public enum BusinessType +{ + /** + * 其它 + */ + OTHER, + + /** + * 新增 + */ + INSERT, + + /** + * 修改 + */ + UPDATE, + + /** + * 删除 + */ + DELETE, + + /** + * 授权 + */ + GRANT, + + /** + * 导出 + */ + EXPORT, + + /** + * 导入 + */ + IMPORT, + + /** + * 强退 + */ + FORCE, + + /** + * 生成代码 + */ + GENCODE, + + /** + * 清空数据 + */ + CLEAN, +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java new file mode 100644 index 0000000..0d945be --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DataSourceType.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.enums; + +/** + * 数据源 + * + * @author ruoyi + */ +public enum DataSourceType +{ + /** + * 主库 + */ + MASTER, + + /** + * 从库 + */ + SLAVE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java new file mode 100644 index 0000000..07f02ee --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/DesensitizedType.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.enums; + +import java.util.function.Function; +import com.ruoyi.common.utils.DesensitizedUtil; + +/** + * 脱敏类型 + * + * @author ruoyi + */ +public enum DesensitizedType +{ + /** + * 姓名,第2位星号替换 + */ + USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)", "$1*$2")), + + /** + * 密码,全部字符都用*代替 + */ + PASSWORD(DesensitizedUtil::password), + + /** + * 身份证,中间10位星号替换 + */ + ID_CARD(s -> s.replaceAll("(\\d{4})\\d{10}(\\d{3}[Xx]|\\d{4})", "$1** **** ****$2")), + + /** + * 手机号,中间4位星号替换 + */ + PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")), + + /** + * 电子邮箱,仅显示第一个字母和@后面的地址显示,其他星号替换 + */ + EMAIL(s -> s.replaceAll("(^.)[^@]*(@.*$)", "$1****$2")), + + /** + * 银行卡号,保留最后4位,其他星号替换 + */ + BANK_CARD(s -> s.replaceAll("\\d{15}(\\d{3})", "**** **** **** **** $1")), + + /** + * 车牌号码,包含普通车辆、新能源车辆 + */ + CAR_LICENSE(DesensitizedUtil::carLicense); + + private final Function desensitizer; + + DesensitizedType(Function desensitizer) + { + this.desensitizer = desensitizer; + } + + public Function desensitizer() + { + return desensitizer; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java new file mode 100644 index 0000000..be6f739 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/HttpMethod.java @@ -0,0 +1,36 @@ +package com.ruoyi.common.enums; + +import java.util.HashMap; +import java.util.Map; +import org.springframework.lang.Nullable; + +/** + * 请求方式 + * + * @author ruoyi + */ +public enum HttpMethod +{ + GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE; + + private static final Map mappings = new HashMap<>(16); + + static + { + for (HttpMethod httpMethod : values()) + { + mappings.put(httpMethod.name(), httpMethod); + } + } + + @Nullable + public static HttpMethod resolve(@Nullable String method) + { + return (method != null ? mappings.get(method) : null); + } + + public boolean matches(String method) + { + return (this == resolve(method)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java new file mode 100644 index 0000000..c609fd8 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/LimitType.java @@ -0,0 +1,20 @@ +package com.ruoyi.common.enums; + +/** + * 限流类型 + * + * @author ruoyi + */ + +public enum LimitType +{ + /** + * 默认策略全局限流 + */ + DEFAULT, + + /** + * 根据请求者IP进行限流 + */ + IP +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java new file mode 100644 index 0000000..bdd143c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/OperatorType.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.enums; + +/** + * 操作人类别 + * + * @author ruoyi + */ +public enum OperatorType +{ + /** + * 其它 + */ + OTHER, + + /** + * 后台用户 + */ + MANAGE, + + /** + * 手机端用户 + */ + MOBILE +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java new file mode 100644 index 0000000..d7ff44a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/UserStatus.java @@ -0,0 +1,30 @@ +package com.ruoyi.common.enums; + +/** + * 用户状态 + * + * @author ruoyi + */ +public enum UserStatus +{ + OK("0", "正常"), DISABLE("1", "停用"), DELETED("2", "删除"); + + private final String code; + private final String info; + + UserStatus(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java new file mode 100644 index 0000000..f6ad2ab --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/DemoModeException.java @@ -0,0 +1,15 @@ +package com.ruoyi.common.exception; + +/** + * 演示模式异常 + * + * @author ruoyi + */ +public class DemoModeException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + public DemoModeException() + { + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java new file mode 100644 index 0000000..81a71b5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/GlobalException.java @@ -0,0 +1,58 @@ +package com.ruoyi.common.exception; + +/** + * 全局异常 + * + * @author ruoyi + */ +public class GlobalException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public GlobalException() + { + } + + public GlobalException(String message) + { + this.message = message; + } + + public String getDetailMessage() + { + return detailMessage; + } + + public GlobalException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } + + @Override + public String getMessage() + { + return message; + } + + public GlobalException setMessage(String message) + { + this.message = message; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java new file mode 100644 index 0000000..fcc7ab6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/ServiceException.java @@ -0,0 +1,74 @@ +package com.ruoyi.common.exception; + +/** + * 业务异常 + * + * @author ruoyi + */ +public final class ServiceException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 错误码 + */ + private Integer code; + + /** + * 错误提示 + */ + private String message; + + /** + * 错误明细,内部调试错误 + * + * 和 {@link CommonResult#getDetailMessage()} 一致的设计 + */ + private String detailMessage; + + /** + * 空构造方法,避免反序列化问题 + */ + public ServiceException() + { + } + + public ServiceException(String message) + { + this.message = message; + } + + public ServiceException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public String getDetailMessage() + { + return detailMessage; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } + + public ServiceException setMessage(String message) + { + this.message = message; + return this; + } + + public ServiceException setDetailMessage(String detailMessage) + { + this.detailMessage = detailMessage; + return this; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java new file mode 100644 index 0000000..980fa46 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/UtilException.java @@ -0,0 +1,26 @@ +package com.ruoyi.common.exception; + +/** + * 工具类异常 + * + * @author ruoyi + */ +public class UtilException extends RuntimeException +{ + private static final long serialVersionUID = 8247610319171014183L; + + public UtilException(Throwable e) + { + super(e.getMessage(), e); + } + + public UtilException(String message) + { + super(message); + } + + public UtilException(String message, Throwable throwable) + { + super(message, throwable); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java new file mode 100644 index 0000000..b55d72e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/base/BaseException.java @@ -0,0 +1,97 @@ +package com.ruoyi.common.exception.base; + +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 基础异常 + * + * @author ruoyi + */ +public class BaseException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + /** + * 所属模块 + */ + private String module; + + /** + * 错误码 + */ + private String code; + + /** + * 错误码对应的参数 + */ + private Object[] args; + + /** + * 错误消息 + */ + private String defaultMessage; + + public BaseException(String module, String code, Object[] args, String defaultMessage) + { + this.module = module; + this.code = code; + this.args = args; + this.defaultMessage = defaultMessage; + } + + public BaseException(String module, String code, Object[] args) + { + this(module, code, args, null); + } + + public BaseException(String module, String defaultMessage) + { + this(module, null, null, defaultMessage); + } + + public BaseException(String code, Object[] args) + { + this(null, code, args, null); + } + + public BaseException(String defaultMessage) + { + this(null, null, null, defaultMessage); + } + + @Override + public String getMessage() + { + String message = null; + if (!StringUtils.isEmpty(code)) + { + message = MessageUtils.message(code, args); + } + if (message == null) + { + message = defaultMessage; + } + return message; + } + + public String getModule() + { + return module; + } + + public String getCode() + { + return code; + } + + public Object[] getArgs() + { + return args; + } + + public String getDefaultMessage() + { + return defaultMessage; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java new file mode 100644 index 0000000..871f09b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileException.java @@ -0,0 +1,19 @@ +package com.ruoyi.common.exception.file; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 文件信息异常类 + * + * @author ruoyi + */ +public class FileException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public FileException(String code, Object[] args) + { + super("file", code, args, null); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java new file mode 100644 index 0000000..70e0ec9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileNameLengthLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名称超长限制异常类 + * + * @author ruoyi + */ +public class FileNameLengthLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileNameLengthLimitExceededException(int defaultFileNameLength) + { + super("upload.filename.exceed.length", new Object[] { defaultFileNameLength }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java new file mode 100644 index 0000000..ec6ab05 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileSizeLimitExceededException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.file; + +/** + * 文件名大小限制异常类 + * + * @author ruoyi + */ +public class FileSizeLimitExceededException extends FileException +{ + private static final long serialVersionUID = 1L; + + public FileSizeLimitExceededException(long defaultMaxSize) + { + super("upload.exceed.maxSize", new Object[] { defaultMaxSize }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java new file mode 100644 index 0000000..f45e7ef --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/FileUploadException.java @@ -0,0 +1,61 @@ +package com.ruoyi.common.exception.file; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * 文件上传异常类 + * + * @author ruoyi + */ +public class FileUploadException extends Exception +{ + + private static final long serialVersionUID = 1L; + + private final Throwable cause; + + public FileUploadException() + { + this(null, null); + } + + public FileUploadException(final String msg) + { + this(msg, null); + } + + public FileUploadException(String msg, Throwable cause) + { + super(msg); + this.cause = cause; + } + + @Override + public void printStackTrace(PrintStream stream) + { + super.printStackTrace(stream); + if (cause != null) + { + stream.println("Caused by:"); + cause.printStackTrace(stream); + } + } + + @Override + public void printStackTrace(PrintWriter writer) + { + super.printStackTrace(writer); + if (cause != null) + { + writer.println("Caused by:"); + cause.printStackTrace(writer); + } + } + + @Override + public Throwable getCause() + { + return cause; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java new file mode 100644 index 0000000..011f308 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/file/InvalidExtensionException.java @@ -0,0 +1,80 @@ +package com.ruoyi.common.exception.file; + +import java.util.Arrays; + +/** + * 文件上传 误异常类 + * + * @author ruoyi + */ +public class InvalidExtensionException extends FileUploadException +{ + private static final long serialVersionUID = 1L; + + private String[] allowedExtension; + private String extension; + private String filename; + + public InvalidExtensionException(String[] allowedExtension, String extension, String filename) + { + super("文件[" + filename + "]后缀[" + extension + "]不正确,请上传" + Arrays.toString(allowedExtension) + "格式"); + this.allowedExtension = allowedExtension; + this.extension = extension; + this.filename = filename; + } + + public String[] getAllowedExtension() + { + return allowedExtension; + } + + public String getExtension() + { + return extension; + } + + public String getFilename() + { + return filename; + } + + public static class InvalidImageExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidImageExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidFlashExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidFlashExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidMediaExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidMediaExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } + + public static class InvalidVideoExtensionException extends InvalidExtensionException + { + private static final long serialVersionUID = 1L; + + public InvalidVideoExtensionException(String[] allowedExtension, String extension, String filename) + { + super(allowedExtension, extension, filename); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java new file mode 100644 index 0000000..a567b40 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/job/TaskException.java @@ -0,0 +1,34 @@ +package com.ruoyi.common.exception.job; + +/** + * 计划策略异常 + * + * @author ruoyi + */ +public class TaskException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Code code; + + public TaskException(String msg, Code code) + { + this(msg, code, null); + } + + public TaskException(String msg, Code code, Exception nestedEx) + { + super(msg, nestedEx); + this.code = code; + } + + public Code getCode() + { + return code; + } + + public enum Code + { + TASK_EXISTS, NO_TASK_EXISTS, TASK_ALREADY_STARTED, UNKNOWN, CONFIG_ERROR, TASK_NODE_NOT_AVAILABLE + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java new file mode 100644 index 0000000..2bf5038 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/BlackListException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 黑名单IP异常类 + * + * @author ruoyi + */ +public class BlackListException extends UserException +{ + private static final long serialVersionUID = 1L; + + public BlackListException() + { + super("login.blocked", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java new file mode 100644 index 0000000..389dbc7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码错误异常类 + * + * @author ruoyi + */ +public class CaptchaException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaException() + { + super("user.jcaptcha.error", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java new file mode 100644 index 0000000..85f9486 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/CaptchaExpireException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 验证码失效异常类 + * + * @author ruoyi + */ +public class CaptchaExpireException extends UserException +{ + private static final long serialVersionUID = 1L; + + public CaptchaExpireException() + { + super("user.jcaptcha.expire", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java new file mode 100644 index 0000000..c292d70 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserException.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.exception.user; + +import com.ruoyi.common.exception.base.BaseException; + +/** + * 用户信息异常类 + * + * @author ruoyi + */ +public class UserException extends BaseException +{ + private static final long serialVersionUID = 1L; + + public UserException(String code, Object[] args) + { + super("user", code, args, null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java new file mode 100644 index 0000000..eff8181 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserNotExistsException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户不存在异常类 + * + * @author ruoyi + */ +public class UserNotExistsException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserNotExistsException() + { + super("user.not.exists", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java new file mode 100644 index 0000000..a7f3e5f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordNotMatchException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户密码不正确或不符合规范异常类 + * + * @author ruoyi + */ +public class UserPasswordNotMatchException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordNotMatchException() + { + super("user.password.not.match", null); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java new file mode 100644 index 0000000..c887cf1 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/exception/user/UserPasswordRetryLimitExceedException.java @@ -0,0 +1,16 @@ +package com.ruoyi.common.exception.user; + +/** + * 用户错误最大次数异常类 + * + * @author ruoyi + */ +public class UserPasswordRetryLimitExceedException extends UserException +{ + private static final long serialVersionUID = 1L; + + public UserPasswordRetryLimitExceedException(int retryLimitCount, int lockTime) + { + super("user.password.retry.limit.exceed", new Object[] { retryLimitCount, lockTime }); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java new file mode 100644 index 0000000..e1e431b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/PropertyPreExcludeFilter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.filter; + +import com.alibaba.fastjson2.filter.SimplePropertyPreFilter; + +/** + * 排除JSON敏感属性 + * + * @author ruoyi + */ +public class PropertyPreExcludeFilter extends SimplePropertyPreFilter +{ + public PropertyPreExcludeFilter() + { + } + + public PropertyPreExcludeFilter addExcludes(String... filters) + { + for (int i = 0; i < filters.length; i++) + { + this.getExcludes().add(filters[i]); + } + return this; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java new file mode 100644 index 0000000..a1bcfe2 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatableFilter.java @@ -0,0 +1,52 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; + +/** + * Repeatable 过滤器 + * + * @author ruoyi + */ +public class RepeatableFilter implements Filter +{ + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + ServletRequest requestWrapper = null; + if (request instanceof HttpServletRequest + && StringUtils.startsWithIgnoreCase(request.getContentType(), MediaType.APPLICATION_JSON_VALUE)) + { + requestWrapper = new RepeatedlyRequestWrapper((HttpServletRequest) request, response); + } + if (null == requestWrapper) + { + chain.doFilter(request, response); + } + else + { + chain.doFilter(requestWrapper, response); + } + } + + @Override + public void destroy() + { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java new file mode 100644 index 0000000..407d1ba --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/RepeatedlyRequestWrapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.filter; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.common.constant.Constants; + +/** + * 构建可重复读取inputStream的request + * + * @author ruoyi + */ +public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper +{ + private final byte[] body; + + public RepeatedlyRequestWrapper(HttpServletRequest request, ServletResponse response) throws IOException + { + super(request); + request.setCharacterEncoding(Constants.UTF8); + response.setCharacterEncoding(Constants.UTF8); + + body = HttpHelper.getBodyString(request).getBytes(Constants.UTF8); + } + + @Override + public BufferedReader getReader() throws IOException + { + return new BufferedReader(new InputStreamReader(getInputStream())); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + final ByteArrayInputStream bais = new ByteArrayInputStream(body); + return new ServletInputStream() + { + @Override + public int read() throws IOException + { + return bais.read(); + } + + @Override + public int available() throws IOException + { + return body.length; + } + + @Override + public boolean isFinished() + { + return false; + } + + @Override + public boolean isReady() + { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) + { + + } + }; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java new file mode 100644 index 0000000..5c4cbe4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssFilter.java @@ -0,0 +1,75 @@ +package com.ruoyi.common.filter; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.enums.HttpMethod; + +/** + * 防止XSS攻击的过滤器 + * + * @author ruoyi + */ +public class XssFilter implements Filter +{ + /** + * 排除链接 + */ + public List excludes = new ArrayList<>(); + + @Override + public void init(FilterConfig filterConfig) throws ServletException + { + String tempExcludes = filterConfig.getInitParameter("excludes"); + if (StringUtils.isNotEmpty(tempExcludes)) + { + String[] urls = tempExcludes.split(","); + for (String url : urls) + { + excludes.add(url); + } + } + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + HttpServletRequest req = (HttpServletRequest) request; + HttpServletResponse resp = (HttpServletResponse) response; + if (handleExcludeURL(req, resp)) + { + chain.doFilter(request, response); + return; + } + XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) request); + chain.doFilter(xssRequest, response); + } + + private boolean handleExcludeURL(HttpServletRequest request, HttpServletResponse response) + { + String url = request.getServletPath(); + String method = request.getMethod(); + // GET DELETE 不过滤 + if (method == null || HttpMethod.GET.matches(method) || HttpMethod.DELETE.matches(method)) + { + return true; + } + return StringUtils.matches(url, excludes); + } + + @Override + public void destroy() + { + + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..05149f0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/filter/XssHttpServletRequestWrapper.java @@ -0,0 +1,111 @@ +package com.ruoyi.common.filter; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import javax.servlet.ReadListener; +import javax.servlet.ServletInputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * XSS过滤处理 + * + * @author ruoyi + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper +{ + /** + * @param request + */ + public XssHttpServletRequestWrapper(HttpServletRequest request) + { + super(request); + } + + @Override + public String[] getParameterValues(String name) + { + String[] values = super.getParameterValues(name); + if (values != null) + { + int length = values.length; + String[] escapesValues = new String[length]; + for (int i = 0; i < length; i++) + { + // 防xss攻击和过滤前后空格 + escapesValues[i] = EscapeUtil.clean(values[i]).trim(); + } + return escapesValues; + } + return super.getParameterValues(name); + } + + @Override + public ServletInputStream getInputStream() throws IOException + { + // 非json类型,直接返回 + if (!isJsonRequest()) + { + return super.getInputStream(); + } + + // 为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), "utf-8"); + if (StringUtils.isEmpty(json)) + { + return super.getInputStream(); + } + + // xss过滤 + json = EscapeUtil.clean(json).trim(); + byte[] jsonBytes = json.getBytes("utf-8"); + final ByteArrayInputStream bis = new ByteArrayInputStream(jsonBytes); + return new ServletInputStream() + { + @Override + public boolean isFinished() + { + return true; + } + + @Override + public boolean isReady() + { + return true; + } + + @Override + public int available() throws IOException + { + return jsonBytes.length; + } + + @Override + public void setReadListener(ReadListener readListener) + { + } + + @Override + public int read() throws IOException + { + return bis.read(); + } + }; + } + + /** + * 是否是Json请求 + * + * @param request + */ + public boolean isJsonRequest() + { + String header = super.getHeader(HttpHeaders.CONTENT_TYPE); + return StringUtils.startsWithIgnoreCase(header, MediaType.APPLICATION_JSON_VALUE); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java new file mode 100644 index 0000000..9f95c0f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Arith.java @@ -0,0 +1,113 @@ +package com.ruoyi.common.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 精确的浮点数运算 + * + * @author ruoyi + */ +public class Arith +{ + + /** 默认除法运算精度 */ + private static final int DEF_DIV_SCALE = 10; + + /** 这个类不能实例化 */ + private Arith() + { + } + + /** + * 提供精确的加法运算。 + * @param v1 被加数 + * @param v2 加数 + * @return 两个参数的和 + */ + public static double add(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.add(b2).doubleValue(); + } + + /** + * 提供精确的减法运算。 + * @param v1 被减数 + * @param v2 减数 + * @return 两个参数的差 + */ + public static double sub(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.subtract(b2).doubleValue(); + } + + /** + * 提供精确的乘法运算。 + * @param v1 被乘数 + * @param v2 乘数 + * @return 两个参数的积 + */ + public static double mul(double v1, double v2) + { + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + return b1.multiply(b2).doubleValue(); + } + + /** + * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 + * 小数点以后10位,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @return 两个参数的商 + */ + public static double div(double v1, double v2) + { + return div(v1, v2, DEF_DIV_SCALE); + } + + /** + * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 + * 定精度,以后的数字四舍五入。 + * @param v1 被除数 + * @param v2 除数 + * @param scale 表示表示需要精确到小数点以后几位。 + * @return 两个参数的商 + */ + public static double div(double v1, double v2, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b1 = new BigDecimal(Double.toString(v1)); + BigDecimal b2 = new BigDecimal(Double.toString(v2)); + if (b1.compareTo(BigDecimal.ZERO) == 0) + { + return BigDecimal.ZERO.doubleValue(); + } + return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue(); + } + + /** + * 提供精确的小数位四舍五入处理。 + * @param v 需要四舍五入的数字 + * @param scale 小数点后保留几位 + * @return 四舍五入后的结果 + */ + public static double round(double v, int scale) + { + if (scale < 0) + { + throw new IllegalArgumentException( + "The scale must be a positive integer or zero"); + } + BigDecimal b = new BigDecimal(Double.toString(v)); + return b.divide(BigDecimal.ONE, scale, RoundingMode.HALF_UP).doubleValue(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java new file mode 100644 index 0000000..fb2ae21 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java @@ -0,0 +1,191 @@ +package com.ruoyi.common.utils; + +import java.lang.management.ManagementFactory; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Date; +import org.apache.commons.lang3.time.DateFormatUtils; + +/** + * 时间工具类 + * + * @author ruoyi + */ +public class DateUtils extends org.apache.commons.lang3.time.DateUtils +{ + public static String YYYY = "yyyy"; + + public static String YYYY_MM = "yyyy-MM"; + + public static String YYYY_MM_DD = "yyyy-MM-dd"; + + public static String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; + + public static String YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss"; + + private static String[] parsePatterns = { + "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", + "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM", + "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"}; + + /** + * 获取当前Date型日期 + * + * @return Date() 当前日期 + */ + public static Date getNowDate() + { + return new Date(); + } + + /** + * 获取当前日期, 默认格式为yyyy-MM-dd + * + * @return String + */ + public static String getDate() + { + return dateTimeNow(YYYY_MM_DD); + } + + public static final String getTime() + { + return dateTimeNow(YYYY_MM_DD_HH_MM_SS); + } + + public static final String dateTimeNow() + { + return dateTimeNow(YYYYMMDDHHMMSS); + } + + public static final String dateTimeNow(final String format) + { + return parseDateToStr(format, new Date()); + } + + public static final String dateTime(final Date date) + { + return parseDateToStr(YYYY_MM_DD, date); + } + + public static final String parseDateToStr(final String format, final Date date) + { + return new SimpleDateFormat(format).format(date); + } + + public static final Date dateTime(final String format, final String ts) + { + try + { + return new SimpleDateFormat(format).parse(ts); + } + catch (ParseException e) + { + throw new RuntimeException(e); + } + } + + /** + * 日期路径 即年/月/日 如2018/08/08 + */ + public static final String datePath() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyy/MM/dd"); + } + + /** + * 日期路径 即年/月/日 如20180808 + */ + public static final String dateTime() + { + Date now = new Date(); + return DateFormatUtils.format(now, "yyyyMMdd"); + } + + /** + * 日期型字符串转化为日期 格式 + */ + public static Date parseDate(Object str) + { + if (str == null) + { + return null; + } + try + { + return parseDate(str.toString(), parsePatterns); + } + catch (ParseException e) + { + return null; + } + } + + /** + * 获取服务器启动时间 + */ + public static Date getServerStartDate() + { + long time = ManagementFactory.getRuntimeMXBean().getStartTime(); + return new Date(time); + } + + /** + * 计算相差天数 + */ + public static int differentDaysByMillisecond(Date date1, Date date2) + { + return Math.abs((int) ((date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))); + } + + /** + * 计算时间差 + * + * @param endDate 最后时间 + * @param startTime 开始时间 + * @return 时间差(天/小时/分钟) + */ + public static String timeDistance(Date endDate, Date startTime) + { + long nd = 1000 * 24 * 60 * 60; + long nh = 1000 * 60 * 60; + long nm = 1000 * 60; + // long ns = 1000; + // 获得两个时间的毫秒时间差异 + long diff = endDate.getTime() - startTime.getTime(); + // 计算差多少天 + long day = diff / nd; + // 计算差多少小时 + long hour = diff % nd / nh; + // 计算差多少分钟 + long min = diff % nd % nh / nm; + // 计算差多少秒//输出结果 + // long sec = diff % nd % nh % nm / ns; + return day + "天" + hour + "小时" + min + "分钟"; + } + + /** + * 增加 LocalDateTime ==> Date + */ + public static Date toDate(LocalDateTime temporalAccessor) + { + ZonedDateTime zdt = temporalAccessor.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } + + /** + * 增加 LocalDate ==> Date + */ + public static Date toDate(LocalDate temporalAccessor) + { + LocalDateTime localDateTime = LocalDateTime.of(temporalAccessor, LocalTime.of(0, 0, 0)); + ZonedDateTime zdt = localDateTime.atZone(ZoneId.systemDefault()); + return Date.from(zdt.toInstant()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java new file mode 100644 index 0000000..f8a4c02 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DesensitizedUtil.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils; + +/** + * 脱敏工具类 + * + * @author ruoyi + */ +public class DesensitizedUtil +{ + /** + * 密码的全部字符都用*代替,比如:****** + * + * @param password 密码 + * @return 脱敏后的密码 + */ + public static String password(String password) + { + if (StringUtils.isBlank(password)) + { + return StringUtils.EMPTY; + } + return StringUtils.repeat('*', password.length()); + } + + /** + * 车牌中间用*代替,如果是错误的车牌,不处理 + * + * @param carLicense 完整的车牌号 + * @return 脱敏后的车牌 + */ + public static String carLicense(String carLicense) + { + if (StringUtils.isBlank(carLicense)) + { + return StringUtils.EMPTY; + } + // 普通车牌 + if (carLicense.length() == 7) + { + carLicense = StringUtils.hide(carLicense, 3, 6); + } + else if (carLicense.length() == 8) + { + // 新能源车牌 + carLicense = StringUtils.hide(carLicense, 3, 7); + } + return carLicense; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java new file mode 100644 index 0000000..f198462 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/DictUtils.java @@ -0,0 +1,239 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import com.alibaba.fastjson2.JSONArray; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 字典工具类 + * + * @author ruoyi + */ +public class DictUtils +{ + /** + * 分隔符 + */ + public static final String SEPARATOR = ","; + + /** + * 设置字典缓存 + * + * @param key 参数键 + * @param dictDatas 字典数据列表 + */ + public static void setDictCache(String key, List dictDatas) + { + SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(key), dictDatas); + } + + /** + * 获取字典缓存 + * + * @param key 参数键 + * @return dictDatas 字典数据列表 + */ + public static List getDictCache(String key) + { + JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key)); + if (StringUtils.isNotNull(arrayCache)) + { + return arrayCache.toList(SysDictData.class); + } + return null; + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue) + { + if (StringUtils.isEmpty(dictValue)) + { + return StringUtils.EMPTY; + } + return getDictLabel(dictType, dictValue, SEPARATOR); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel) + { + if (StringUtils.isEmpty(dictLabel)) + { + return StringUtils.EMPTY; + } + return getDictValue(dictType, dictLabel, SEPARATOR); + } + + /** + * 根据字典类型和字典值获取字典标签 + * + * @param dictType 字典类型 + * @param dictValue 字典值 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String getDictLabel(String dictType, String dictValue, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictValue)) + { + for (SysDictData dict : datas) + { + for (String value : dictValue.split(separator)) + { + if (value.equals(dict.getDictValue())) + { + propertyString.append(dict.getDictLabel()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictValue.equals(dict.getDictValue())) + { + return dict.getDictLabel(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型和字典标签获取字典值 + * + * @param dictType 字典类型 + * @param dictLabel 字典标签 + * @param separator 分隔符 + * @return 字典值 + */ + public static String getDictValue(String dictType, String dictLabel, String separator) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + if (StringUtils.containsAny(separator, dictLabel)) + { + for (SysDictData dict : datas) + { + for (String label : dictLabel.split(separator)) + { + if (label.equals(dict.getDictLabel())) + { + propertyString.append(dict.getDictValue()).append(separator); + break; + } + } + } + } + else + { + for (SysDictData dict : datas) + { + if (dictLabel.equals(dict.getDictLabel())) + { + return dict.getDictValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 根据字典类型获取字典所有值 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictValues(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictValue()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 根据字典类型获取字典所有标签 + * + * @param dictType 字典类型 + * @return 字典值 + */ + public static String getDictLabels(String dictType) + { + StringBuilder propertyString = new StringBuilder(); + List datas = getDictCache(dictType); + if (StringUtils.isNull(datas)) + { + return StringUtils.EMPTY; + } + for (SysDictData dict : datas) + { + propertyString.append(dict.getDictLabel()).append(SEPARATOR); + } + return StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + + /** + * 删除指定字典缓存 + * + * @param key 字典键 + */ + public static void removeDictCache(String key) + { + SpringUtils.getBean(RedisCache.class).deleteObject(getCacheKey(key)); + } + + /** + * 清空字典缓存 + */ + public static void clearDictCache() + { + Collection keys = SpringUtils.getBean(RedisCache.class).keys(CacheConstants.SYS_DICT_KEY + "*"); + SpringUtils.getBean(RedisCache.class).deleteObject(keys); + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + public static String getCacheKey(String configKey) + { + return CacheConstants.SYS_DICT_KEY + configKey; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java new file mode 100644 index 0000000..214e4a0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ExceptionUtil.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.utils; + +import java.io.PrintWriter; +import java.io.StringWriter; +import org.apache.commons.lang3.exception.ExceptionUtils; + +/** + * 错误信息处理类。 + * + * @author ruoyi + */ +public class ExceptionUtil +{ + /** + * 获取exception的详细错误信息。 + */ + public static String getExceptionMessage(Throwable e) + { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw, true)); + return sw.toString(); + } + + public static String getRootErrorMessage(Exception e) + { + Throwable root = ExceptionUtils.getRootCause(e); + root = (root == null ? e : root); + if (root == null) + { + return ""; + } + String msg = root.getMessage(); + if (msg == null) + { + return "null"; + } + return StringUtils.defaultString(msg); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java new file mode 100644 index 0000000..0de30c6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/LogUtils.java @@ -0,0 +1,18 @@ +package com.ruoyi.common.utils; + +/** + * 处理并记录日志文件 + * + * @author ruoyi + */ +public class LogUtils +{ + public static String getBlock(Object msg) + { + if (msg == null) + { + msg = ""; + } + return "[" + msg.toString() + "]"; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java new file mode 100644 index 0000000..da24f0b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/MessageUtils.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; +import com.ruoyi.common.utils.spring.SpringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 获取i18n资源文件 + * + * @author ruoyi + */ +public class MessageUtils +{ + /** + * 根据消息键和参数 获取消息 委托给spring messageSource + * + * @param code 消息键 + * @param args 参数 + * @return 获取国际化翻译值 + */ + public static String message(String code, Object... args) + { + MessageSource messageSource = SpringUtils.getBean(MessageSource.class); + return messageSource.getMessage(code, args, LocaleContextHolder.getLocale()); + } + + /** + * 解析 ${}中括号中的值 + * + * @param input 包含 ${} 占位符的字符串 + * @return 解析后的值(如果国际化文件中不存在对应的value,则会返回Key) + */ + public static String parseMessage(String input) + { + // 匹配 ${} 占位符的正则表达式 + Pattern pattern = Pattern.compile("\\$\\{([^\\}]+)\\}"); + Matcher matcher = pattern.matcher(input); + StringBuffer result = new StringBuffer(); + // 逐个匹配并替换 ${} 中的值 + while (matcher.find()) + { + String key = matcher.group(1); // 匹配到的括号中的值 + // tag 国际化 + String replacement = MessageUtils.message(key); + // 替换占位符 + matcher.appendReplacement(result, replacement); + } + // 处理剩余部分 + matcher.appendTail(result); + return result.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java new file mode 100644 index 0000000..70e9b08 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/PageUtils.java @@ -0,0 +1,35 @@ +package com.ruoyi.common.utils; + +import com.github.pagehelper.PageHelper; +import com.ruoyi.common.core.page.PageDomain; +import com.ruoyi.common.core.page.TableSupport; +import com.ruoyi.common.utils.sql.SqlUtil; + +/** + * 分页工具类 + * + * @author ruoyi + */ +public class PageUtils extends PageHelper +{ + /** + * 设置请求分页数据 + */ + public static void startPage() + { + PageDomain pageDomain = TableSupport.buildPageRequest(); + Integer pageNum = pageDomain.getPageNum(); + Integer pageSize = pageDomain.getPageSize(); + String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy()); + Boolean reasonable = pageDomain.getReasonable(); + PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable); + } + + /** + * 清理分页的线程变量 + */ + public static void clearPage() + { + PageHelper.clearPage(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java new file mode 100644 index 0000000..0d3ac5f --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/SecurityUtils.java @@ -0,0 +1,178 @@ +package com.ruoyi.common.utils; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.util.PatternMatchUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.exception.ServiceException; + +/** + * 安全服务工具类 + * + * @author ruoyi + */ +public class SecurityUtils +{ + + /** + * 用户ID + **/ + public static Long getUserId() + { + try + { + return getLoginUser().getUserId(); + } + catch (Exception e) + { + throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取部门ID + **/ + public static Long getDeptId() + { + try + { + return getLoginUser().getDeptId(); + } + catch (Exception e) + { + throw new ServiceException("获取部门ID异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户账户 + **/ + public static String getUsername() + { + try + { + return getLoginUser().getUsername(); + } + catch (Exception e) + { + throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取用户 + **/ + public static LoginUser getLoginUser() + { + try + { + return (LoginUser) getAuthentication().getPrincipal(); + } + catch (Exception e) + { + throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED); + } + } + + /** + * 获取Authentication + */ + public static Authentication getAuthentication() + { + return SecurityContextHolder.getContext().getAuthentication(); + } + + /** + * 生成BCryptPasswordEncoder密码 + * + * @param password 密码 + * @return 加密字符串 + */ + public static String encryptPassword(String password) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.encode(password); + } + + /** + * 判断密码是否相同 + * + * @param rawPassword 真实密码 + * @param encodedPassword 加密后字符 + * @return 结果 + */ + public static boolean matchesPassword(String rawPassword, String encodedPassword) + { + BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + return passwordEncoder.matches(rawPassword, encodedPassword); + } + + /** + * 是否为管理员 + * + * @param userId 用户ID + * @return 结果 + */ + public static boolean isAdmin(Long userId) + { + return userId != null && 1L == userId; + } + + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(String permission) + { + return hasPermi(getLoginUser().getPermissions(), permission); + } + + /** + * 判断是否包含权限 + * + * @param authorities 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public static boolean hasPermi(Collection authorities, String permission) + { + return authorities.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.ALL_PERMISSION.equals(x) || PatternMatchUtils.simpleMatch(x, permission)); + } + + /** + * 验证用户是否拥有某个角色 + * + * @param role 角色标识 + * @return 用户是否具备某角色 + */ + public static boolean hasRole(String role) + { + List roleList = getLoginUser().getUser().getRoles(); + Collection roles = roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toSet()); + return hasRole(roles, role); + } + + /** + * 判断是否包含角色 + * + * @param roles 角色列表 + * @param role 角色 + * @return 用户是否具备某角色权限 + */ + public static boolean hasRole(Collection roles, String role) + { + return roles.stream().filter(StringUtils::hasText) + .anyMatch(x -> Constants.SUPER_ADMIN.equals(x) || PatternMatchUtils.simpleMatch(x, role)); + } + +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java new file mode 100644 index 0000000..febb603 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ServletUtils.java @@ -0,0 +1,218 @@ +package com.ruoyi.common.utils; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.Convert; + +/** + * 客户端工具类 + * + * @author ruoyi + */ +public class ServletUtils +{ + /** + * 获取String参数 + */ + public static String getParameter(String name) + { + return getRequest().getParameter(name); + } + + /** + * 获取String参数 + */ + public static String getParameter(String name, String defaultValue) + { + return Convert.toStr(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name) + { + return Convert.toInt(getRequest().getParameter(name)); + } + + /** + * 获取Integer参数 + */ + public static Integer getParameterToInt(String name, Integer defaultValue) + { + return Convert.toInt(getRequest().getParameter(name), defaultValue); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name) + { + return Convert.toBool(getRequest().getParameter(name)); + } + + /** + * 获取Boolean参数 + */ + public static Boolean getParameterToBool(String name, Boolean defaultValue) + { + return Convert.toBool(getRequest().getParameter(name), defaultValue); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParams(ServletRequest request) + { + final Map map = request.getParameterMap(); + return Collections.unmodifiableMap(map); + } + + /** + * 获得所有请求参数 + * + * @param request 请求对象{@link ServletRequest} + * @return Map + */ + public static Map getParamMap(ServletRequest request) + { + Map params = new HashMap<>(); + for (Map.Entry entry : getParams(request).entrySet()) + { + params.put(entry.getKey(), StringUtils.join(entry.getValue(), ",")); + } + return params; + } + + /** + * 获取request + */ + public static HttpServletRequest getRequest() + { + return getRequestAttributes().getRequest(); + } + + /** + * 获取response + */ + public static HttpServletResponse getResponse() + { + return getRequestAttributes().getResponse(); + } + + /** + * 获取session + */ + public static HttpSession getSession() + { + return getRequest().getSession(); + } + + public static ServletRequestAttributes getRequestAttributes() + { + RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); + return (ServletRequestAttributes) attributes; + } + + /** + * 将字符串渲染到客户端 + * + * @param response 渲染对象 + * @param string 待渲染的字符串 + */ + public static void renderString(HttpServletResponse response, String string) + { + try + { + response.setStatus(200); + response.setContentType("application/json"); + response.setCharacterEncoding("utf-8"); + response.getWriter().print(string); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + /** + * 是否是Ajax异步请求 + * + * @param request + */ + public static boolean isAjaxRequest(HttpServletRequest request) + { + String accept = request.getHeader("accept"); + if (accept != null && accept.contains("application/json")) + { + return true; + } + + String xRequestedWith = request.getHeader("X-Requested-With"); + if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) + { + return true; + } + + String uri = request.getRequestURI(); + if (StringUtils.inStringIgnoreCase(uri, ".json", ".xml")) + { + return true; + } + + String ajax = request.getParameter("__ajax"); + return StringUtils.inStringIgnoreCase(ajax, "json", "xml"); + } + + /** + * 内容编码 + * + * @param str 内容 + * @return 编码后的内容 + */ + public static String urlEncode(String str) + { + try + { + return URLEncoder.encode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } + + /** + * 内容解码 + * + * @param str 内容 + * @return 解码后的内容 + */ + public static String urlDecode(String str) + { + try + { + return URLDecoder.decode(str, Constants.UTF8); + } + catch (UnsupportedEncodingException e) + { + return StringUtils.EMPTY; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java new file mode 100644 index 0000000..c92759b --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/StringUtils.java @@ -0,0 +1,722 @@ +package com.ruoyi.common.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.springframework.util.AntPathMatcher; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.text.StrFormatter; + +/** + * 字符串工具类 + * + * @author ruoyi + */ +public class StringUtils extends org.apache.commons.lang3.StringUtils +{ + /** 空字符串 */ + private static final String NULLSTR = ""; + + /** 下划线 */ + private static final char SEPARATOR = '_'; + + /** 星号 */ + private static final char ASTERISK = '*'; + + /** + * 获取参数不为空值 + * + * @param value defaultValue 要判断的value + * @return value 返回值 + */ + public static T nvl(T value, T defaultValue) + { + return value != null ? value : defaultValue; + } + + /** + * * 判断一个Collection是否为空, 包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Collection coll) + { + return isNull(coll) || coll.isEmpty(); + } + + /** + * * 判断一个Collection是否非空,包含List,Set,Queue + * + * @param coll 要判断的Collection + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Collection coll) + { + return !isEmpty(coll); + } + + /** + * * 判断一个对象数组是否为空 + * + * @param objects 要判断的对象数组 + ** @return true:为空 false:非空 + */ + public static boolean isEmpty(Object[] objects) + { + return isNull(objects) || (objects.length == 0); + } + + /** + * * 判断一个对象数组是否非空 + * + * @param objects 要判断的对象数组 + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Object[] objects) + { + return !isEmpty(objects); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:为空 false:非空 + */ + public static boolean isEmpty(Map map) + { + return isNull(map) || map.isEmpty(); + } + + /** + * * 判断一个Map是否为空 + * + * @param map 要判断的Map + * @return true:非空 false:空 + */ + public static boolean isNotEmpty(Map map) + { + return !isEmpty(map); + } + + /** + * * 判断一个字符串是否为空串 + * + * @param str String + * @return true:为空 false:非空 + */ + public static boolean isEmpty(String str) + { + return isNull(str) || NULLSTR.equals(str.trim()); + } + + /** + * * 判断一个字符串是否为非空串 + * + * @param str String + * @return true:非空串 false:空串 + */ + public static boolean isNotEmpty(String str) + { + return !isEmpty(str); + } + + /** + * * 判断一个对象是否为空 + * + * @param object Object + * @return true:为空 false:非空 + */ + public static boolean isNull(Object object) + { + return object == null; + } + + /** + * * 判断一个对象是否非空 + * + * @param object Object + * @return true:非空 false:空 + */ + public static boolean isNotNull(Object object) + { + return !isNull(object); + } + + /** + * * 判断一个对象是否是数组类型(Java基本型别的数组) + * + * @param object 对象 + * @return true:是数组 false:不是数组 + */ + public static boolean isArray(Object object) + { + return isNotNull(object) && object.getClass().isArray(); + } + + /** + * 去空格 + */ + public static String trim(String str) + { + return (str == null ? "" : str.trim()); + } + + /** + * 替换指定字符串的指定区间内字符为"*" + * + * @param str 字符串 + * @param startInclude 开始位置(包含) + * @param endExclude 结束位置(不包含) + * @return 替换后的字符串 + */ + public static String hide(CharSequence str, int startInclude, int endExclude) + { + if (isEmpty(str)) + { + return NULLSTR; + } + final int strLength = str.length(); + if (startInclude > strLength) + { + return NULLSTR; + } + if (endExclude > strLength) + { + endExclude = strLength; + } + if (startInclude > endExclude) + { + // 如果起始位置大于结束位置,不替换 + return NULLSTR; + } + final char[] chars = new char[strLength]; + for (int i = 0; i < strLength; i++) + { + if (i >= startInclude && i < endExclude) + { + chars[i] = ASTERISK; + } + else + { + chars[i] = str.charAt(i); + } + } + return new String(chars); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @return 结果 + */ + public static String substring(final String str, int start) + { + if (str == null) + { + return NULLSTR; + } + + if (start < 0) + { + start = str.length() + start; + } + + if (start < 0) + { + start = 0; + } + if (start > str.length()) + { + return NULLSTR; + } + + return str.substring(start); + } + + /** + * 截取字符串 + * + * @param str 字符串 + * @param start 开始 + * @param end 结束 + * @return 结果 + */ + public static String substring(final String str, int start, int end) + { + if (str == null) + { + return NULLSTR; + } + + if (end < 0) + { + end = str.length() + end; + } + if (start < 0) + { + start = str.length() + start; + } + + if (end > str.length()) + { + end = str.length(); + } + + if (start > end) + { + return NULLSTR; + } + + if (start < 0) + { + start = 0; + } + if (end < 0) + { + end = 0; + } + + return str.substring(start, end); + } + + /** + * 在字符串中查找第一个出现的 `open` 和最后一个出现的 `close` 之间的子字符串 + * + * @param str 要截取的字符串 + * @param open 起始字符串 + * @param close 结束字符串 + * @return 截取结果 + */ + public static String substringBetweenLast(final String str, final String open, final String close) + { + if (isEmpty(str) || isEmpty(open) || isEmpty(close)) + { + return NULLSTR; + } + final int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) + { + final int end = str.lastIndexOf(close); + if (end != INDEX_NOT_FOUND) + { + return str.substring(start + open.length(), end); + } + } + return NULLSTR; + } + + /** + * 判断是否为空,并且不是空白字符 + * + * @param str 要判断的value + * @return 结果 + */ + public static boolean hasText(String str) + { + return (str != null && !str.isEmpty() && containsText(str)); + } + + private static boolean containsText(CharSequence str) + { + int strLen = str.length(); + for (int i = 0; i < strLen; i++) + { + if (!Character.isWhitespace(str.charAt(i))) + { + return true; + } + } + return false; + } + + /** + * 格式化文本, {} 表示占位符
+ * 此方法只是简单将占位符 {} 按照顺序替换为参数
+ * 如果想输出 {} 使用 \\转义 { 即可,如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可
+ * 例:
+ * 通常使用:format("this is {} for {}", "a", "b") -> this is a for b
+ * 转义{}: format("this is \\{} for {}", "a", "b") -> this is \{} for a
+ * 转义\: format("this is \\\\{} for {}", "a", "b") -> this is \a for b
+ * + * @param template 文本模板,被替换的部分用 {} 表示 + * @param params 参数值 + * @return 格式化后的文本 + */ + public static String format(String template, Object... params) + { + if (isEmpty(params) || isEmpty(template)) + { + return template; + } + return StrFormatter.format(template, params); + } + + /** + * 是否为http(s)://开头 + * + * @param link 链接 + * @return 结果 + */ + public static boolean ishttp(String link) + { + return StringUtils.startsWithAny(link, Constants.HTTP, Constants.HTTPS); + } + + /** + * 字符串转set + * + * @param str 字符串 + * @param sep 分隔符 + * @return set集合 + */ + public static final Set str2Set(String str, String sep) + { + return new HashSet(str2List(str, sep, true, false)); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @return list集合 + */ + public static final List str2List(String str, String sep) + { + return str2List(str, sep, true, false); + } + + /** + * 字符串转list + * + * @param str 字符串 + * @param sep 分隔符 + * @param filterBlank 过滤纯空白 + * @param trim 去掉首尾空白 + * @return list集合 + */ + public static final List str2List(String str, String sep, boolean filterBlank, boolean trim) + { + List list = new ArrayList(); + if (StringUtils.isEmpty(str)) + { + return list; + } + + // 过滤空白字符串 + if (filterBlank && StringUtils.isBlank(str)) + { + return list; + } + String[] split = str.split(sep); + for (String string : split) + { + if (filterBlank && StringUtils.isBlank(string)) + { + continue; + } + if (trim) + { + string = string.trim(); + } + list.add(string); + } + + return list; + } + + /** + * 判断给定的collection列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value + * + * @param collection 给定的集合 + * @param array 给定的数组 + * @return boolean 结果 + */ + public static boolean containsAny(Collection collection, String... array) + { + if (isEmpty(collection) || isEmpty(array)) + { + return false; + } + else + { + for (String str : array) + { + if (collection.contains(str)) + { + return true; + } + } + return false; + } + } + + /** + * 查找指定字符串是否包含指定字符串列表中的任意一个字符串同时串忽略大小写 + * + * @param cs 指定字符串 + * @param searchCharSequences 需要检查的字符串数组 + * @return 是否包含任意一个字符串 + */ + public static boolean containsAnyIgnoreCase(CharSequence cs, CharSequence... searchCharSequences) + { + if (isEmpty(cs) || isEmpty(searchCharSequences)) + { + return false; + } + for (CharSequence testStr : searchCharSequences) + { + if (containsIgnoreCase(cs, testStr)) + { + return true; + } + } + return false; + } + + /** + * 驼峰转下划线命名 + */ + public static String toUnderScoreCase(String str) + { + if (str == null) + { + return null; + } + StringBuilder sb = new StringBuilder(); + // 前置字符是否大写 + boolean preCharIsUpperCase = true; + // 当前字符是否大写 + boolean curreCharIsUpperCase = true; + // 下一字符是否大写 + boolean nexteCharIsUpperCase = true; + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + if (i > 0) + { + preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1)); + } + else + { + preCharIsUpperCase = false; + } + + curreCharIsUpperCase = Character.isUpperCase(c); + + if (i < (str.length() - 1)) + { + nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1)); + } + + if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) + { + sb.append(SEPARATOR); + } + else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) + { + sb.append(SEPARATOR); + } + sb.append(Character.toLowerCase(c)); + } + + return sb.toString(); + } + + /** + * 是否包含字符串 + * + * @param str 验证字符串 + * @param strs 字符串组 + * @return 包含返回true + */ + public static boolean inStringIgnoreCase(String str, String... strs) + { + if (str != null && strs != null) + { + for (String s : strs) + { + if (str.equalsIgnoreCase(trim(s))) + { + return true; + } + } + } + return false; + } + + /** + * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。 例如:HELLO_WORLD->HelloWorld + * + * @param name 转换前的下划线大写方式命名的字符串 + * @return 转换后的驼峰式命名的字符串 + */ + public static String convertToCamelCase(String name) + { + StringBuilder result = new StringBuilder(); + // 快速检查 + if (name == null || name.isEmpty()) + { + // 没必要转换 + return ""; + } + else if (!name.contains("_")) + { + // 不含下划线,仅将首字母大写 + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + // 用下划线将原始字符串分割 + String[] camels = name.split("_"); + for (String camel : camels) + { + // 跳过原始字符串中开头、结尾的下换线或双重下划线 + if (camel.isEmpty()) + { + continue; + } + // 首字母大写 + result.append(camel.substring(0, 1).toUpperCase()); + result.append(camel.substring(1).toLowerCase()); + } + return result.toString(); + } + + /** + * 驼峰式命名法 + * 例如:user_name->userName + */ + public static String toCamelCase(String s) + { + if (s == null) + { + return null; + } + if (s.indexOf(SEPARATOR) == -1) + { + return s; + } + s = s.toLowerCase(); + StringBuilder sb = new StringBuilder(s.length()); + boolean upperCase = false; + for (int i = 0; i < s.length(); i++) + { + char c = s.charAt(i); + + if (c == SEPARATOR) + { + upperCase = true; + } + else if (upperCase) + { + sb.append(Character.toUpperCase(c)); + upperCase = false; + } + else + { + sb.append(c); + } + } + return sb.toString(); + } + + /** + * 查找指定字符串是否匹配指定字符串列表中的任意一个字符串 + * + * @param str 指定字符串 + * @param strs 需要检查的字符串数组 + * @return 是否匹配 + */ + public static boolean matches(String str, List strs) + { + if (isEmpty(str) || isEmpty(strs)) + { + return false; + } + for (String pattern : strs) + { + if (isMatch(pattern, str)) + { + return true; + } + } + return false; + } + + /** + * 判断url是否与规则配置: + * ? 表示单个字符; + * * 表示一层路径内的任意字符串,不可跨层级; + * ** 表示任意层路径; + * + * @param pattern 匹配规则 + * @param url 需要匹配的url + * @return + */ + public static boolean isMatch(String pattern, String url) + { + AntPathMatcher matcher = new AntPathMatcher(); + return matcher.match(pattern, url); + } + + @SuppressWarnings("unchecked") + public static T cast(Object obj) + { + return (T) obj; + } + + /** + * 数字左边补齐0,使之达到指定长度。注意,如果数字转换为字符串后,长度大于size,则只保留 最后size个字符。 + * + * @param num 数字对象 + * @param size 字符串指定长度 + * @return 返回数字的字符串格式,该字符串为指定长度。 + */ + public static final String padl(final Number num, final int size) + { + return padl(num.toString(), size, '0'); + } + + /** + * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 + * + * @param s 原始字符串 + * @param size 字符串指定长度 + * @param c 用于补齐的字符 + * @return 返回指定长度的字符串,由原字符串左补齐或截取得到。 + */ + public static final String padl(final String s, final int size, final char c) + { + final StringBuilder sb = new StringBuilder(size); + if (s != null) + { + final int len = s.length(); + if (s.length() <= size) + { + for (int i = size - len; i > 0; i--) + { + sb.append(c); + } + sb.append(s); + } + else + { + return s.substring(len - size, len); + } + } + else + { + for (int i = size; i > 0; i--) + { + sb.append(c); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java new file mode 100644 index 0000000..71fe6d5 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/Threads.java @@ -0,0 +1,99 @@ +package com.ruoyi.common.utils; + +import java.util.concurrent.CancellationException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 线程相关工具类. + * + * @author ruoyi + */ +public class Threads +{ + private static final Logger logger = LoggerFactory.getLogger(Threads.class); + + /** + * sleep等待,单位为毫秒 + */ + public static void sleep(long milliseconds) + { + try + { + Thread.sleep(milliseconds); + } + catch (InterruptedException e) + { + return; + } + } + + /** + * 停止线程池 + * 先使用shutdown, 停止接收新任务并尝试完成所有已存在任务. + * 如果超时, 则调用shutdownNow, 取消在workQueue中Pending的任务,并中断所有阻塞函数. + * 如果仍然超時,則強制退出. + * 另对在shutdown时线程本身被调用中断做了处理. + */ + public static void shutdownAndAwaitTermination(ExecutorService pool) + { + if (pool != null && !pool.isShutdown()) + { + pool.shutdown(); + try + { + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + pool.shutdownNow(); + if (!pool.awaitTermination(120, TimeUnit.SECONDS)) + { + logger.info("Pool did not terminate"); + } + } + } + catch (InterruptedException ie) + { + pool.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + } + + /** + * 打印线程异常信息 + */ + public static void printException(Runnable r, Throwable t) + { + if (t == null && r instanceof Future) + { + try + { + Future future = (Future) r; + if (future.isDone()) + { + future.get(); + } + } + catch (CancellationException ce) + { + t = ce; + } + catch (ExecutionException ee) + { + t = ee.getCause(); + } + catch (InterruptedException ie) + { + Thread.currentThread().interrupt(); + } + } + if (t != null) + { + logger.error(t.getMessage(), t); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java new file mode 100644 index 0000000..4463662 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanUtils.java @@ -0,0 +1,110 @@ +package com.ruoyi.common.utils.bean; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Bean 工具类 + * + * @author ruoyi + */ +public class BeanUtils extends org.springframework.beans.BeanUtils +{ + /** Bean方法名中属性名开始的下标 */ + private static final int BEAN_METHOD_PROP_INDEX = 3; + + /** * 匹配getter方法的正则表达式 */ + private static final Pattern GET_PATTERN = Pattern.compile("get(\\p{javaUpperCase}\\w*)"); + + /** * 匹配setter方法的正则表达式 */ + private static final Pattern SET_PATTERN = Pattern.compile("set(\\p{javaUpperCase}\\w*)"); + + /** + * Bean属性复制工具方法。 + * + * @param dest 目标对象 + * @param src 源对象 + */ + public static void copyBeanProp(Object dest, Object src) + { + try + { + copyProperties(src, dest); + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + /** + * 获取对象的setter方法。 + * + * @param obj 对象 + * @return 对象的setter方法列表 + */ + public static List getSetterMethods(Object obj) + { + // setter方法列表 + List setterMethods = new ArrayList(); + + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + + // 查找setter方法 + + for (Method method : methods) + { + Matcher m = SET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 1)) + { + setterMethods.add(method); + } + } + // 返回setter方法列表 + return setterMethods; + } + + /** + * 获取对象的getter方法。 + * + * @param obj 对象 + * @return 对象的getter方法列表 + */ + + public static List getGetterMethods(Object obj) + { + // getter方法列表 + List getterMethods = new ArrayList(); + // 获取所有方法 + Method[] methods = obj.getClass().getMethods(); + // 查找getter方法 + for (Method method : methods) + { + Matcher m = GET_PATTERN.matcher(method.getName()); + if (m.matches() && (method.getParameterTypes().length == 0)) + { + getterMethods.add(method); + } + } + // 返回getter方法列表 + return getterMethods; + } + + /** + * 检查Bean方法名中的属性名是否相等。
+ * 如getName()和setName()属性名一样,getName()和setAge()属性名不一样。 + * + * @param m1 方法名1 + * @param m2 方法名2 + * @return 属性名一样返回true,否则返回false + */ + + public static boolean isMethodPropEquals(String m1, String m2) + { + return m1.substring(BEAN_METHOD_PROP_INDEX).equals(m2.substring(BEAN_METHOD_PROP_INDEX)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java new file mode 100644 index 0000000..80bfed7 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/bean/BeanValidators.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.bean; + +import java.util.Set; +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; + +/** + * bean对象属性验证 + * + * @author ruoyi + */ +public class BeanValidators +{ + public static void validateWithException(Validator validator, Object object, Class... groups) + throws ConstraintViolationException + { + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) + { + throw new ConstraintViolationException(constraintViolations); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java new file mode 100644 index 0000000..68130b9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileTypeUtils.java @@ -0,0 +1,76 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import org.apache.commons.lang3.StringUtils; + +/** + * 文件类型工具类 + * + * @author ruoyi + */ +public class FileTypeUtils +{ + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param file 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(File file) + { + if (null == file) + { + return StringUtils.EMPTY; + } + return getFileType(file.getName()); + } + + /** + * 获取文件类型 + *

+ * 例如: ruoyi.txt, 返回: txt + * + * @param fileName 文件名 + * @return 后缀(不含".") + */ + public static String getFileType(String fileName) + { + int separatorIndex = fileName.lastIndexOf("."); + if (separatorIndex < 0) + { + return ""; + } + return fileName.substring(separatorIndex + 1).toLowerCase(); + } + + /** + * 获取文件类型 + * + * @param photoByte 文件字节码 + * @return 后缀(不含".") + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "JPG"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "GIF"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "JPG"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "BMP"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "PNG"; + } + return strFileExtendName; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java new file mode 100644 index 0000000..d5455c4 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUploadUtils.java @@ -0,0 +1,232 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Objects; +import org.apache.commons.io.FilenameUtils; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.exception.file.FileNameLengthLimitExceededException; +import com.ruoyi.common.exception.file.FileSizeLimitExceededException; +import com.ruoyi.common.exception.file.InvalidExtensionException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.Seq; + +/** + * 文件上传工具类 + * + * @author ruoyi + */ +public class FileUploadUtils +{ + /** + * 默认大小 50M + */ + public static final long DEFAULT_MAX_SIZE = 50 * 1024 * 1024L; + + /** + * 默认的文件名最大长度 100 + */ + public static final int DEFAULT_FILE_NAME_LENGTH = 100; + + /** + * 默认上传的地址 + */ + private static String defaultBaseDir = RuoYiConfig.getProfile(); + + public static void setDefaultBaseDir(String defaultBaseDir) + { + FileUploadUtils.defaultBaseDir = defaultBaseDir; + } + + public static String getDefaultBaseDir() + { + return defaultBaseDir; + } + + /** + * 以默认配置进行文件上传 + * + * @param file 上传的文件 + * @return 文件名称 + * @throws Exception + */ + public static final String upload(MultipartFile file) throws IOException + { + try + { + return upload(getDefaultBaseDir(), file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 根据文件路径上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @return 文件名称 + * @throws IOException + */ + public static final String upload(String baseDir, MultipartFile file) throws IOException + { + try + { + return upload(baseDir, file, MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION); + } + catch (Exception e) + { + throw new IOException(e.getMessage(), e); + } + } + + /** + * 文件上传 + * + * @param baseDir 相对应用的基目录 + * @param file 上传的文件 + * @param allowedExtension 上传文件类型 + * @return 返回上传成功的文件名 + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws FileNameLengthLimitExceededException 文件名太长 + * @throws IOException 比如读写文件出错时 + * @throws InvalidExtensionException 文件校验异常 + */ + public static final String upload(String baseDir, MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, IOException, FileNameLengthLimitExceededException, + InvalidExtensionException + { + int fileNamelength = Objects.requireNonNull(file.getOriginalFilename()).length(); + if (fileNamelength > FileUploadUtils.DEFAULT_FILE_NAME_LENGTH) + { + throw new FileNameLengthLimitExceededException(FileUploadUtils.DEFAULT_FILE_NAME_LENGTH); + } + + assertAllowed(file, allowedExtension); + + String fileName = extractFilename(file); + + String absPath = getAbsoluteFile(baseDir, fileName).getAbsolutePath(); + file.transferTo(Paths.get(absPath)); + return getPathFileName(baseDir, fileName); + } + + /** + * 编码文件名 + */ + public static final String extractFilename(MultipartFile file) + { + return StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), + FilenameUtils.getBaseName(file.getOriginalFilename()), Seq.getId(Seq.uploadSeqType), getExtension(file)); + } + + public static final File getAbsoluteFile(String uploadDir, String fileName) throws IOException + { + File desc = new File(uploadDir + File.separator + fileName); + + if (!desc.exists()) + { + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + } + return desc; + } + + public static final String getPathFileName(String uploadDir, String fileName) throws IOException + { + int dirLastIndex = RuoYiConfig.getProfile().length() + 1; + String currentDir = StringUtils.substring(uploadDir, dirLastIndex); + return Constants.RESOURCE_PREFIX + "/" + currentDir + "/" + fileName; + } + + /** + * 文件大小校验 + * + * @param file 上传的文件 + * @return + * @throws FileSizeLimitExceededException 如果超出最大大小 + * @throws InvalidExtensionException + */ + public static final void assertAllowed(MultipartFile file, String[] allowedExtension) + throws FileSizeLimitExceededException, InvalidExtensionException + { + long size = file.getSize(); + if (size > DEFAULT_MAX_SIZE) + { + throw new FileSizeLimitExceededException(DEFAULT_MAX_SIZE / 1024 / 1024); + } + + String fileName = file.getOriginalFilename(); + String extension = getExtension(file); + if (allowedExtension != null && !isAllowedExtension(extension, allowedExtension)) + { + if (allowedExtension == MimeTypeUtils.IMAGE_EXTENSION) + { + throw new InvalidExtensionException.InvalidImageExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.FLASH_EXTENSION) + { + throw new InvalidExtensionException.InvalidFlashExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.MEDIA_EXTENSION) + { + throw new InvalidExtensionException.InvalidMediaExtensionException(allowedExtension, extension, + fileName); + } + else if (allowedExtension == MimeTypeUtils.VIDEO_EXTENSION) + { + throw new InvalidExtensionException.InvalidVideoExtensionException(allowedExtension, extension, + fileName); + } + else + { + throw new InvalidExtensionException(allowedExtension, extension, fileName); + } + } + } + + /** + * 判断MIME类型是否是允许的MIME类型 + * + * @param extension + * @param allowedExtension + * @return + */ + public static final boolean isAllowedExtension(String extension, String[] allowedExtension) + { + for (String str : allowedExtension) + { + if (str.equalsIgnoreCase(extension)) + { + return true; + } + } + return false; + } + + /** + * 获取文件名的后缀 + * + * @param file 表单文件 + * @return 后缀名 + */ + public static final String getExtension(MultipartFile file) + { + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + if (StringUtils.isEmpty(extension)) + { + extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType())); + } + return extension; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java new file mode 100644 index 0000000..ed4cbc9 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/FileUtils.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.file; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.apache.commons.io.FilenameUtils; + +/** + * 文件处理工具类 + * + * @author ruoyi + */ +public class FileUtils +{ + public static String FILENAME_PATTERN = "[a-zA-Z0-9_\\-\\|\\.\\u4e00-\\u9fa5]+"; + + /** + * 输出指定文件的byte数组 + * + * @param filePath 文件路径 + * @param os 输出流 + * @return + */ + public static void writeBytes(String filePath, OutputStream os) throws IOException + { + FileInputStream fis = null; + try + { + File file = new File(filePath); + if (!file.exists()) + { + throw new FileNotFoundException(filePath); + } + fis = new FileInputStream(file); + byte[] b = new byte[1024]; + int length; + while ((length = fis.read(b)) > 0) + { + os.write(b, 0, length); + } + } + catch (IOException e) + { + throw e; + } + finally + { + IOUtils.close(os); + IOUtils.close(fis); + } + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeImportBytes(byte[] data) throws IOException + { + return writeBytes(data, RuoYiConfig.getImportPath()); + } + + /** + * 写数据到文件中 + * + * @param data 数据 + * @param uploadDir 目标文件 + * @return 目标文件 + * @throws IOException IO异常 + */ + public static String writeBytes(byte[] data, String uploadDir) throws IOException + { + FileOutputStream fos = null; + String pathName = ""; + try + { + String extension = getFileExtendName(data); + pathName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension; + File file = FileUploadUtils.getAbsoluteFile(uploadDir, pathName); + fos = new FileOutputStream(file); + fos.write(data); + } + finally + { + IOUtils.close(fos); + } + return FileUploadUtils.getPathFileName(uploadDir, pathName); + } + + /** + * 删除文件 + * + * @param filePath 文件 + * @return + */ + public static boolean deleteFile(String filePath) + { + boolean flag = false; + File file = new File(filePath); + // 路径为文件且不为空则进行删除 + if (file.isFile() && file.exists()) + { + flag = file.delete(); + } + return flag; + } + + /** + * 文件名称验证 + * + * @param filename 文件名称 + * @return true 正常 false 非法 + */ + public static boolean isValidFilename(String filename) + { + return filename.matches(FILENAME_PATTERN); + } + + /** + * 检查文件是否可下载 + * + * @param resource 需要下载的文件 + * @return true 正常 false 非法 + */ + public static boolean checkAllowDownload(String resource) + { + // 禁止目录上跳级别 + if (StringUtils.contains(resource, "..")) + { + return false; + } + + // 检查允许下载的文件规则 + if (ArrayUtils.contains(MimeTypeUtils.DEFAULT_ALLOWED_EXTENSION, FileTypeUtils.getFileType(resource))) + { + return true; + } + + // 不在允许下载的文件规则 + return false; + } + + /** + * 下载文件名重新编码 + * + * @param request 请求对象 + * @param fileName 文件名 + * @return 编码后的文件名 + */ + public static String setFileDownloadHeader(HttpServletRequest request, String fileName) throws UnsupportedEncodingException + { + final String agent = request.getHeader("USER-AGENT"); + String filename = fileName; + if (agent.contains("MSIE")) + { + // IE浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + filename = filename.replace("+", " "); + } + else if (agent.contains("Firefox")) + { + // 火狐浏览器 + filename = new String(fileName.getBytes(), "ISO8859-1"); + } + else if (agent.contains("Chrome")) + { + // google浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + else + { + // 其它浏览器 + filename = URLEncoder.encode(filename, "utf-8"); + } + return filename; + } + + /** + * 下载文件名重新编码 + * + * @param response 响应对象 + * @param realFileName 真实文件名 + */ + public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) throws UnsupportedEncodingException + { + String percentEncodedFileName = percentEncode(realFileName); + + StringBuilder contentDispositionValue = new StringBuilder(); + contentDispositionValue.append("attachment; filename=") + .append(percentEncodedFileName) + .append(";") + .append("filename*=") + .append("utf-8''") + .append(percentEncodedFileName); + + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); + response.setHeader("Content-disposition", contentDispositionValue.toString()); + response.setHeader("download-filename", percentEncodedFileName); + } + + /** + * 百分号编码工具方法 + * + * @param s 需要百分号编码的字符串 + * @return 百分号编码后的字符串 + */ + public static String percentEncode(String s) throws UnsupportedEncodingException + { + String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); + return encode.replaceAll("\\+", "%20"); + } + + /** + * 获取图像后缀 + * + * @param photoByte 图像数据 + * @return 后缀名 + */ + public static String getFileExtendName(byte[] photoByte) + { + String strFileExtendName = "jpg"; + if ((photoByte[0] == 71) && (photoByte[1] == 73) && (photoByte[2] == 70) && (photoByte[3] == 56) + && ((photoByte[4] == 55) || (photoByte[4] == 57)) && (photoByte[5] == 97)) + { + strFileExtendName = "gif"; + } + else if ((photoByte[6] == 74) && (photoByte[7] == 70) && (photoByte[8] == 73) && (photoByte[9] == 70)) + { + strFileExtendName = "jpg"; + } + else if ((photoByte[0] == 66) && (photoByte[1] == 77)) + { + strFileExtendName = "bmp"; + } + else if ((photoByte[1] == 80) && (photoByte[2] == 78) && (photoByte[3] == 71)) + { + strFileExtendName = "png"; + } + return strFileExtendName; + } + + /** + * 获取文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi.png + * + * @param fileName 路径名称 + * @return 没有文件路径的名称 + */ + public static String getName(String fileName) + { + if (fileName == null) + { + return null; + } + int lastUnixPos = fileName.lastIndexOf('/'); + int lastWindowsPos = fileName.lastIndexOf('\\'); + int index = Math.max(lastUnixPos, lastWindowsPos); + return fileName.substring(index + 1); + } + + /** + * 获取不带后缀文件名称 /profile/upload/2022/04/16/ruoyi.png -- ruoyi + * + * @param fileName 路径名称 + * @return 没有文件路径和后缀的名称 + */ + public static String getNameNotSuffix(String fileName) + { + if (fileName == null) + { + return null; + } + String baseName = FilenameUtils.getBaseName(fileName); + return baseName; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java new file mode 100644 index 0000000..432dfda --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/ImageUtils.java @@ -0,0 +1,98 @@ +package com.ruoyi.common.utils.file; + +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.Arrays; +import org.apache.poi.util.IOUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; + +/** + * 图片处理工具类 + * + * @author ruoyi + */ +public class ImageUtils +{ + private static final Logger log = LoggerFactory.getLogger(ImageUtils.class); + + public static byte[] getImage(String imagePath) + { + InputStream is = getFile(imagePath); + try + { + return IOUtils.toByteArray(is); + } + catch (Exception e) + { + log.error("图片加载异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(is); + } + } + + public static InputStream getFile(String imagePath) + { + try + { + byte[] result = readFile(imagePath); + result = Arrays.copyOf(result, result.length); + return new ByteArrayInputStream(result); + } + catch (Exception e) + { + log.error("获取图片异常 {}", e); + } + return null; + } + + /** + * 读取文件为字节数据 + * + * @param url 地址 + * @return 字节数据 + */ + public static byte[] readFile(String url) + { + InputStream in = null; + try + { + if (url.startsWith("http")) + { + // 网络地址 + URL urlObj = new URL(url); + URLConnection urlConnection = urlObj.openConnection(); + urlConnection.setConnectTimeout(30 * 1000); + urlConnection.setReadTimeout(60 * 1000); + urlConnection.setDoInput(true); + in = urlConnection.getInputStream(); + } + else + { + // 本机地址 + String localPath = RuoYiConfig.getProfile(); + String downloadPath = localPath + StringUtils.substringAfter(url, Constants.RESOURCE_PREFIX); + in = new FileInputStream(downloadPath); + } + return IOUtils.toByteArray(in); + } + catch (Exception e) + { + log.error("获取文件路径异常 {}", e); + return null; + } + finally + { + IOUtils.closeQuietly(in); + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java new file mode 100644 index 0000000..f968f1a --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/file/MimeTypeUtils.java @@ -0,0 +1,59 @@ +package com.ruoyi.common.utils.file; + +/** + * 媒体类型工具类 + * + * @author ruoyi + */ +public class MimeTypeUtils +{ + public static final String IMAGE_PNG = "image/png"; + + public static final String IMAGE_JPG = "image/jpg"; + + public static final String IMAGE_JPEG = "image/jpeg"; + + public static final String IMAGE_BMP = "image/bmp"; + + public static final String IMAGE_GIF = "image/gif"; + + public static final String[] IMAGE_EXTENSION = { "bmp", "gif", "jpg", "jpeg", "png" }; + + public static final String[] FLASH_EXTENSION = { "swf", "flv" }; + + public static final String[] MEDIA_EXTENSION = { "swf", "flv", "mp3", "wav", "wma", "wmv", "mid", "avi", "mpg", + "asf", "rm", "rmvb" }; + + public static final String[] VIDEO_EXTENSION = { "mp4", "avi", "rmvb" }; + + public static final String[] DEFAULT_ALLOWED_EXTENSION = { + // 图片 + "bmp", "gif", "jpg", "jpeg", "png", + // word excel powerpoint + "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt", + // 压缩文件 + "rar", "zip", "gz", "bz2", + // 视频格式 + "mp4", "avi", "rmvb", + // pdf + "pdf" }; + + public static String getExtension(String prefix) + { + switch (prefix) + { + case IMAGE_PNG: + return "png"; + case IMAGE_JPG: + return "jpg"; + case IMAGE_JPEG: + return "jpeg"; + case IMAGE_BMP: + return "bmp"; + case IMAGE_GIF: + return "gif"; + default: + return ""; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java new file mode 100644 index 0000000..f52e83e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/EscapeUtil.java @@ -0,0 +1,167 @@ +package com.ruoyi.common.utils.html; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 转义和反转义工具类 + * + * @author ruoyi + */ +public class EscapeUtil +{ + public static final String RE_HTML_MARK = "(<[^<]*?>)|(<[\\s]*?/[^<]*?>)|(<[^<]*?/[\\s]*?>)"; + + private static final char[][] TEXT = new char[64][]; + + static + { + for (int i = 0; i < 64; i++) + { + TEXT[i] = new char[] { (char) i }; + } + + // special HTML characters + TEXT['\''] = "'".toCharArray(); // 单引号 + TEXT['"'] = """.toCharArray(); // 双引号 + TEXT['&'] = "&".toCharArray(); // &符 + TEXT['<'] = "<".toCharArray(); // 小于号 + TEXT['>'] = ">".toCharArray(); // 大于号 + } + + /** + * 转义文本中的HTML字符为安全的字符 + * + * @param text 被转义的文本 + * @return 转义后的文本 + */ + public static String escape(String text) + { + return encode(text); + } + + /** + * 还原被转义的HTML特殊字符 + * + * @param content 包含转义符的HTML内容 + * @return 转换后的字符串 + */ + public static String unescape(String content) + { + return decode(content); + } + + /** + * 清除所有HTML标签,但是不删除标签内的内容 + * + * @param content 文本 + * @return 清除标签后的文本 + */ + public static String clean(String content) + { + return new HTMLFilter().filter(content); + } + + /** + * Escape编码 + * + * @param text 被编码的文本 + * @return 编码后的字符 + */ + private static String encode(String text) + { + if (StringUtils.isEmpty(text)) + { + return StringUtils.EMPTY; + } + + final StringBuilder tmp = new StringBuilder(text.length() * 6); + char c; + for (int i = 0; i < text.length(); i++) + { + c = text.charAt(i); + if (c < 256) + { + tmp.append("%"); + if (c < 16) + { + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + else + { + tmp.append("%u"); + if (c <= 0xfff) + { + // issue#I49JU8@Gitee + tmp.append("0"); + } + tmp.append(Integer.toString(c, 16)); + } + } + return tmp.toString(); + } + + /** + * Escape解码 + * + * @param content 被转义的内容 + * @return 解码后的字符串 + */ + public static String decode(String content) + { + if (StringUtils.isEmpty(content)) + { + return content; + } + + StringBuilder tmp = new StringBuilder(content.length()); + int lastPos = 0, pos = 0; + char ch; + while (lastPos < content.length()) + { + pos = content.indexOf("%", lastPos); + if (pos == lastPos) + { + if (content.charAt(pos + 1) == 'u') + { + ch = (char) Integer.parseInt(content.substring(pos + 2, pos + 6), 16); + tmp.append(ch); + lastPos = pos + 6; + } + else + { + ch = (char) Integer.parseInt(content.substring(pos + 1, pos + 3), 16); + tmp.append(ch); + lastPos = pos + 3; + } + } + else + { + if (pos == -1) + { + tmp.append(content.substring(lastPos)); + lastPos = content.length(); + } + else + { + tmp.append(content.substring(lastPos, pos)); + lastPos = pos; + } + } + } + return tmp.toString(); + } + + public static void main(String[] args) + { + String html = ""; + String escape = EscapeUtil.escape(html); + // String html = "ipt>alert(\"XSS\")ipt>"; + // String html = "<123"; + // String html = "123>"; + System.out.println("clean: " + EscapeUtil.clean(html)); + System.out.println("escape: " + escape); + System.out.println("unescape: " + EscapeUtil.unescape(escape)); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java new file mode 100644 index 0000000..ebff3fd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/html/HTMLFilter.java @@ -0,0 +1,570 @@ +package com.ruoyi.common.utils.html; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * HTML过滤器,用于去除XSS漏洞隐患。 + * + * @author ruoyi + */ +public final class HTMLFilter +{ + /** + * regex flag union representing /si modifiers in php + **/ + private static final int REGEX_FLAGS_SI = Pattern.CASE_INSENSITIVE | Pattern.DOTALL; + private static final Pattern P_COMMENTS = Pattern.compile("", Pattern.DOTALL); + private static final Pattern P_COMMENT = Pattern.compile("^!--(.*)--$", REGEX_FLAGS_SI); + private static final Pattern P_TAGS = Pattern.compile("<(.*?)>", Pattern.DOTALL); + private static final Pattern P_END_TAG = Pattern.compile("^/([a-z0-9]+)", REGEX_FLAGS_SI); + private static final Pattern P_START_TAG = Pattern.compile("^([a-z0-9]+)(.*?)(/?)$", REGEX_FLAGS_SI); + private static final Pattern P_QUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)=([\"'])(.*?)\\2", REGEX_FLAGS_SI); + private static final Pattern P_UNQUOTED_ATTRIBUTES = Pattern.compile("([a-z0-9]+)(=)([^\"\\s']+)", REGEX_FLAGS_SI); + private static final Pattern P_PROTOCOL = Pattern.compile("^([^:]+):", REGEX_FLAGS_SI); + private static final Pattern P_ENTITY = Pattern.compile("&#(\\d+);?"); + private static final Pattern P_ENTITY_UNICODE = Pattern.compile("&#x([0-9a-f]+);?"); + private static final Pattern P_ENCODE = Pattern.compile("%([0-9a-f]{2});?"); + private static final Pattern P_VALID_ENTITIES = Pattern.compile("&([^&;]*)(?=(;|&|$))"); + private static final Pattern P_VALID_QUOTES = Pattern.compile("(>|^)([^<]+?)(<|$)", Pattern.DOTALL); + private static final Pattern P_END_ARROW = Pattern.compile("^>"); + private static final Pattern P_BODY_TO_END = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_XML_CONTENT = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_STRAY_LEFT_ARROW = Pattern.compile("<([^>]*?)(?=<|$)"); + private static final Pattern P_STRAY_RIGHT_ARROW = Pattern.compile("(^|>)([^<]*?)(?=>)"); + private static final Pattern P_AMP = Pattern.compile("&"); + private static final Pattern P_QUOTE = Pattern.compile("\""); + private static final Pattern P_LEFT_ARROW = Pattern.compile("<"); + private static final Pattern P_RIGHT_ARROW = Pattern.compile(">"); + private static final Pattern P_BOTH_ARROWS = Pattern.compile("<>"); + + // @xxx could grow large... maybe use sesat's ReferenceMap + private static final ConcurrentMap P_REMOVE_PAIR_BLANKS = new ConcurrentHashMap<>(); + private static final ConcurrentMap P_REMOVE_SELF_BLANKS = new ConcurrentHashMap<>(); + + /** + * set of allowed html elements, along with allowed attributes for each element + **/ + private final Map> vAllowed; + /** + * counts of open tags for each (allowable) html element + **/ + private final Map vTagCounts = new HashMap<>(); + + /** + * html elements which must always be self-closing (e.g. "") + **/ + private final String[] vSelfClosingTags; + /** + * html elements which must always have separate opening and closing tags (e.g. "") + **/ + private final String[] vNeedClosingTags; + /** + * set of disallowed html elements + **/ + private final String[] vDisallowed; + /** + * attributes which should be checked for valid protocols + **/ + private final String[] vProtocolAtts; + /** + * allowed protocols + **/ + private final String[] vAllowedProtocols; + /** + * tags which should be removed if they contain no content (e.g. "" or "") + **/ + private final String[] vRemoveBlanks; + /** + * entities allowed within html markup + **/ + private final String[] vAllowedEntities; + /** + * flag determining whether comments are allowed in input String. + */ + private final boolean stripComment; + private final boolean encodeQuotes; + /** + * flag determining whether to try to make tags when presented with "unbalanced" angle brackets (e.g. "" + * becomes " text "). If set to false, unbalanced angle brackets will be html escaped. + */ + private final boolean alwaysMakeTags; + + /** + * Default constructor. + */ + public HTMLFilter() + { + vAllowed = new HashMap<>(); + + final ArrayList a_atts = new ArrayList<>(); + a_atts.add("href"); + a_atts.add("target"); + vAllowed.put("a", a_atts); + + final ArrayList img_atts = new ArrayList<>(); + img_atts.add("src"); + img_atts.add("width"); + img_atts.add("height"); + img_atts.add("alt"); + vAllowed.put("img", img_atts); + + final ArrayList no_atts = new ArrayList<>(); + vAllowed.put("b", no_atts); + vAllowed.put("strong", no_atts); + vAllowed.put("i", no_atts); + vAllowed.put("em", no_atts); + + vSelfClosingTags = new String[] { "img" }; + vNeedClosingTags = new String[] { "a", "b", "strong", "i", "em" }; + vDisallowed = new String[] {}; + vAllowedProtocols = new String[] { "http", "mailto", "https" }; // no ftp. + vProtocolAtts = new String[] { "src", "href" }; + vRemoveBlanks = new String[] { "a", "b", "strong", "i", "em" }; + vAllowedEntities = new String[] { "amp", "gt", "lt", "quot" }; + stripComment = true; + encodeQuotes = true; + alwaysMakeTags = false; + } + + /** + * Map-parameter configurable constructor. + * + * @param conf map containing configuration. keys match field names. + */ + @SuppressWarnings("unchecked") + public HTMLFilter(final Map conf) + { + + assert conf.containsKey("vAllowed") : "configuration requires vAllowed"; + assert conf.containsKey("vSelfClosingTags") : "configuration requires vSelfClosingTags"; + assert conf.containsKey("vNeedClosingTags") : "configuration requires vNeedClosingTags"; + assert conf.containsKey("vDisallowed") : "configuration requires vDisallowed"; + assert conf.containsKey("vAllowedProtocols") : "configuration requires vAllowedProtocols"; + assert conf.containsKey("vProtocolAtts") : "configuration requires vProtocolAtts"; + assert conf.containsKey("vRemoveBlanks") : "configuration requires vRemoveBlanks"; + assert conf.containsKey("vAllowedEntities") : "configuration requires vAllowedEntities"; + + vAllowed = Collections.unmodifiableMap((HashMap>) conf.get("vAllowed")); + vSelfClosingTags = (String[]) conf.get("vSelfClosingTags"); + vNeedClosingTags = (String[]) conf.get("vNeedClosingTags"); + vDisallowed = (String[]) conf.get("vDisallowed"); + vAllowedProtocols = (String[]) conf.get("vAllowedProtocols"); + vProtocolAtts = (String[]) conf.get("vProtocolAtts"); + vRemoveBlanks = (String[]) conf.get("vRemoveBlanks"); + vAllowedEntities = (String[]) conf.get("vAllowedEntities"); + stripComment = conf.containsKey("stripComment") ? (Boolean) conf.get("stripComment") : true; + encodeQuotes = conf.containsKey("encodeQuotes") ? (Boolean) conf.get("encodeQuotes") : true; + alwaysMakeTags = conf.containsKey("alwaysMakeTags") ? (Boolean) conf.get("alwaysMakeTags") : true; + } + + private void reset() + { + vTagCounts.clear(); + } + + // --------------------------------------------------------------- + // my versions of some PHP library functions + public static String chr(final int decimal) + { + return String.valueOf((char) decimal); + } + + public static String htmlSpecialChars(final String s) + { + String result = s; + result = regexReplace(P_AMP, "&", result); + result = regexReplace(P_QUOTE, """, result); + result = regexReplace(P_LEFT_ARROW, "<", result); + result = regexReplace(P_RIGHT_ARROW, ">", result); + return result; + } + + // --------------------------------------------------------------- + + /** + * given a user submitted input String, filter out any invalid or restricted html. + * + * @param input text (i.e. submitted by a user) than may contain html + * @return "clean" version of input, with only valid, whitelisted html elements allowed + */ + public String filter(final String input) + { + reset(); + String s = input; + + s = escapeComments(s); + + s = balanceHTML(s); + + s = checkTags(s); + + s = processRemoveBlanks(s); + + // s = validateEntities(s); + + return s; + } + + public boolean isAlwaysMakeTags() + { + return alwaysMakeTags; + } + + public boolean isStripComments() + { + return stripComment; + } + + private String escapeComments(final String s) + { + final Matcher m = P_COMMENTS.matcher(s); + final StringBuffer buf = new StringBuffer(); + if (m.find()) + { + final String match = m.group(1); // (.*?) + m.appendReplacement(buf, Matcher.quoteReplacement("")); + } + m.appendTail(buf); + + return buf.toString(); + } + + private String balanceHTML(String s) + { + if (alwaysMakeTags) + { + // + // try and form html + // + s = regexReplace(P_END_ARROW, "", s); + // 不追加结束标签 + s = regexReplace(P_BODY_TO_END, "<$1>", s); + s = regexReplace(P_XML_CONTENT, "$1<$2", s); + + } + else + { + // + // escape stray brackets + // + s = regexReplace(P_STRAY_LEFT_ARROW, "<$1", s); + s = regexReplace(P_STRAY_RIGHT_ARROW, "$1$2><", s); + + // + // the last regexp causes '<>' entities to appear + // (we need to do a lookahead assertion so that the last bracket can + // be used in the next pass of the regexp) + // + s = regexReplace(P_BOTH_ARROWS, "", s); + } + + return s; + } + + private String checkTags(String s) + { + Matcher m = P_TAGS.matcher(s); + + final StringBuffer buf = new StringBuffer(); + while (m.find()) + { + String replaceStr = m.group(1); + replaceStr = processTag(replaceStr); + m.appendReplacement(buf, Matcher.quoteReplacement(replaceStr)); + } + m.appendTail(buf); + + // these get tallied in processTag + // (remember to reset before subsequent calls to filter method) + final StringBuilder sBuilder = new StringBuilder(buf.toString()); + for (String key : vTagCounts.keySet()) + { + for (int ii = 0; ii < vTagCounts.get(key); ii++) + { + sBuilder.append(""); + } + } + s = sBuilder.toString(); + + return s; + } + + private String processRemoveBlanks(final String s) + { + String result = s; + for (String tag : vRemoveBlanks) + { + if (!P_REMOVE_PAIR_BLANKS.containsKey(tag)) + { + P_REMOVE_PAIR_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?>")); + } + result = regexReplace(P_REMOVE_PAIR_BLANKS.get(tag), "", result); + if (!P_REMOVE_SELF_BLANKS.containsKey(tag)) + { + P_REMOVE_SELF_BLANKS.putIfAbsent(tag, Pattern.compile("<" + tag + "(\\s[^>]*)?/>")); + } + result = regexReplace(P_REMOVE_SELF_BLANKS.get(tag), "", result); + } + + return result; + } + + private static String regexReplace(final Pattern regex_pattern, final String replacement, final String s) + { + Matcher m = regex_pattern.matcher(s); + return m.replaceAll(replacement); + } + + private String processTag(final String s) + { + // ending tags + Matcher m = P_END_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + if (allowed(name)) + { + if (!inArray(name, vSelfClosingTags)) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) - 1); + return ""; + } + } + } + } + + // starting tags + m = P_START_TAG.matcher(s); + if (m.find()) + { + final String name = m.group(1).toLowerCase(); + final String body = m.group(2); + String ending = m.group(3); + + // debug( "in a starting tag, name='" + name + "'; body='" + body + "'; ending='" + ending + "'" ); + if (allowed(name)) + { + final StringBuilder params = new StringBuilder(); + + final Matcher m2 = P_QUOTED_ATTRIBUTES.matcher(body); + final Matcher m3 = P_UNQUOTED_ATTRIBUTES.matcher(body); + final List paramNames = new ArrayList<>(); + final List paramValues = new ArrayList<>(); + while (m2.find()) + { + paramNames.add(m2.group(1)); // ([a-z0-9]+) + paramValues.add(m2.group(3)); // (.*?) + } + while (m3.find()) + { + paramNames.add(m3.group(1)); // ([a-z0-9]+) + paramValues.add(m3.group(3)); // ([^\"\\s']+) + } + + String paramName, paramValue; + for (int ii = 0; ii < paramNames.size(); ii++) + { + paramName = paramNames.get(ii).toLowerCase(); + paramValue = paramValues.get(ii); + + // debug( "paramName='" + paramName + "'" ); + // debug( "paramValue='" + paramValue + "'" ); + // debug( "allowed? " + vAllowed.get( name ).contains( paramName ) ); + + if (allowedAttribute(name, paramName)) + { + if (inArray(paramName, vProtocolAtts)) + { + paramValue = processParamProtocol(paramValue); + } + params.append(' ').append(paramName).append("=\\\"").append(paramValue).append("\\\""); + } + } + + if (inArray(name, vSelfClosingTags)) + { + ending = " /"; + } + + if (inArray(name, vNeedClosingTags)) + { + ending = ""; + } + + if (ending == null || ending.length() < 1) + { + if (vTagCounts.containsKey(name)) + { + vTagCounts.put(name, vTagCounts.get(name) + 1); + } + else + { + vTagCounts.put(name, 1); + } + } + else + { + ending = " /"; + } + return "<" + name + params + ending + ">"; + } + else + { + return ""; + } + } + + // comments + m = P_COMMENT.matcher(s); + if (!stripComment && m.find()) + { + return "<" + m.group() + ">"; + } + + return ""; + } + + private String processParamProtocol(String s) + { + s = decodeEntities(s); + final Matcher m = P_PROTOCOL.matcher(s); + if (m.find()) + { + final String protocol = m.group(1); + if (!inArray(protocol, vAllowedProtocols)) + { + // bad protocol, turn into local anchor link instead + s = "#" + s.substring(protocol.length() + 1); + if (s.startsWith("#//")) + { + s = "#" + s.substring(3); + } + } + } + + return s; + } + + private String decodeEntities(String s) + { + StringBuffer buf = new StringBuffer(); + + Matcher m = P_ENTITY.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.decode(match).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENTITY_UNICODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + buf = new StringBuffer(); + m = P_ENCODE.matcher(s); + while (m.find()) + { + final String match = m.group(1); + final int decimal = Integer.valueOf(match, 16).intValue(); + m.appendReplacement(buf, Matcher.quoteReplacement(chr(decimal))); + } + m.appendTail(buf); + s = buf.toString(); + + s = validateEntities(s); + return s; + } + + private String validateEntities(final String s) + { + StringBuffer buf = new StringBuffer(); + + // validate entities throughout the string + Matcher m = P_VALID_ENTITIES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // ([^&;]*) + final String two = m.group(2); // (?=(;|&|$)) + m.appendReplacement(buf, Matcher.quoteReplacement(checkEntity(one, two))); + } + m.appendTail(buf); + + return encodeQuotes(buf.toString()); + } + + private String encodeQuotes(final String s) + { + if (encodeQuotes) + { + StringBuffer buf = new StringBuffer(); + Matcher m = P_VALID_QUOTES.matcher(s); + while (m.find()) + { + final String one = m.group(1); // (>|^) + final String two = m.group(2); // ([^<]+?) + final String three = m.group(3); // (<|$) + // 不替换双引号为",防止json格式无效 regexReplace(P_QUOTE, """, two) + m.appendReplacement(buf, Matcher.quoteReplacement(one + two + three)); + } + m.appendTail(buf); + return buf.toString(); + } + else + { + return s; + } + } + + private String checkEntity(final String preamble, final String term) + { + + return ";".equals(term) && isValidEntity(preamble) ? '&' + preamble : "&" + preamble; + } + + private boolean isValidEntity(final String entity) + { + return inArray(entity, vAllowedEntities); + } + + private static boolean inArray(final String s, final String[] array) + { + for (String item : array) + { + if (item != null && item.equals(s)) + { + return true; + } + } + return false; + } + + private boolean allowed(final String name) + { + return (vAllowed.isEmpty() || vAllowed.containsKey(name)) && !inArray(name, vDisallowed); + } + + private boolean allowedAttribute(final String name, final String paramName) + { + return allowed(name) && (vAllowed.isEmpty() || vAllowed.get(name).contains(paramName)); + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java new file mode 100644 index 0000000..589d123 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpHelper.java @@ -0,0 +1,55 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletRequest; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 通用http工具封装 + * + * @author ruoyi + */ +public class HttpHelper +{ + private static final Logger LOGGER = LoggerFactory.getLogger(HttpHelper.class); + + public static String getBodyString(ServletRequest request) + { + StringBuilder sb = new StringBuilder(); + BufferedReader reader = null; + try (InputStream inputStream = request.getInputStream()) + { + reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + String line = ""; + while ((line = reader.readLine()) != null) + { + sb.append(line); + } + } + catch (IOException e) + { + LOGGER.warn("getBodyString出现问题!"); + } + finally + { + if (reader != null) + { + try + { + reader.close(); + } + catch (IOException e) + { + LOGGER.error(ExceptionUtils.getMessage(e)); + } + } + } + return sb.toString(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java new file mode 100644 index 0000000..534d21c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/http/HttpUtils.java @@ -0,0 +1,293 @@ +package com.ruoyi.common.utils.http; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ConnectException; +import java.net.SocketTimeoutException; +import java.net.URL; +import java.net.URLConnection; +import java.nio.charset.StandardCharsets; +import java.security.cert.X509Certificate; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.http.MediaType; + +/** + * 通用http发送方法 + * + * @author ruoyi + */ +public class HttpUtils +{ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url) + { + return sendGet(url, StringUtils.EMPTY); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param) + { + return sendGet(url, param, Constants.UTF8); + } + + /** + * 向指定 URL 发送GET方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @param contentType 编码类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendGet(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + BufferedReader in = null; + try + { + String urlNameString = StringUtils.isNotBlank(param) ? url + "?" + param : url; + log.info("sendGet - {}", urlNameString); + URL realUrl = new URL(urlNameString); + URLConnection connection = realUrl.openConnection(); + connection.setRequestProperty("accept", "*/*"); + connection.setRequestProperty("connection", "Keep-Alive"); + connection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + connection.connect(); + in = new BufferedReader(new InputStreamReader(connection.getInputStream(), contentType)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendGet IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendGet Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (in != null) + { + in.close(); + } + } + catch (Exception ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数,请求参数应该是 name1=value1&name2=value2 的形式。 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param) + { + return sendPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + /** + * 向指定 URL 发送POST方法的请求 + * + * @param url 发送请求的 URL + * @param param 请求参数 + * @param contentType 内容类型 + * @return 所代表远程资源的响应结果 + */ + public static String sendPost(String url, String param, String contentType) + { + PrintWriter out = null; + BufferedReader in = null; + StringBuilder result = new StringBuilder(); + try + { + log.info("sendPost - {}", url); + URL realUrl = new URL(url); + URLConnection conn = realUrl.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + out = new PrintWriter(conn.getOutputStream()); + out.print(param); + out.flush(); + in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8)); + String line; + while ((line = in.readLine()) != null) + { + result.append(line); + } + log.info("recv - {}", result); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + if (in != null) + { + in.close(); + } + } + catch (IOException ex) + { + log.error("调用in.close Exception, url=" + url + ",param=" + param, ex); + } + } + return result.toString(); + } + + public static String sendSSLPost(String url, String param) + { + return sendSSLPost(url, param, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + } + + public static String sendSSLPost(String url, String param, String contentType) + { + StringBuilder result = new StringBuilder(); + String urlNameString = url + "?" + param; + try + { + log.info("sendSSLPost - {}", urlNameString); + SSLContext sc = SSLContext.getInstance("SSL"); + sc.init(null, new TrustManager[] { new TrustAnyTrustManager() }, new java.security.SecureRandom()); + URL console = new URL(urlNameString); + HttpsURLConnection conn = (HttpsURLConnection) console.openConnection(); + conn.setRequestProperty("accept", "*/*"); + conn.setRequestProperty("connection", "Keep-Alive"); + conn.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"); + conn.setRequestProperty("Accept-Charset", "utf-8"); + conn.setRequestProperty("Content-Type", contentType); + conn.setDoOutput(true); + conn.setDoInput(true); + + conn.setSSLSocketFactory(sc.getSocketFactory()); + conn.setHostnameVerifier(new TrustAnyHostnameVerifier()); + conn.connect(); + InputStream is = conn.getInputStream(); + BufferedReader br = new BufferedReader(new InputStreamReader(is)); + String ret = ""; + while ((ret = br.readLine()) != null) + { + if (ret != null && !"".equals(ret.trim())) + { + result.append(new String(ret.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); + } + } + log.info("recv - {}", result); + conn.disconnect(); + br.close(); + } + catch (ConnectException e) + { + log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e); + } + catch (SocketTimeoutException e) + { + log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e); + } + catch (IOException e) + { + log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e); + } + catch (Exception e) + { + log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e); + } + return result.toString(); + } + + private static class TrustAnyTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return new X509Certificate[] {}; + } + } + + private static class TrustAnyHostnameVerifier implements HostnameVerifier + { + @Override + public boolean verify(String hostname, SSLSession session) + { + return true; + } + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java new file mode 100644 index 0000000..edfe419 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/AddressUtils.java @@ -0,0 +1,56 @@ +package com.ruoyi.common.utils.ip; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpUtils; + +/** + * 获取地址类 + * + * @author ruoyi + */ +public class AddressUtils +{ + private static final Logger log = LoggerFactory.getLogger(AddressUtils.class); + + // IP地址查询 + public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp"; + + // 未知地址 + public static final String UNKNOWN = "XX XX"; + + public static String getRealAddressByIP(String ip) + { + // 内网不查询 + if (IpUtils.internalIp(ip)) + { + return "内网IP"; + } + if (RuoYiConfig.isAddressEnabled()) + { + try + { + String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", Constants.GBK); + if (StringUtils.isEmpty(rspStr)) + { + log.error("获取地理位置异常 {}", ip); + return UNKNOWN; + } + JSONObject obj = JSON.parseObject(rspStr); + String region = obj.getString("pro"); + String city = obj.getString("city"); + return String.format("%s %s", region, city); + } + catch (Exception e) + { + log.error("获取地理位置异常 {}", ip); + } + } + return UNKNOWN; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java new file mode 100644 index 0000000..8e89e30 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/ip/IpUtils.java @@ -0,0 +1,382 @@ +package com.ruoyi.common.utils.ip; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import javax.servlet.http.HttpServletRequest; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 获取IP方法 + * + * @author ruoyi + */ +public class IpUtils +{ + public final static String REGX_0_255 = "(25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]\\d|\\d)"; + // 匹配 ip + public final static String REGX_IP = "((" + REGX_0_255 + "\\.){3}" + REGX_0_255 + ")"; + public final static String REGX_IP_WILDCARD = "(((\\*\\.){3}\\*)|(" + REGX_0_255 + "(\\.\\*){3})|(" + REGX_0_255 + "\\." + REGX_0_255 + ")(\\.\\*){2}" + "|((" + REGX_0_255 + "\\.){3}\\*))"; + // 匹配网段 + public final static String REGX_IP_SEG = "(" + REGX_IP + "\\-" + REGX_IP + ")"; + + /** + * 获取客户端IP + * + * @return IP地址 + */ + public static String getIpAddr() + { + return getIpAddr(ServletUtils.getRequest()); + } + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getIpAddr(HttpServletRequest request) + { + if (request == null) + { + return "unknown"; + } + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Forwarded-For"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getHeader("X-Real-IP"); + } + + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) + { + ip = request.getRemoteAddr(); + } + + return "0:0:0:0:0:0:0:1".equals(ip) ? "127.0.0.1" : getMultistageReverseProxyIp(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param ip IP地址 + * @return 结果 + */ + public static boolean internalIp(String ip) + { + byte[] addr = textToNumericFormatV4(ip); + return internalIp(addr) || "127.0.0.1".equals(ip); + } + + /** + * 检查是否为内部IP地址 + * + * @param addr byte地址 + * @return 结果 + */ + private static boolean internalIp(byte[] addr) + { + if (StringUtils.isNull(addr) || addr.length < 2) + { + return true; + } + final byte b0 = addr[0]; + final byte b1 = addr[1]; + // 10.x.x.x/8 + final byte SECTION_1 = 0x0A; + // 172.16.x.x/12 + final byte SECTION_2 = (byte) 0xAC; + final byte SECTION_3 = (byte) 0x10; + final byte SECTION_4 = (byte) 0x1F; + // 192.168.x.x/16 + final byte SECTION_5 = (byte) 0xC0; + final byte SECTION_6 = (byte) 0xA8; + switch (b0) + { + case SECTION_1: + return true; + case SECTION_2: + if (b1 >= SECTION_3 && b1 <= SECTION_4) + { + return true; + } + case SECTION_5: + switch (b1) + { + case SECTION_6: + return true; + } + default: + return false; + } + } + + /** + * 将IPv4地址转换成字节 + * + * @param text IPv4地址 + * @return byte 字节 + */ + public static byte[] textToNumericFormatV4(String text) + { + if (text.length() == 0) + { + return null; + } + + byte[] bytes = new byte[4]; + String[] elements = text.split("\\.", -1); + try + { + long l; + int i; + switch (elements.length) + { + case 1: + l = Long.parseLong(elements[0]); + if ((l < 0L) || (l > 4294967295L)) + { + return null; + } + bytes[0] = (byte) (int) (l >> 24 & 0xFF); + bytes[1] = (byte) (int) ((l & 0xFFFFFF) >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 2: + l = Integer.parseInt(elements[0]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[0] = (byte) (int) (l & 0xFF); + l = Integer.parseInt(elements[1]); + if ((l < 0L) || (l > 16777215L)) + { + return null; + } + bytes[1] = (byte) (int) (l >> 16 & 0xFF); + bytes[2] = (byte) (int) ((l & 0xFFFF) >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 3: + for (i = 0; i < 2; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + l = Integer.parseInt(elements[2]); + if ((l < 0L) || (l > 65535L)) + { + return null; + } + bytes[2] = (byte) (int) (l >> 8 & 0xFF); + bytes[3] = (byte) (int) (l & 0xFF); + break; + case 4: + for (i = 0; i < 4; ++i) + { + l = Integer.parseInt(elements[i]); + if ((l < 0L) || (l > 255L)) + { + return null; + } + bytes[i] = (byte) (int) (l & 0xFF); + } + break; + default: + return null; + } + } + catch (NumberFormatException e) + { + return null; + } + return bytes; + } + + /** + * 获取IP地址 + * + * @return 本地IP地址 + */ + public static String getHostIp() + { + try + { + return InetAddress.getLocalHost().getHostAddress(); + } + catch (UnknownHostException e) + { + } + return "127.0.0.1"; + } + + /** + * 获取主机名 + * + * @return 本地主机名 + */ + public static String getHostName() + { + try + { + return InetAddress.getLocalHost().getHostName(); + } + catch (UnknownHostException e) + { + } + return "未知"; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) + { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) + { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) + { + if (false == isUnknown(subIp)) + { + ip = subIp; + break; + } + } + } + return StringUtils.substring(ip, 0, 255); + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) + { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 是否为IP + */ + public static boolean isIP(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP); + } + + /** + * 是否为IP,或 *为间隔的通配符地址 + */ + public static boolean isIpWildCard(String ip) + { + return StringUtils.isNotBlank(ip) && ip.matches(REGX_IP_WILDCARD); + } + + /** + * 检测参数是否在ip通配符里 + */ + public static boolean ipIsInWildCardNoCheck(String ipWildCard, String ip) + { + String[] s1 = ipWildCard.split("\\."); + String[] s2 = ip.split("\\."); + boolean isMatchedSeg = true; + for (int i = 0; i < s1.length && !s1[i].equals("*"); i++) + { + if (!s1[i].equals(s2[i])) + { + isMatchedSeg = false; + break; + } + } + return isMatchedSeg; + } + + /** + * 是否为特定格式如:“10.10.10.1-10.10.10.99”的ip段字符串 + */ + public static boolean isIPSegment(String ipSeg) + { + return StringUtils.isNotBlank(ipSeg) && ipSeg.matches(REGX_IP_SEG); + } + + /** + * 判断ip是否在指定网段中 + */ + public static boolean ipIsInNetNoCheck(String iparea, String ip) + { + int idx = iparea.indexOf('-'); + String[] sips = iparea.substring(0, idx).split("\\."); + String[] sipe = iparea.substring(idx + 1).split("\\."); + String[] sipt = ip.split("\\."); + long ips = 0L, ipe = 0L, ipt = 0L; + for (int i = 0; i < 4; ++i) + { + ips = ips << 8 | Integer.parseInt(sips[i]); + ipe = ipe << 8 | Integer.parseInt(sipe[i]); + ipt = ipt << 8 | Integer.parseInt(sipt[i]); + } + if (ips > ipe) + { + long t = ips; + ips = ipe; + ipe = t; + } + return ips <= ipt && ipt <= ipe; + } + + /** + * 校验ip是否符合过滤串规则 + * + * @param filter 过滤IP列表,支持后缀'*'通配,支持网段如:`10.10.10.1-10.10.10.99` + * @param ip 校验IP地址 + * @return boolean 结果 + */ + public static boolean isMatchedIp(String filter, String ip) + { + if (StringUtils.isEmpty(filter) || StringUtils.isEmpty(ip)) + { + return false; + } + String[] ips = filter.split(";"); + for (String iStr : ips) + { + if (isIP(iStr) && iStr.equals(ip)) + { + return true; + } + else if (isIpWildCard(iStr) && ipIsInWildCardNoCheck(iStr, ip)) + { + return true; + } + else if (isIPSegment(iStr) && ipIsInNetNoCheck(iStr, ip)) + { + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java new file mode 100644 index 0000000..ccab288 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelHandlerAdapter.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.utils.poi; + +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Workbook; + +/** + * Excel数据格式处理适配器 + * + * @author ruoyi + */ +public interface ExcelHandlerAdapter +{ + /** + * 格式化 + * + * @param value 单元格数据值 + * @param args excel注解args参数组 + * @param cell 单元格对象 + * @param wb 工作簿对象 + * + * @return 处理后的值 + */ + Object format(Object value, String[] args, Cell cell, Workbook wb); +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java new file mode 100644 index 0000000..989f988 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -0,0 +1,1901 @@ +package com.ruoyi.common.utils.poi; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; +import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.common.utils.MessageUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.RegExUtils; +import org.apache.commons.lang3.reflect.FieldUtils; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFPicture; +import org.apache.poi.hssf.usermodel.HSSFPictureData; +import org.apache.poi.hssf.usermodel.HSSFShape; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ooxml.POIXMLDocumentPart; +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.CellType; +import org.apache.poi.ss.usermodel.ClientAnchor; +import org.apache.poi.ss.usermodel.DataFormat; +import org.apache.poi.ss.usermodel.DataValidation; +import org.apache.poi.ss.usermodel.DataValidationConstraint; +import org.apache.poi.ss.usermodel.DataValidationHelper; +import org.apache.poi.ss.usermodel.DateUtil; +import org.apache.poi.ss.usermodel.Drawing; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Name; +import org.apache.poi.ss.usermodel.PictureData; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.CellRangeAddress; +import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.util.IOUtils; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFDataValidation; +import org.apache.poi.xssf.usermodel.XSSFDrawing; +import org.apache.poi.xssf.usermodel.XSSFPicture; +import org.apache.poi.xssf.usermodel.XSSFShape; +import org.apache.poi.xssf.usermodel.XSSFSheet; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.annotation.Excels; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileTypeUtils; +import com.ruoyi.common.utils.file.FileUtils; +import com.ruoyi.common.utils.file.ImageUtils; +import com.ruoyi.common.utils.reflect.ReflectUtils; + +/** + * Excel相关处理 + * + * @author ruoyi + */ +public class ExcelUtil +{ + private static final Logger log = LoggerFactory.getLogger(ExcelUtil.class); + + public static final String SEPARATOR = ","; + + public static final String FORMULA_REGEX_STR = "=|-|\\+|@"; + + public static final String[] FORMULA_STR = { "=", "-", "+", "@" }; + + /** + * 用于dictType属性数据存储,避免重复查缓存 + */ + public Map sysDictMap = new HashMap(); + + /** + * Excel sheet最大行数,默认65536 + */ + public static final int sheetSize = 65536; + + /** + * 工作表名称 + */ + private String sheetName; + + /** + * 导出类型(EXPORT:导出数据;IMPORT:导入模板) + */ + private Type type; + + /** + * 工作薄对象 + */ + private Workbook wb; + + /** + * 工作表对象 + */ + private Sheet sheet; + + /** + * 样式列表 + */ + private Map styles; + + /** + * 导入导出数据列表 + */ + private List list; + + /** + * 注解列表 + */ + private List fields; + + /** + * 当前行号 + */ + private int rownum; + + /** + * 标题 + */ + private String title; + + /** + * 最大高度 + */ + private short maxHeight; + + /** + * 合并后最后行数 + */ + private int subMergedLastRowNum = 0; + + /** + * 合并后开始行数 + */ + private int subMergedFirstRowNum = 1; + + /** + * 对象的子列表方法 + */ + private Method subMethod; + + /** + * 对象的子列表属性 + */ + private List subFields; + + /** + * 统计列表 + */ + private Map statistics = new HashMap(); + + /** + * 数字格式 + */ + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + /** + * 实体对象 + */ + public Class clazz; + + /** + * 需要显示列属性 + */ + public String[] includeFields; + + /** + * 需要排除列属性 + */ + public String[] excludeFields; + + public ExcelUtil(Class clazz) + { + this.clazz = clazz; + } + + /** + * 仅在Excel中显示列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void showColumn(String... fields) + { + this.includeFields = fields; + } + + /** + * 隐藏Excel中列属性 + * + * @param fields 列属性名 示例[单个"name"/多个"id","name"] + */ + public void hideColumn(String... fields) + { + this.excludeFields = fields; + } + + public void init(List list, String sheetName, String title, Type type) + { + if (list == null) + { + list = new ArrayList(); + } + this.list = list; + this.sheetName = sheetName; + this.type = type; + this.title = title; + createExcelField(); + createWorkbook(); + createTitle(); + createSubHead(); + } + + /** + * 创建excel第一行标题 + */ + public void createTitle() + { + if (StringUtils.isNotEmpty(title)) + { + int titleLastCol = this.fields.size() - 1; + if (isSubList()) + { + titleLastCol = titleLastCol + subFields.size() - 1; + } + Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); + titleRow.setHeightInPoints(30); + Cell titleCell = titleRow.createCell(0); + titleCell.setCellStyle(styles.get("title")); + titleCell.setCellValue(title); + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, titleLastCol)); + } + } + + /** + * 创建对象的子列表名称 + */ + public void createSubHead() + { + if (isSubList()) + { + Row subRow = sheet.createRow(rownum); + int column = 0; + int subFieldSize = subFields != null ? subFields.size() : 0; + for (Object[] objects : fields) + { + Field field = (Field) objects[0]; + Excel attr = (Excel) objects[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + Cell cell = subRow.createCell(column); + cell.setCellValue(attr.name()); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (subFieldSize > 1) + { + CellRangeAddress cellAddress = new CellRangeAddress(rownum, rownum, column, column + subFieldSize - 1); + sheet.addMergedRegion(cellAddress); + } + column += subFieldSize; + } + else + { + Cell cell = subRow.createCell(column++); + cell.setCellValue(attr.name()); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + } + } + rownum++; + } + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(InputStream is) + { + return importExcel(is, 0); + } + + /** + * 对excel表单默认第一个索引名转换成list + * + * @param is 输入流 + * @param titleNum 标题占用行数 + * @return 转换后集合 + */ + public List importExcel(InputStream is, int titleNum) + { + List list = null; + try + { + list = importExcel(StringUtils.EMPTY, is, titleNum); + } + catch (Exception e) + { + log.error("导入Excel异常{}", e.getMessage()); + throw new UtilException(e.getMessage()); + } + finally + { + IOUtils.closeQuietly(is); + } + return list; + } + + /** + * 对excel表单指定表格索引名转换成list + * + * @param sheetName 表格索引名 + * @param titleNum 标题占用行数 + * @param is 输入流 + * @return 转换后集合 + */ + public List importExcel(String sheetName, InputStream is, int titleNum) throws Exception + { + this.type = Type.IMPORT; + this.wb = WorkbookFactory.create(is); + List list = new ArrayList(); + // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet + Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0); + if (sheet == null) + { + throw new IOException("文件sheet不存在"); + } + boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook); + Map> pictures = null; + if (isXSSFWorkbook) + { + pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb); + } + else + { + pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb); + } + // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1 + int rows = sheet.getLastRowNum(); + if (rows > 0) + { + // 定义一个map用于存放excel列的序号和field. + Map cellMap = new HashMap(); + // 获取表头 + Row heard = sheet.getRow(titleNum); + for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) + { + Cell cell = heard.getCell(i); + if (StringUtils.isNotNull(cell)) + { + String value = this.getCellValue(heard, i).toString(); + cellMap.put(value, i); + } + else + { + cellMap.put(null, i); + } + } + // 有数据时才处理 得到类的所有field. + List fields = this.getFields(); + Map fieldsMap = new HashMap(); + for (Object[] objects : fields) + { + Excel attr = (Excel) objects[1]; + Integer column = cellMap.get(attr.name()); + if (column != null) + { + fieldsMap.put(column, objects); + } + } + for (int i = titleNum + 1; i <= rows; i++) + { + // 从第2行开始取数据,默认第一行是表头. + Row row = sheet.getRow(i); + // 判断当前行是否是空行 + if (isRowEmpty(row)) + { + continue; + } + T entity = null; + for (Map.Entry entry : fieldsMap.entrySet()) + { + Object val = this.getCellValue(row, entry.getKey()); + + // 如果不存在实例则新建. + entity = (entity == null ? clazz.newInstance() : entity); + // 从map中得到对应列的field. + Field field = (Field) entry.getValue()[0]; + Excel attr = (Excel) entry.getValue()[1]; + // 取得类型,并根据对象类型设置值. + Class fieldType = field.getType(); + if (String.class == fieldType) + { + String s = Convert.toStr(val); + if (s.matches("^\\d+\\.0$")) + { + val = StringUtils.substringBefore(s, ".0"); + } + else + { + String dateFormat = field.getAnnotation(Excel.class).dateFormat(); + if (StringUtils.isNotEmpty(dateFormat)) + { + val = parseDateToStr(dateFormat, val); + } + else + { + val = Convert.toStr(val); + } + } + } + else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toInt(val); + } + else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val))) + { + val = Convert.toLong(val); + } + else if (Double.TYPE == fieldType || Double.class == fieldType) + { + val = Convert.toDouble(val); + } + else if (Float.TYPE == fieldType || Float.class == fieldType) + { + val = Convert.toFloat(val); + } + else if (BigDecimal.class == fieldType) + { + val = Convert.toBigDecimal(val); + } + else if (Date.class == fieldType) + { + if (val instanceof String) + { + val = DateUtils.parseDate(val); + } + else if (val instanceof Double) + { + val = DateUtil.getJavaDate((Double) val); + } + } + else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) + { + val = Convert.toBool(val, false); + } + if (StringUtils.isNotNull(fieldType)) + { + String propertyName = field.getName(); + if (StringUtils.isNotEmpty(attr.targetAttr())) + { + propertyName = field.getName() + "." + attr.targetAttr(); + } + if (StringUtils.isNotEmpty(attr.readConverterExp())) + { + val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator()); + } + else if (StringUtils.isNotEmpty(attr.dictType())) + { + if (!sysDictMap.containsKey(attr.dictType() + val)) + { + String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator()); + sysDictMap.put(attr.dictType() + val, dictValue); + } + val = sysDictMap.get(attr.dictType() + val); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + val = dataFormatHandlerAdapter(val, attr, null); + } + else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures)) + { + StringBuilder propertyString = new StringBuilder(); + List images = pictures.get(row.getRowNum() + "_" + entry.getKey()); + for (PictureData picture : images) + { + byte[] data = picture.getData(); + String fileName = FileUtils.writeImportBytes(data); + propertyString.append(fileName).append(SEPARATOR); + } + val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR); + } + ReflectUtils.invokeSetter(entity, propertyName, val); + } + } + list.add(entity); + } + } + return list; + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName) + { + return exportExcel(list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult exportExcel(List list, String sheetName, String title) + { + this.init(list, sheetName, title, Type.EXPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName) + { + exportExcel(response, list, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param response 返回数据 + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void exportExcel(HttpServletResponse response, List list, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(list, sheetName, title, Type.EXPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName) + { + return importTemplateExcel(sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public AjaxResult importTemplateExcel(String sheetName, String title) + { + this.init(null, sheetName, title, Type.IMPORT); + return exportExcel(); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName) + { + importTemplateExcel(response, sheetName, StringUtils.EMPTY); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @param sheetName 工作表的名称 + * @param title 标题 + * @return 结果 + */ + public void importTemplateExcel(HttpServletResponse response, String sheetName, String title) + { + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + this.init(null, sheetName, title, Type.IMPORT); + exportExcel(response); + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public void exportExcel(HttpServletResponse response) + { + try + { + writeSheet(); + wb.write(response.getOutputStream()); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + } + finally + { + IOUtils.closeQuietly(wb); + } + } + + /** + * 对list数据源将其里面的数据导入到excel表单 + * + * @return 结果 + */ + public AjaxResult exportExcel() + { + OutputStream out = null; + try + { + writeSheet(); + String filename = encodingFilename(sheetName); + out = new FileOutputStream(getAbsoluteFile(filename)); + wb.write(out); + return AjaxResult.success(filename); + } + catch (Exception e) + { + log.error("导出Excel异常{}", e.getMessage()); + throw new UtilException("导出Excel失败,请联系网站管理员!"); + } + finally + { + IOUtils.closeQuietly(wb); + IOUtils.closeQuietly(out); + } + } + + /** + * 创建写入数据到Sheet + */ + public void writeSheet() + { + // 取出一共有多少个sheet. + int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize)); + for (int index = 0; index < sheetNo; index++) + { + createSheet(sheetNo, index); + + // 产生一行 + Row row = sheet.createRow(rownum); + int column = 0; + // 写入各个字段的列头名称 + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + this.createHeadCell(subExcel, row, column++); + } + } + else + { + this.createHeadCell(excel, row, column++); + } + } + if (Type.EXPORT.equals(type)) + { + fillExcelData(index, row); + addStatisticsRow(); + } + } + } + + /** + * 填充excel数据 + * + * @param index 序号 + * @param row 单元格行 + */ + @SuppressWarnings("unchecked") + public void fillExcelData(int index, Row row) + { + int startNo = index * sheetSize; + int endNo = Math.min(startNo + sheetSize, list.size()); + int currentRowNum = rownum + 1; // 从标题行后开始 + + for (int i = startNo; i < endNo; i++) + { + row = sheet.createRow(currentRowNum); + T vo = (T) list.get(i); + int column = 0; + int maxSubListSize = getCurrentMaxSubListSize(vo); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, excel); + if (subList != null && !subList.isEmpty()) + { + int subIndex = 0; + for (Object subVo : subList) + { + Row subRow = sheet.getRow(currentRowNum + subIndex); + if (subRow == null) + { + subRow = sheet.createRow(currentRowNum + subIndex); + } + + int subColumn = column; + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + addCell(subExcel, subRow, (T) subVo, subField, subColumn++); + } + subIndex++; + } + column += subFields.size(); + } + } + catch (Exception e) + { + log.error("填充集合数据失败", e); + } + } + else + { + // 创建单元格并设置值 + addCell(excel, row, vo, field, column); + if (maxSubListSize > 1 && excel.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(currentRowNum, currentRowNum + maxSubListSize - 1, column, column)); + } + column++; + } + } + currentRowNum += maxSubListSize; + } + } + + /** + * 获取子列表最大数 + */ + private int getCurrentMaxSubListSize(T vo) + { + int maxSubListSize = 1; + for (Object[] os : fields) + { + Field field = (Field) os[0]; + if (Collection.class.isAssignableFrom(field.getType())) + { + try + { + Collection subList = (Collection) getTargetValue(vo, field, (Excel) os[1]); + if (subList != null && !subList.isEmpty()) + { + maxSubListSize = Math.max(maxSubListSize, subList.size()); + } + } + catch (Exception e) + { + log.error("获取集合大小失败", e); + } + } + } + return maxSubListSize; + } + + /** + * 创建表格样式 + * + * @param wb 工作薄对象 + * @return 样式列表 + */ + private Map createStyles(Workbook wb) + { + // 写入各条记录,每条记录对应excel表中的一行 + Map styles = new HashMap(); + CellStyle style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font titleFont = wb.createFont(); + titleFont.setFontName("Arial"); + titleFont.setFontHeightInPoints((short) 16); + titleFont.setBold(true); + style.setFont(titleFont); + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + styles.put("title", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + style.setFont(dataFont); + styles.put("data", style); + + style = wb.createCellStyle(); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + Font totalFont = wb.createFont(); + totalFont.setFontName("Arial"); + totalFont.setFontHeightInPoints((short) 10); + style.setFont(totalFont); + styles.put("total", style); + + styles.putAll(annotationHeaderStyles(wb, styles)); + + styles.putAll(annotationDataStyles(wb)); + + return styles; + } + + /** + * 根据Excel注解创建表格头样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationHeaderStyles(Workbook wb, Map styles) + { + Map headerStyles = new HashMap(); + for (Object[] os : fields) + { + Excel excel = (Excel) os[1]; + String key = StringUtils.format("header_{}_{}", excel.headerColor(), excel.headerBackgroundColor()); + if (!headerStyles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.cloneStyleFrom(styles.get("data")); + style.setAlignment(HorizontalAlignment.CENTER); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setFillForegroundColor(excel.headerBackgroundColor().index); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + Font headerFont = wb.createFont(); + headerFont.setFontName("Arial"); + headerFont.setFontHeightInPoints((short) 10); + headerFont.setBold(true); + headerFont.setColor(excel.headerColor().index); + style.setFont(headerFont); + // 设置表格头单元格文本形式 + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + headerStyles.put(key, style); + } + } + return headerStyles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param wb 工作薄对象 + * @return 自定义样式列表 + */ + private Map annotationDataStyles(Workbook wb) + { + Map styles = new HashMap(); + for (Object[] os : fields) + { + Field field = (Field) os[0]; + Excel excel = (Excel) os[1]; + if (Collection.class.isAssignableFrom(field.getType())) + { + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + List subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + for (Field subField : subFields) + { + Excel subExcel = subField.getAnnotation(Excel.class); + annotationDataStyles(styles, subField, subExcel); + } + } + else + { + annotationDataStyles(styles, field, excel); + } + } + return styles; + } + + /** + * 根据Excel注解创建表格列样式 + * + * @param styles 自定义样式列表 + * @param field 属性列信息 + * @param excel 注解信息 + */ + public void annotationDataStyles(Map styles, Field field, Excel excel) + { + String key = StringUtils.format("data_{}_{}_{}_{}_{}", excel.align(), excel.color(), excel.backgroundColor(), excel.cellType(), excel.wrapText()); + if (!styles.containsKey(key)) + { + CellStyle style = wb.createCellStyle(); + style.setAlignment(excel.align()); + style.setVerticalAlignment(VerticalAlignment.CENTER); + style.setBorderRight(BorderStyle.THIN); + style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderLeft(BorderStyle.THIN); + style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderTop(BorderStyle.THIN); + style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setBorderBottom(BorderStyle.THIN); + style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); + style.setFillPattern(FillPatternType.SOLID_FOREGROUND); + style.setFillForegroundColor(excel.backgroundColor().getIndex()); + style.setWrapText(excel.wrapText()); + Font dataFont = wb.createFont(); + dataFont.setFontName("Arial"); + dataFont.setFontHeightInPoints((short) 10); + dataFont.setColor(excel.color().index); + style.setFont(dataFont); + if (ColumnType.TEXT == excel.cellType()) + { + DataFormat dataFormat = wb.createDataFormat(); + style.setDataFormat(dataFormat.getFormat("@")); + } + styles.put(key, style); + } + } + + /** + * 创建单元格 + */ + public Cell createHeadCell(Excel attr, Row row, int column) + { + // 创建列 + Cell cell = row.createCell(column); + // 写入列信息 + cell.setCellValue(attr.name()); + // 国际化 + //cell.setCellValue(MessageUtils.parseMessage(attr.name())); + setDataValidation(attr, row, column); + cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); + if (isSubList()) + { + // 填充默认样式,防止合并单元格样式失效 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + if (attr.needMerge()) + { + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column)); + } + } + return cell; + } + + /** + * 设置单元格信息 + * + * @param value 单元格值 + * @param attr 注解相关 + * @param cell 单元格信息 + */ + public void setCellVo(Object value, Excel attr, Cell cell) + { + if (ColumnType.STRING == attr.cellType() || ColumnType.TEXT == attr.cellType()) + { + String cellValue = Convert.toStr(value); + // 对于任何以表达式触发字符 =-+@开头的单元格,直接使用tab字符作为前缀,防止CSV注入。 + if (StringUtils.startsWithAny(cellValue, FORMULA_STR)) + { + cellValue = RegExUtils.replaceFirst(cellValue, FORMULA_REGEX_STR, "\t$0"); + } + if (value instanceof Collection && StringUtils.equals("[]", cellValue)) + { + cellValue = StringUtils.EMPTY; + } + cell.setCellValue(StringUtils.isNull(cellValue) ? attr.defaultValue() : cellValue + attr.suffix()); + } + else if (ColumnType.NUMERIC == attr.cellType()) + { + if (StringUtils.isNotNull(value)) + { + cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value) : Convert.toInt(value)); + } + } + else if (ColumnType.IMAGE == attr.cellType()) + { + ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), cell.getRow().getRowNum() + 1); + String propertyValue = Convert.toStr(value); + if (StringUtils.isNotEmpty(propertyValue)) + { + List imagePaths = StringUtils.str2List(propertyValue, SEPARATOR); + for (String imagePath : imagePaths) + { + byte[] data = ImageUtils.getImage(imagePath); + getDrawingPatriarch(cell.getSheet()).createPicture(anchor, cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + } + } + } + + /** + * 获取画布 + */ + public static Drawing getDrawingPatriarch(Sheet sheet) + { + if (sheet.getDrawingPatriarch() == null) + { + sheet.createDrawingPatriarch(); + } + return sheet.getDrawingPatriarch(); + } + + /** + * 获取图片类型,设置图片插入类型 + */ + public int getImageType(byte[] value) + { + String type = FileTypeUtils.getFileExtendName(value); + if ("JPG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_JPEG; + } + else if ("PNG".equalsIgnoreCase(type)) + { + return Workbook.PICTURE_TYPE_PNG; + } + return Workbook.PICTURE_TYPE_JPEG; + } + + /** + * 创建表格样式 + */ + public void setDataValidation(Excel attr, Row row, int column) + { + if (attr.name().indexOf("注:") >= 0) + { + sheet.setColumnWidth(column, 6000); + } + else + { + // 设置列宽 + sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256)); + } + if (StringUtils.isNotEmpty(attr.prompt()) || attr.combo().length > 0 || attr.comboReadDict()) + { + String[] comboArray = attr.combo(); + if (attr.comboReadDict()) + { + if (!sysDictMap.containsKey("combo_" + attr.dictType())) + { + String labels = DictUtils.getDictLabels(attr.dictType()); + sysDictMap.put("combo_" + attr.dictType(), labels); + } + String val = sysDictMap.get("combo_" + attr.dictType()); + comboArray = StringUtils.split(val, DictUtils.SEPARATOR); + } + if (comboArray.length > 15 || StringUtils.join(comboArray).length() > 255) + { + // 如果下拉数大于15或字符串长度大于255,则使用一个新sheet存储,避免生成的模板下拉值获取不到 + setXSSFValidationWithHidden(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + else + { + // 提示信息或只能选择不能输入的列内容. + setPromptOrValidation(sheet, comboArray, attr.prompt(), 1, 100, column, column); + } + } + } + + /** + * 添加单元格 + */ + public Cell addCell(Excel attr, Row row, T vo, Field field, int column) + { + Cell cell = null; + try + { + // 设置行高 + row.setHeight(maxHeight); + // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列. + if (attr.isExport()) + { + // 创建cell + cell = row.createCell(column); + if (isSubListValue(vo) && getListCellValue(vo).size() > 1 && attr.needMerge()) + { + if (subMergedLastRowNum >= subMergedFirstRowNum) + { + sheet.addMergedRegion(new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column)); + } + } + cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor(), attr.cellType(), attr.wrapText()))); + + // 用于读取对象中的属性 + Object value = getTargetValue(vo, field, attr); + String dateFormat = attr.dateFormat(); + String readConverterExp = attr.readConverterExp(); + String separator = attr.separator(); + String dictType = attr.dictType(); + if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) + { + cell.getCellStyle().setDataFormat(this.wb.getCreationHelper().createDataFormat().getFormat(dateFormat)); + cell.setCellValue(parseDateToStr(dateFormat, value)); + } + else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) + { + cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator)); + } + else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) + { + if (!sysDictMap.containsKey(dictType + value)) + { + String lable = convertDictByExp(Convert.toStr(value), dictType, separator); + sysDictMap.put(dictType + value, lable); + } + cell.setCellValue(sysDictMap.get(dictType + value)); + } + else if (value instanceof BigDecimal && -1 != attr.scale()) + { + cell.setCellValue((((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).doubleValue()); + } + else if (!attr.handler().equals(ExcelHandlerAdapter.class)) + { + cell.setCellValue(dataFormatHandlerAdapter(value, attr, cell)); + } + else + { + // 设置列类型 + setCellVo(value, attr, cell); + } + addStatisticsData(column, Convert.toStr(value), attr); + } + } + catch (Exception e) + { + log.error("导出Excel失败{}", e); + } + return cell; + } + + /** + * 设置 POI XSSFSheet 单元格提示或选择框 + * + * @param sheet 表单 + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setPromptOrValidation(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, + int firstCol, int endCol) + { + DataValidationHelper helper = sheet.getDataValidationHelper(); + DataValidationConstraint constraint = textlist.length > 0 ? helper.createExplicitListConstraint(textlist) : helper.createCustomConstraint("DD1"); + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + sheet.addValidationData(dataValidation); + } + + /** + * 设置某些列的值只能输入预制的数据,显示下拉框(兼容超出一定数量的下拉框). + * + * @param sheet 要设置的sheet. + * @param textlist 下拉框显示的内容 + * @param promptContent 提示内容 + * @param firstRow 开始行 + * @param endRow 结束行 + * @param firstCol 开始列 + * @param endCol 结束列 + */ + public void setXSSFValidationWithHidden(Sheet sheet, String[] textlist, String promptContent, int firstRow, int endRow, int firstCol, int endCol) + { + String hideSheetName = "combo_" + firstCol + "_" + endCol; + Sheet hideSheet = wb.createSheet(hideSheetName); // 用于存储 下拉菜单数据 + for (int i = 0; i < textlist.length; i++) + { + hideSheet.createRow(i).createCell(0).setCellValue(textlist[i]); + } + // 创建名称,可被其他单元格引用 + Name name = wb.createName(); + name.setNameName(hideSheetName + "_data"); + name.setRefersToFormula(hideSheetName + "!$A$1:$A$" + textlist.length); + DataValidationHelper helper = sheet.getDataValidationHelper(); + // 加载下拉列表内容 + DataValidationConstraint constraint = helper.createFormulaListConstraint(hideSheetName + "_data"); + // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 + CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol); + // 数据有效性对象 + DataValidation dataValidation = helper.createValidation(constraint, regions); + if (StringUtils.isNotEmpty(promptContent)) + { + // 如果设置了提示信息则鼠标放上去提示 + dataValidation.createPromptBox("", promptContent); + dataValidation.setShowPromptBox(true); + } + // 处理Excel兼容性问题 + if (dataValidation instanceof XSSFDataValidation) + { + dataValidation.setSuppressDropDownArrow(true); + dataValidation.setShowErrorBox(true); + } + else + { + dataValidation.setSuppressDropDownArrow(false); + } + + sheet.addValidationData(dataValidation); + // 设置hiddenSheet隐藏 + wb.setSheetHidden(wb.getSheetIndex(hideSheet), true); + } + + /** + * 解析导出值 0=男,1=女,2=未知 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String convertByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[0].equals(value)) + { + propertyString.append(itemArray[1] + separator); + break; + } + } + } + else + { + if (itemArray[0].equals(propertyValue)) + { + return itemArray[1]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2 + * + * @param propertyValue 参数值 + * @param converterExp 翻译注解 + * @param separator 分隔符 + * @return 解析后值 + */ + public static String reverseByExp(String propertyValue, String converterExp, String separator) + { + StringBuilder propertyString = new StringBuilder(); + String[] convertSource = converterExp.split(SEPARATOR); + for (String item : convertSource) + { + String[] itemArray = item.split("="); + if (StringUtils.containsAny(propertyValue, separator)) + { + for (String value : propertyValue.split(separator)) + { + if (itemArray[1].equals(value)) + { + propertyString.append(itemArray[0] + separator); + break; + } + } + } + else + { + if (itemArray[1].equals(propertyValue)) + { + return itemArray[0]; + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 解析字典值 + * + * @param dictValue 字典值 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典标签 + */ + public static String convertDictByExp(String dictValue, String dictType, String separator) + { + return DictUtils.getDictLabel(dictType, dictValue, separator); + } + + /** + * 反向解析值字典值 + * + * @param dictLabel 字典标签 + * @param dictType 字典类型 + * @param separator 分隔符 + * @return 字典值 + */ + public static String reverseDictByExp(String dictLabel, String dictType, String separator) + { + return DictUtils.getDictValue(dictType, dictLabel, separator); + } + + /** + * 数据处理器 + * + * @param value 数据值 + * @param excel 数据注解 + * @return + */ + public String dataFormatHandlerAdapter(Object value, Excel excel, Cell cell) + { + try + { + Object instance = excel.handler().newInstance(); + Method formatMethod = excel.handler().getMethod("format", new Class[] { Object.class, String[].class, Cell.class, Workbook.class }); + value = formatMethod.invoke(instance, value, excel.args(), cell, this.wb); + } + catch (Exception e) + { + log.error("不能格式化数据 " + excel.handler(), e.getMessage()); + } + return Convert.toStr(value); + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, Excel entity) + { + if (entity != null && entity.isStatistics()) + { + Double temp = 0D; + if (!statistics.containsKey(index)) + { + statistics.put(index, temp); + } + try + { + temp = Double.valueOf(text); + } + catch (NumberFormatException e) + { + } + statistics.put(index, statistics.get(index) + temp); + } + } + + /** + * 创建统计行 + */ + public void addStatisticsRow() + { + if (statistics.size() > 0) + { + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + Cell cell = row.createCell(0); + cell.setCellStyle(styles.get("total")); + cell.setCellValue("合计"); + + for (Integer key : keys) + { + cell = row.createCell(key); + cell.setCellStyle(styles.get("total")); + cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key))); + } + statistics.clear(); + } + } + + /** + * 编码文件名 + */ + public String encodingFilename(String filename) + { + return UUID.randomUUID() + "_" + filename + ".xlsx"; + } + + /** + * 获取下载路径 + * + * @param filename 文件名称 + */ + public String getAbsoluteFile(String filename) + { + String downloadPath = RuoYiConfig.getDownloadPath() + filename; + File desc = new File(downloadPath); + if (!desc.getParentFile().exists()) + { + desc.getParentFile().mkdirs(); + } + return downloadPath; + } + + /** + * 获取bean中的属性值 + * + * @param vo 实体对象 + * @param field 字段 + * @param excel 注解 + * @return 最终的属性值 + * @throws Exception + */ + private Object getTargetValue(T vo, Field field, Excel excel) throws Exception + { + field.setAccessible(true); + Object o = field.get(vo); + if (StringUtils.isNotEmpty(excel.targetAttr())) + { + String target = excel.targetAttr(); + if (target.contains(".")) + { + String[] targets = target.split("[.]"); + for (String name : targets) + { + o = getValue(o, name); + } + } + else + { + o = getValue(o, target); + } + } + return o; + } + + /** + * 以类的属性的get方法方法形式获取值 + * + * @param o + * @param name + * @return value + * @throws Exception + */ + private Object getValue(Object o, String name) throws Exception + { + if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) + { + Class clazz = o.getClass(); + Field field = clazz.getDeclaredField(name); + field.setAccessible(true); + o = field.get(o); + } + return o; + } + + /** + * 得到所有定义字段 + */ + private void createExcelField() + { + this.fields = getFields(); + this.fields = this.fields.stream().sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort())).collect(Collectors.toList()); + this.maxHeight = getRowHeight(); + } + + /** + * 获取字段注解信息 + */ + public List getFields() + { + List fields = new ArrayList(); + List tempFields = new ArrayList<>(); + tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields())); + tempFields.addAll(Arrays.asList(clazz.getDeclaredFields())); + if (StringUtils.isNotEmpty(includeFields)) + { + for (Field field : tempFields) + { + if (ArrayUtils.contains(this.includeFields, field.getName()) || field.isAnnotationPresent(Excels.class)) + { + addField(fields, field); + } + } + } + else if (StringUtils.isNotEmpty(excludeFields)) + { + for (Field field : tempFields) + { + if (!ArrayUtils.contains(this.excludeFields, field.getName())) + { + addField(fields, field); + } + } + } + else + { + for (Field field : tempFields) + { + addField(fields, field); + } + } + return fields; + } + + /** + * 添加字段信息 + */ + public void addField(List fields, Field field) + { + // 单注解 + if (field.isAnnotationPresent(Excel.class)) + { + Excel attr = field.getAnnotation(Excel.class); + if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) + { + fields.add(new Object[] { field, attr }); + } + if (Collection.class.isAssignableFrom(field.getType())) + { + subMethod = getSubMethod(field.getName(), clazz); + ParameterizedType pt = (ParameterizedType) field.getGenericType(); + Class subClass = (Class) pt.getActualTypeArguments()[0]; + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class); + } + } + + // 多注解 + if (field.isAnnotationPresent(Excels.class)) + { + Excels attrs = field.getAnnotation(Excels.class); + Excel[] excels = attrs.value(); + for (Excel attr : excels) + { + if (StringUtils.isNotEmpty(includeFields)) + { + if (ArrayUtils.contains(this.includeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + else + { + if (!ArrayUtils.contains(this.excludeFields, field.getName() + "." + attr.targetAttr()) + && (attr != null && (attr.type() == Type.ALL || attr.type() == type))) + { + fields.add(new Object[] { field, attr }); + } + } + } + } + } + + /** + * 根据注解获取最大行高 + */ + public short getRowHeight() + { + double maxHeight = 0; + for (Object[] os : this.fields) + { + Excel excel = (Excel) os[1]; + maxHeight = Math.max(maxHeight, excel.height()); + } + return (short) (maxHeight * 20); + } + + /** + * 创建一个工作簿 + */ + public void createWorkbook() + { + this.wb = new SXSSFWorkbook(500); + this.sheet = wb.createSheet(); + wb.setSheetName(0, sheetName); + this.styles = createStyles(wb); + } + + /** + * 创建工作表 + * + * @param sheetNo sheet数量 + * @param index 序号 + */ + public void createSheet(int sheetNo, int index) + { + // 设置工作表的名称. + if (sheetNo > 1 && index > 0) + { + this.sheet = wb.createSheet(); + this.createTitle(); + wb.setSheetName(index, sheetName + index); + } + } + + /** + * 获取单元格值 + * + * @param row 获取的行 + * @param column 获取单元格列号 + * @return 单元格值 + */ + public Object getCellValue(Row row, int column) + { + if (row == null) + { + return row; + } + Object val = ""; + try + { + Cell cell = row.getCell(column); + if (StringUtils.isNotNull(cell)) + { + if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) + { + val = cell.getNumericCellValue(); + if (DateUtil.isCellDateFormatted(cell)) + { + val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换 + } + else + { + if ((Double) val % 1 != 0) + { + val = new BigDecimal(val.toString()); + } + else + { + val = new DecimalFormat("0").format(val); + } + } + } + else if (cell.getCellType() == CellType.STRING) + { + val = cell.getStringCellValue(); + } + else if (cell.getCellType() == CellType.BOOLEAN) + { + val = cell.getBooleanCellValue(); + } + else if (cell.getCellType() == CellType.ERROR) + { + val = cell.getErrorCellValue(); + } + + } + } + catch (Exception e) + { + return val; + } + return val; + } + + /** + * 判断是否是空行 + * + * @param row 判断的行 + * @return + */ + private boolean isRowEmpty(Row row) + { + if (row == null) + { + return true; + } + for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) + { + Cell cell = row.getCell(i); + if (cell != null && cell.getCellType() != CellType.BLANK) + { + return false; + } + } + return true; + } + + /** + * 获取Excel2003图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures03(HSSFSheet sheet, HSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + List pictures = workbook.getAllPictures(); + if (!pictures.isEmpty() && sheet.getDrawingPatriarch() != null) + { + for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) + { + if (shape instanceof HSSFPicture) + { + HSSFPicture pic = (HSSFPicture) shape; + HSSFClientAnchor anchor = (HSSFClientAnchor) pic.getAnchor(); + String picIndex = anchor.getRow1() + "_" + anchor.getCol1(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + return sheetIndexPicMap; + } + + /** + * 获取Excel2007图片 + * + * @param sheet 当前sheet对象 + * @param workbook 工作簿对象 + * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData + */ + public static Map> getSheetPictures07(XSSFSheet sheet, XSSFWorkbook workbook) + { + Map> sheetIndexPicMap = new HashMap<>(); + for (POIXMLDocumentPart dr : sheet.getRelations()) + { + if (dr instanceof XSSFDrawing) + { + XSSFDrawing drawing = (XSSFDrawing) dr; + for (XSSFShape shape : drawing.getShapes()) + { + if (shape instanceof XSSFPicture) + { + XSSFPicture pic = (XSSFPicture) shape; + XSSFClientAnchor anchor = pic.getPreferredSize(); + CTMarker ctMarker = anchor.getFrom(); + String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol(); + sheetIndexPicMap.computeIfAbsent(picIndex, k -> new ArrayList<>()).add(pic.getPictureData()); + } + } + } + } + return sheetIndexPicMap; + } + + /** + * 格式化不同类型的日期对象 + * + * @param dateFormat 日期格式 + * @param val 被格式化的日期对象 + * @return 格式化后的日期字符 + */ + public String parseDateToStr(String dateFormat, Object val) + { + if (val == null) + { + return ""; + } + String str; + if (val instanceof Date) + { + str = DateUtils.parseDateToStr(dateFormat, (Date) val); + } + else if (val instanceof LocalDateTime) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDateTime) val)); + } + else if (val instanceof LocalDate) + { + str = DateUtils.parseDateToStr(dateFormat, DateUtils.toDate((LocalDate) val)); + } + else + { + str = val.toString(); + } + return str; + } + + /** + * 是否有对象的子列表 + */ + public boolean isSubList() + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0; + } + + /** + * 是否有对象的子列表,集合不为空 + */ + public boolean isSubListValue(T vo) + { + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0; + } + + /** + * 获取集合的值 + */ + public Collection getListCellValue(Object obj) + { + Object value; + try + { + value = subMethod.invoke(obj, new Object[] {}); + } + catch (Exception e) + { + return new ArrayList(); + } + return (Collection) value; + } + + /** + * 获取对象的子列表方法 + * + * @param name 名称 + * @param pojoClass 类对象 + * @return 子列表方法 + */ + public Method getSubMethod(String name, Class pojoClass) + { + StringBuffer getMethodName = new StringBuffer("get"); + getMethodName.append(name.substring(0, 1).toUpperCase()); + getMethodName.append(name.substring(1)); + Method method = null; + try + { + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {}); + } + catch (Exception e) + { + log.error("获取对象异常{}", e.getMessage()); + } + return method; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java new file mode 100644 index 0000000..b19953e --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/reflect/ReflectUtils.java @@ -0,0 +1,410 @@ +package com.ruoyi.common.utils.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Date; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; +import org.apache.poi.ss.usermodel.DateUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; + +/** + * 反射工具类. 提供调用getter/setter方法, 访问私有变量, 调用私有方法, 获取泛型类型Class, 被AOP过的真实类等工具函数. + * + * @author ruoyi + */ +@SuppressWarnings("rawtypes") +public class ReflectUtils +{ + private static final String SETTER_PREFIX = "set"; + + private static final String GETTER_PREFIX = "get"; + + private static final String CGLIB_CLASS_SEPARATOR = "$$"; + + private static Logger logger = LoggerFactory.getLogger(ReflectUtils.class); + + /** + * 调用Getter方法. + * 支持多级,如:对象名.对象名.方法 + */ + @SuppressWarnings("unchecked") + public static E invokeGetter(Object obj, String propertyName) + { + Object object = obj; + for (String name : StringUtils.split(propertyName, ".")) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + return (E) object; + } + + /** + * 调用Setter方法, 仅匹配方法名。 + * 支持多级,如:对象名.对象名.方法 + */ + public static void invokeSetter(Object obj, String propertyName, E value) + { + Object object = obj; + String[] names = StringUtils.split(propertyName, "."); + for (int i = 0; i < names.length; i++) + { + if (i < names.length - 1) + { + String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]); + object = invokeMethod(object, getterMethodName, new Class[] {}, new Object[] {}); + } + else + { + String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]); + invokeMethodByName(object, setterMethodName, new Object[] { value }); + } + } + } + + /** + * 直接读取对象属性值, 无视private/protected修饰符, 不经过getter函数. + */ + @SuppressWarnings("unchecked") + public static E getFieldValue(final Object obj, final String fieldName) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return null; + } + E result = null; + try + { + result = (E) field.get(obj); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常{}", e.getMessage()); + } + return result; + } + + /** + * 直接设置对象属性值, 无视private/protected修饰符, 不经过setter函数. + */ + public static void setFieldValue(final Object obj, final String fieldName, final E value) + { + Field field = getAccessibleField(obj, fieldName); + if (field == null) + { + // throw new IllegalArgumentException("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + fieldName + "] 字段 "); + return; + } + try + { + field.set(obj, value); + } + catch (IllegalAccessException e) + { + logger.error("不可能抛出的异常: {}", e.getMessage()); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符. + * 用于一次性调用的情况,否则应使用getAccessibleMethod()函数获得Method后反复调用. + * 同时匹配方法名+参数类型, + */ + @SuppressWarnings("unchecked") + public static E invokeMethod(final Object obj, final String methodName, final Class[] parameterTypes, + final Object[] args) + { + if (obj == null || methodName == null) + { + return null; + } + Method method = getAccessibleMethod(obj, methodName, parameterTypes); + if (method == null) + { + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 直接调用对象方法, 无视private/protected修饰符, + * 用于一次性调用的情况,否则应使用getAccessibleMethodByName()函数获得Method后反复调用. + * 只匹配函数名,如果有多个同名函数调用第一个。 + */ + @SuppressWarnings("unchecked") + public static E invokeMethodByName(final Object obj, final String methodName, final Object[] args) + { + Method method = getAccessibleMethodByName(obj, methodName, args.length); + if (method == null) + { + // 如果为空不报错,直接返回空。 + logger.debug("在 [" + obj.getClass() + "] 中,没有找到 [" + methodName + "] 方法 "); + return null; + } + try + { + // 类型转换(将参数数据类型转换为目标方法参数类型) + Class[] cs = method.getParameterTypes(); + for (int i = 0; i < cs.length; i++) + { + if (args[i] != null && !args[i].getClass().equals(cs[i])) + { + if (cs[i] == String.class) + { + args[i] = Convert.toStr(args[i]); + if (StringUtils.endsWith((String) args[i], ".0")) + { + args[i] = StringUtils.substringBefore((String) args[i], ".0"); + } + } + else if (cs[i] == Integer.class) + { + args[i] = Convert.toInt(args[i]); + } + else if (cs[i] == Long.class) + { + args[i] = Convert.toLong(args[i]); + } + else if (cs[i] == Double.class) + { + args[i] = Convert.toDouble(args[i]); + } + else if (cs[i] == Float.class) + { + args[i] = Convert.toFloat(args[i]); + } + else if (cs[i] == Date.class) + { + if (args[i] instanceof String) + { + args[i] = DateUtils.parseDate(args[i]); + } + else + { + args[i] = DateUtil.getJavaDate((Double) args[i]); + } + } + else if (cs[i] == boolean.class || cs[i] == Boolean.class) + { + args[i] = Convert.toBool(args[i]); + } + } + } + return (E) method.invoke(obj, args); + } + catch (Exception e) + { + String msg = "method: " + method + ", obj: " + obj + ", args: " + args + ""; + throw convertReflectionExceptionToUnchecked(msg, e); + } + } + + /** + * 循环向上转型, 获取对象的DeclaredField, 并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + */ + public static Field getAccessibleField(final Object obj, final String fieldName) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(fieldName, "fieldName can't be blank"); + for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) + { + try + { + Field field = superClass.getDeclaredField(fieldName); + makeAccessible(field); + return field; + } + catch (NoSuchFieldException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 匹配函数名+参数类型。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethod(final Object obj, final String methodName, + final Class... parameterTypes) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + try + { + Method method = searchType.getDeclaredMethod(methodName, parameterTypes); + makeAccessible(method); + return method; + } + catch (NoSuchMethodException e) + { + continue; + } + } + return null; + } + + /** + * 循环向上转型, 获取对象的DeclaredMethod,并强制设置为可访问. + * 如向上转型到Object仍无法找到, 返回null. + * 只匹配函数名。 + * 用于方法需要被多次调用的情况. 先使用本函数先取得Method,然后调用Method.invoke(Object obj, Object... args) + */ + public static Method getAccessibleMethodByName(final Object obj, final String methodName, int argsNum) + { + // 为空不报错。直接返回 null + if (obj == null) + { + return null; + } + Validate.notBlank(methodName, "methodName can't be blank"); + for (Class searchType = obj.getClass(); searchType != Object.class; searchType = searchType.getSuperclass()) + { + Method[] methods = searchType.getDeclaredMethods(); + for (Method method : methods) + { + if (method.getName().equals(methodName) && method.getParameterTypes().length == argsNum) + { + makeAccessible(method); + return method; + } + } + } + return null; + } + + /** + * 改变private/protected的方法为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Method method) + { + if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) + && !method.isAccessible()) + { + method.setAccessible(true); + } + } + + /** + * 改变private/protected的成员变量为public,尽量不调用实际改动的语句,避免JDK的SecurityManager抱怨。 + */ + public static void makeAccessible(Field field) + { + if ((!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers()) + || Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) + { + field.setAccessible(true); + } + } + + /** + * 通过反射, 获得Class定义中声明的泛型参数的类型, 注意泛型必须定义在父类处 + * 如无法找到, 返回Object.class. + */ + @SuppressWarnings("unchecked") + public static Class getClassGenricType(final Class clazz) + { + return getClassGenricType(clazz, 0); + } + + /** + * 通过反射, 获得Class定义中声明的父类的泛型参数的类型. + * 如无法找到, 返回Object.class. + */ + public static Class getClassGenricType(final Class clazz, final int index) + { + Type genType = clazz.getGenericSuperclass(); + + if (!(genType instanceof ParameterizedType)) + { + logger.debug(clazz.getSimpleName() + "'s superclass not ParameterizedType"); + return Object.class; + } + + Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); + + if (index >= params.length || index < 0) + { + logger.debug("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: " + + params.length); + return Object.class; + } + if (!(params[index] instanceof Class)) + { + logger.debug(clazz.getSimpleName() + " not set the actual class on superclass generic parameter"); + return Object.class; + } + + return (Class) params[index]; + } + + public static Class getUserClass(Object instance) + { + if (instance == null) + { + throw new RuntimeException("Instance must not be null"); + } + Class clazz = instance.getClass(); + if (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR)) + { + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) + { + return superClass; + } + } + return clazz; + + } + + /** + * 将反射时的checked exception转换为unchecked exception. + */ + public static RuntimeException convertReflectionExceptionToUnchecked(String msg, Exception e) + { + if (e instanceof IllegalAccessException || e instanceof IllegalArgumentException + || e instanceof NoSuchMethodException) + { + return new IllegalArgumentException(msg, e); + } + else if (e instanceof InvocationTargetException) + { + return new RuntimeException(msg, ((InvocationTargetException) e).getTargetException()); + } + return new RuntimeException(msg, e); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java new file mode 100644 index 0000000..ca1cd92 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Base64.java @@ -0,0 +1,291 @@ +package com.ruoyi.common.utils.sign; + +/** + * Base64工具类 + * + * @author ruoyi + */ +public final class Base64 +{ + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 64; + static private final int TWENTYFOURBITGROUP = 24; + static private final int EIGHTBIT = 8; + static private final int SIXTEENBIT = 16; + static private final int FOURBYTE = 4; + static private final int SIGN = -128; + static private final char PAD = '='; + static final private byte[] base64Alphabet = new byte[BASELENGTH]; + static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH]; + + static + { + for (int i = 0; i < BASELENGTH; ++i) + { + base64Alphabet[i] = -1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte) (i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte) (i - 'a' + 26); + } + + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte) (i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (char) ('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (char) ('0' + j); + } + lookUpBase64Alphabet[62] = (char) '+'; + lookUpBase64Alphabet[63] = (char) '/'; + } + + private static boolean isWhiteSpace(char octect) + { + return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9); + } + + private static boolean isPad(char octect) + { + return (octect == PAD); + } + + private static boolean isData(char octect) + { + return (octect < BASELENGTH && base64Alphabet[octect] != -1); + } + + /** + * Encodes hex octects into Base64 + * + * @param binaryData Array containing binaryData + * @return Encoded Base64 array + */ + public static String encode(byte[] binaryData) + { + if (binaryData == null) + { + return null; + } + + int lengthDataBits = binaryData.length * EIGHTBIT; + if (lengthDataBits == 0) + { + return ""; + } + + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets; + char encodedData[] = null; + + encodedData = new char[numberQuartet * 4]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + + for (int i = 0; i < numberTriplets; i++) + { + b1 = binaryData[dataIndex++]; + b2 = binaryData[dataIndex++]; + b3 = binaryData[dataIndex++]; + + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f]; + } + + // form integral number of 6-bit groups + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte) (b1 & 0x03); + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex++] = PAD; + encodedData[encodedIndex++] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte) (b2 & 0x0f); + k = (byte) (b1 & 0x03); + + byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0); + byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex++] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex++] = PAD; + } + return new String(encodedData); + } + + /** + * Decodes Base64 data into octects + * + * @param encoded string containing Base64 data + * @return Array containind decoded data. + */ + public static byte[] decode(String encoded) + { + if (encoded == null) + { + return null; + } + + char[] base64Data = encoded.toCharArray(); + // remove white spaces + int len = removeWhiteSpace(base64Data); + + if (len % FOURBYTE != 0) + { + return null;// should be divisible by four + } + + int numberQuadruple = (len / FOURBYTE); + + if (numberQuadruple == 0) + { + return new byte[0]; + } + + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0; + char d1 = 0, d2 = 0, d3 = 0, d4 = 0; + + int i = 0; + int encodedIndex = 0; + int dataIndex = 0; + decodedData = new byte[(numberQuadruple) * 3]; + + for (; i < numberQuadruple - 1; i++) + { + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++])) + || !isData((d3 = base64Data[dataIndex++])) || !isData((d4 = base64Data[dataIndex++]))) + { + return null; + } // if found "no data" just return null + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + } + + if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) + { + return null;// if found "no data" just return null + } + + b1 = base64Alphabet[d1]; + b2 = base64Alphabet[d2]; + + d3 = base64Data[dataIndex++]; + d4 = base64Data[dataIndex++]; + if (!isData((d3)) || !isData((d4))) + {// Check if they are PAD characters + if (isPad(d3) && isPad(d4)) + { + if ((b2 & 0xf) != 0)// last 4 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 1]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4); + return tmp; + } + else if (!isPad(d3) && isPad(d4)) + { + b3 = base64Alphabet[d3]; + if ((b3 & 0x3) != 0)// last 2 bits should be zero + { + return null; + } + byte[] tmp = new byte[i * 3 + 2]; + System.arraycopy(decodedData, 0, tmp, 0, i * 3); + tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + return tmp; + } + else + { + return null; + } + } + else + { // No PAD e.g 3cQl + b3 = base64Alphabet[d3]; + b4 = base64Alphabet[d4]; + decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4); + decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex++] = (byte) (b3 << 6 | b4); + + } + return decodedData; + } + + /** + * remove WhiteSpace from MIME containing encoded Base64 data. + * + * @param data the byte array of base64 data (with WS) + * @return the new length + */ + private static int removeWhiteSpace(char[] data) + { + if (data == null) + { + return 0; + } + + // count characters that's not whitespace + int newSize = 0; + int len = data.length; + for (int i = 0; i < len; i++) + { + if (!isWhiteSpace(data[i])) + { + data[newSize++] = data[i]; + } + } + return newSize; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java new file mode 100644 index 0000000..c1c58db --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sign/Md5Utils.java @@ -0,0 +1,67 @@ +package com.ruoyi.common.utils.sign; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Md5加密方法 + * + * @author ruoyi + */ +public class Md5Utils +{ + private static final Logger log = LoggerFactory.getLogger(Md5Utils.class); + + private static byte[] md5(String s) + { + MessageDigest algorithm; + try + { + algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(s.getBytes("UTF-8")); + byte[] messageDigest = algorithm.digest(); + return messageDigest; + } + catch (Exception e) + { + log.error("MD5 Error...", e); + } + return null; + } + + private static final String toHex(byte hash[]) + { + if (hash == null) + { + return null; + } + StringBuffer buf = new StringBuffer(hash.length * 2); + int i; + + for (i = 0; i < hash.length; i++) + { + if ((hash[i] & 0xff) < 0x10) + { + buf.append("0"); + } + buf.append(Long.toString(hash[i] & 0xff, 16)); + } + return buf.toString(); + } + + public static String hash(String s) + { + try + { + return new String(toHex(md5(s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8); + } + catch (Exception e) + { + log.error("not supported charset...{}", e); + return s; + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java new file mode 100644 index 0000000..4e3f603 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/spring/SpringUtils.java @@ -0,0 +1,164 @@ +package com.ruoyi.common.utils.spring; + +import org.springframework.aop.framework.Advised; +import org.springframework.aop.framework.AopContext; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * spring工具类 方便在非spring管理环境中获取bean + * + * @author ruoyi + */ +@Component +public final class SpringUtils implements BeanFactoryPostProcessor, ApplicationContextAware +{ + /** Spring应用上下文环境 */ + private static ConfigurableListableBeanFactory beanFactory; + + private static ApplicationContext applicationContext; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException + { + SpringUtils.beanFactory = beanFactory; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException + { + SpringUtils.applicationContext = applicationContext; + } + + /** + * 获取对象 + * + * @param name + * @return Object 一个以所给名字注册的bean的实例 + * @throws org.springframework.beans.BeansException + * + */ + @SuppressWarnings("unchecked") + public static T getBean(String name) throws BeansException + { + return (T) beanFactory.getBean(name); + } + + /** + * 获取类型为requiredType的对象 + * + * @param clz + * @return + * @throws org.springframework.beans.BeansException + * + */ + public static T getBean(Class clz) throws BeansException + { + T result = (T) beanFactory.getBean(clz); + return result; + } + + /** + * 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true + * + * @param name + * @return boolean + */ + public static boolean containsBean(String name) + { + return beanFactory.containsBean(name); + } + + /** + * 判断以给定名字注册的bean定义是一个singleton还是一个prototype。 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException) + * + * @param name + * @return boolean + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.isSingleton(name); + } + + /** + * @param name + * @return Class 注册对象的类型 + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static Class getType(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getType(name); + } + + /** + * 如果给定的bean名字在bean定义中有别名,则返回这些别名 + * + * @param name + * @return + * @throws org.springframework.beans.factory.NoSuchBeanDefinitionException + * + */ + public static String[] getAliases(String name) throws NoSuchBeanDefinitionException + { + return beanFactory.getAliases(name); + } + + /** + * 获取aop代理对象 + * + * @param invoker + * @return + */ + @SuppressWarnings("unchecked") + public static T getAopProxy(T invoker) + { + Object proxy = AopContext.currentProxy(); + if (((Advised) proxy).getTargetSource().getTargetClass() == invoker.getClass()) + { + return (T) proxy; + } + return invoker; + } + + /** + * 获取当前的环境配置,无配置返回null + * + * @return 当前的环境配置 + */ + public static String[] getActiveProfiles() + { + return applicationContext.getEnvironment().getActiveProfiles(); + } + + /** + * 获取当前的环境配置,当有多个环境配置时,只获取第一个 + * + * @return 当前的环境配置 + */ + public static String getActiveProfile() + { + final String[] activeProfiles = getActiveProfiles(); + return StringUtils.isNotEmpty(activeProfiles) ? activeProfiles[0] : null; + } + + /** + * 获取配置文件中的值 + * + * @param key 配置文件的key + * @return 当前的配置文件的值 + * + */ + public static String getRequiredProperty(String key) + { + return applicationContext.getEnvironment().getRequiredProperty(key); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java new file mode 100644 index 0000000..48720dc --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/sql/SqlUtil.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.utils.sql; + +import com.ruoyi.common.exception.UtilException; +import com.ruoyi.common.utils.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +public class SqlUtil +{ + /** + * 定义常用的 sql关键字 + */ + public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()"; + + /** + * 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序) + */ + public static String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+"; + + /** + * 限制orderBy最大长度 + */ + private static final int ORDER_BY_MAX_LENGTH = 500; + + /** + * 检查字符,防止注入绕过 + */ + public static String escapeOrderBySql(String value) + { + if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) + { + throw new UtilException("参数不符合规范,不能进行查询"); + } + if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) + { + throw new UtilException("参数已超过最大限制,不能进行查询"); + } + return value; + } + + /** + * 验证 order by 语法是否符合规范 + */ + public static boolean isValidOrderBySql(String value) + { + return value.matches(SQL_PATTERN); + } + + /** + * SQL关键字检查 + */ + public static void filterKeyword(String value) + { + if (StringUtils.isEmpty(value)) + { + return; + } + String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|"); + for (String sqlKeyword : sqlKeywords) + { + if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) + { + throw new UtilException("参数存在SQL注入风险"); + } + } + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java new file mode 100644 index 0000000..2c84427 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/IdUtils.java @@ -0,0 +1,49 @@ +package com.ruoyi.common.utils.uuid; + +/** + * ID生成器工具类 + * + * @author ruoyi + */ +public class IdUtils +{ + /** + * 获取随机UUID + * + * @return 随机UUID + */ + public static String randomUUID() + { + return UUID.randomUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线 + * + * @return 简化的UUID,去掉了横线 + */ + public static String simpleUUID() + { + return UUID.randomUUID().toString(true); + } + + /** + * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 随机UUID + */ + public static String fastUUID() + { + return UUID.fastUUID().toString(); + } + + /** + * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID + * + * @return 简化的UUID,去掉了横线 + */ + public static String fastSimpleUUID() + { + return UUID.fastUUID().toString(true); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java new file mode 100644 index 0000000..bf99611 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/Seq.java @@ -0,0 +1,86 @@ +package com.ruoyi.common.utils.uuid; + +import java.util.concurrent.atomic.AtomicInteger; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * @author ruoyi 序列生成类 + */ +public class Seq +{ + // 通用序列类型 + public static final String commSeqType = "COMMON"; + + // 上传序列类型 + public static final String uploadSeqType = "UPLOAD"; + + // 通用接口序列数 + private static AtomicInteger commSeq = new AtomicInteger(1); + + // 上传接口序列数 + private static AtomicInteger uploadSeq = new AtomicInteger(1); + + // 机器标识 + private static final String machineCode = "A"; + + /** + * 获取通用序列号 + * + * @return 序列值 + */ + public static String getId() + { + return getId(commSeqType); + } + + /** + * 默认16位序列号 yyMMddHHmmss + 一位机器标识 + 3长度循环递增字符串 + * + * @return 序列值 + */ + public static String getId(String type) + { + AtomicInteger atomicInt = commSeq; + if (uploadSeqType.equals(type)) + { + atomicInt = uploadSeq; + } + return getId(atomicInt, 3); + } + + /** + * 通用接口序列号 yyMMddHHmmss + 一位机器标识 + length长度循环递增字符串 + * + * @param atomicInt 序列数 + * @param length 数值长度 + * @return 序列值 + */ + public static String getId(AtomicInteger atomicInt, int length) + { + String result = DateUtils.dateTimeNow(); + result += machineCode; + result += getSeq(atomicInt, length); + return result; + } + + /** + * 序列循环递增字符串[1, 10 的 (length)幂次方), 用0左补齐length位数 + * + * @return 序列值 + */ + private synchronized static String getSeq(AtomicInteger atomicInt, int length) + { + // 先取值再+1 + int value = atomicInt.getAndIncrement(); + + // 如果更新后值>=10 的 (length)幂次方则重置为1 + int maxSeq = (int) Math.pow(10, length); + if (atomicInt.get() >= maxSeq) + { + atomicInt.set(1); + } + // 转字符串,用0左补齐 + return StringUtils.padl(value, length); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java new file mode 100644 index 0000000..a5585d6 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/uuid/UUID.java @@ -0,0 +1,484 @@ +package com.ruoyi.common.utils.uuid; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; +import com.ruoyi.common.exception.UtilException; + +/** + * 提供通用唯一识别码(universally unique identifier)(UUID)实现 + * + * @author ruoyi + */ +public final class UUID implements java.io.Serializable, Comparable +{ + private static final long serialVersionUID = -1185015143654744140L; + + /** + * SecureRandom 的单例 + * + */ + private static class Holder + { + static final SecureRandom numberGenerator = getSecureRandom(); + } + + /** 此UUID的最高64有效位 */ + private final long mostSigBits; + + /** 此UUID的最低64有效位 */ + private final long leastSigBits; + + /** + * 私有构造 + * + * @param data 数据 + */ + private UUID(byte[] data) + { + long msb = 0; + long lsb = 0; + assert data.length == 16 : "data must be 16 bytes in length"; + for (int i = 0; i < 8; i++) + { + msb = (msb << 8) | (data[i] & 0xff); + } + for (int i = 8; i < 16; i++) + { + lsb = (lsb << 8) | (data[i] & 0xff); + } + this.mostSigBits = msb; + this.leastSigBits = lsb; + } + + /** + * 使用指定的数据构造新的 UUID。 + * + * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位 + * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位 + */ + public UUID(long mostSigBits, long leastSigBits) + { + this.mostSigBits = mostSigBits; + this.leastSigBits = leastSigBits; + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID fastUUID() + { + return randomUUID(false); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID() + { + return randomUUID(true); + } + + /** + * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。 + * + * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能 + * @return 随机生成的 {@code UUID} + */ + public static UUID randomUUID(boolean isSecure) + { + final Random ng = isSecure ? Holder.numberGenerator : getRandom(); + + byte[] randomBytes = new byte[16]; + ng.nextBytes(randomBytes); + randomBytes[6] &= 0x0f; /* clear version */ + randomBytes[6] |= 0x40; /* set to version 4 */ + randomBytes[8] &= 0x3f; /* clear variant */ + randomBytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(randomBytes); + } + + /** + * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。 + * + * @param name 用于构造 UUID 的字节数组。 + * + * @return 根据指定数组生成的 {@code UUID} + */ + public static UUID nameUUIDFromBytes(byte[] name) + { + MessageDigest md; + try + { + md = MessageDigest.getInstance("MD5"); + } + catch (NoSuchAlgorithmException nsae) + { + throw new InternalError("MD5 not supported"); + } + byte[] md5Bytes = md.digest(name); + md5Bytes[6] &= 0x0f; /* clear version */ + md5Bytes[6] |= 0x30; /* set to version 3 */ + md5Bytes[8] &= 0x3f; /* clear variant */ + md5Bytes[8] |= 0x80; /* set to IETF variant */ + return new UUID(md5Bytes); + } + + /** + * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。 + * + * @param name 指定 {@code UUID} 字符串 + * @return 具有指定值的 {@code UUID} + * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常 + * + */ + public static UUID fromString(String name) + { + String[] components = name.split("-"); + if (components.length != 5) + { + throw new IllegalArgumentException("Invalid UUID string: " + name); + } + for (int i = 0; i < 5; i++) + { + components[i] = "0x" + components[i]; + } + + long mostSigBits = Long.decode(components[0]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[1]).longValue(); + mostSigBits <<= 16; + mostSigBits |= Long.decode(components[2]).longValue(); + + long leastSigBits = Long.decode(components[3]).longValue(); + leastSigBits <<= 48; + leastSigBits |= Long.decode(components[4]).longValue(); + + return new UUID(mostSigBits, leastSigBits); + } + + /** + * 返回此 UUID 的 128 位值中的最低有效 64 位。 + * + * @return 此 UUID 的 128 位值中的最低有效 64 位。 + */ + public long getLeastSignificantBits() + { + return leastSigBits; + } + + /** + * 返回此 UUID 的 128 位值中的最高有效 64 位。 + * + * @return 此 UUID 的 128 位值中最高有效 64 位。 + */ + public long getMostSignificantBits() + { + return mostSigBits; + } + + /** + * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。 + *

+ * 版本号具有以下含意: + *

    + *
  • 1 基于时间的 UUID + *
  • 2 DCE 安全 UUID + *
  • 3 基于名称的 UUID + *
  • 4 随机生成的 UUID + *
+ * + * @return 此 {@code UUID} 的版本号 + */ + public int version() + { + // Version is bits masked by 0x000000000000F000 in MS long + return (int) ((mostSigBits >> 12) & 0x0f); + } + + /** + * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。 + *

+ * 变体号具有以下含意: + *

    + *
  • 0 为 NCS 向后兼容保留 + *
  • 2 IETF RFC 4122(Leach-Salz), 用于此类 + *
  • 6 保留,微软向后兼容 + *
  • 7 保留供以后定义使用 + *
+ * + * @return 此 {@code UUID} 相关联的变体号 + */ + public int variant() + { + // This field is composed of a varying number of bits. + // 0 - - Reserved for NCS backward compatibility + // 1 0 - The IETF aka Leach-Salz variant (used by this class) + // 1 1 0 Reserved, Microsoft backward compatibility + // 1 1 1 Reserved for future definition. + return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63)); + } + + /** + * 与此 UUID 相关联的时间戳值。 + * + *

+ * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。
+ * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。 + * + *

+ * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。 + */ + public long timestamp() throws UnsupportedOperationException + { + checkTimeBase(); + return (mostSigBits & 0x0FFFL) << 48// + | ((mostSigBits >> 16) & 0x0FFFFL) << 32// + | mostSigBits >>> 32; + } + + /** + * 与此 UUID 相关联的时钟序列值。 + * + *

+ * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。 + *

+ * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出 + * UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的时钟序列 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public int clockSequence() throws UnsupportedOperationException + { + checkTimeBase(); + return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48); + } + + /** + * 与此 UUID 相关的节点值。 + * + *

+ * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。 + *

+ * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。
+ * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。 + * + * @return 此 {@code UUID} 的节点值 + * + * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1 + */ + public long node() throws UnsupportedOperationException + { + checkTimeBase(); + return leastSigBits & 0x0000FFFFFFFFFFFFL; + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @return 此{@code UUID} 的字符串表现形式 + * @see #toString(boolean) + */ + @Override + public String toString() + { + return toString(false); + } + + /** + * 返回此{@code UUID} 的字符串表现形式。 + * + *

+ * UUID 的字符串表示形式由此 BNF 描述: + * + *

+     * {@code
+     * UUID                   = ----
+     * time_low               = 4*
+     * time_mid               = 2*
+     * time_high_and_version  = 2*
+     * variant_and_sequence   = 2*
+     * node                   = 6*
+     * hexOctet               = 
+     * hexDigit               = [0-9a-fA-F]
+     * }
+     * 
+ * + * + * + * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串 + * @return 此{@code UUID} 的字符串表现形式 + */ + public String toString(boolean isSimple) + { + final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36); + // time_low + builder.append(digits(mostSigBits >> 32, 8)); + if (!isSimple) + { + builder.append('-'); + } + // time_mid + builder.append(digits(mostSigBits >> 16, 4)); + if (!isSimple) + { + builder.append('-'); + } + // time_high_and_version + builder.append(digits(mostSigBits, 4)); + if (!isSimple) + { + builder.append('-'); + } + // variant_and_sequence + builder.append(digits(leastSigBits >> 48, 4)); + if (!isSimple) + { + builder.append('-'); + } + // node + builder.append(digits(leastSigBits, 12)); + + return builder.toString(); + } + + /** + * 返回此 UUID 的哈希码。 + * + * @return UUID 的哈希码值。 + */ + @Override + public int hashCode() + { + long hilo = mostSigBits ^ leastSigBits; + return ((int) (hilo >> 32)) ^ (int) hilo; + } + + /** + * 将此对象与指定对象比较。 + *

+ * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。 + * + * @param obj 要与之比较的对象 + * + * @return 如果对象相同,则返回 {@code true};否则返回 {@code false} + */ + @Override + public boolean equals(Object obj) + { + if ((null == obj) || (obj.getClass() != UUID.class)) + { + return false; + } + UUID id = (UUID) obj; + return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits); + } + + // Comparison Operations + + /** + * 将此 UUID 与指定的 UUID 比较。 + * + *

+ * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。 + * + * @param val 与此 UUID 比较的 UUID + * + * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。 + * + */ + @Override + public int compareTo(UUID val) + { + // The ordering is intentionally set up so that the UUIDs + // can simply be numerically compared as two numbers + return (this.mostSigBits < val.mostSigBits ? -1 : // + (this.mostSigBits > val.mostSigBits ? 1 : // + (this.leastSigBits < val.leastSigBits ? -1 : // + (this.leastSigBits > val.leastSigBits ? 1 : // + 0)))); + } + + // ------------------------------------------------------------------------------------------------------------------- + // Private method start + /** + * 返回指定数字对应的hex值 + * + * @param val 值 + * @param digits 位 + * @return 值 + */ + private static String digits(long val, int digits) + { + long hi = 1L << (digits * 4); + return Long.toHexString(hi | (val & (hi - 1))).substring(1); + } + + /** + * 检查是否为time-based版本UUID + */ + private void checkTimeBase() + { + if (version() != 1) + { + throw new UnsupportedOperationException("Not a time-based UUID"); + } + } + + /** + * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG) + * + * @return {@link SecureRandom} + */ + public static SecureRandom getSecureRandom() + { + try + { + return SecureRandom.getInstance("SHA1PRNG"); + } + catch (NoSuchAlgorithmException e) + { + throw new UtilException(e); + } + } + + /** + * 获取随机数生成器对象
+ * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。 + * + * @return {@link ThreadLocalRandom} + */ + public static ThreadLocalRandom getRandom() + { + return ThreadLocalRandom.current(); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java new file mode 100644 index 0000000..7bfdf04 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/Xss.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.xss; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 自定义xss校验注解 + * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(value = { ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) +@Constraint(validatedBy = { XssValidator.class }) +public @interface Xss +{ + String message() + + default "不允许任何脚本运行"; + + Class[] groups() default {}; + + Class[] payload() default {}; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java new file mode 100644 index 0000000..42f425c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/xss/XssValidator.java @@ -0,0 +1,39 @@ +package com.ruoyi.common.xss; + +import com.ruoyi.common.utils.StringUtils; +import javax.validation.ConstraintValidator; +import javax.validation.ConstraintValidatorContext; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * 自定义xss校验注解实现 + * + * @author ruoyi + */ +public class XssValidator implements ConstraintValidator +{ + private static final String HTML_PATTERN = "<(\\S*?)[^>]*>.*?|<.*? />"; + + @Override + public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) + { + if (StringUtils.isBlank(value)) + { + return true; + } + return !containsHtml(value); + } + + public static boolean containsHtml(String value) + { + StringBuilder sHtml = new StringBuilder(); + Pattern pattern = Pattern.compile(HTML_PATTERN); + Matcher matcher = pattern.matcher(value); + while (matcher.find()) + { + sHtml.append(matcher.group()); + } + return pattern.matcher(sHtml).matches(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/pom.xml b/ruoyi-framework/pom.xml new file mode 100644 index 0000000..8ba868c --- /dev/null +++ b/ruoyi-framework/pom.xml @@ -0,0 +1,70 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + + ruoyi-framework + + + framework框架核心 + + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + com.alibaba + druid-spring-boot-starter + + + + + pro.fessional + kaptcha + + + servlet-api + javax.servlet + + + + + + + com.github.oshi + oshi-core + + + + + com.ruoyi + ruoyi-system + + + + + \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java new file mode 100644 index 0000000..b2337c9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataScopeAspect.java @@ -0,0 +1,184 @@ +package com.ruoyi.framework.aspectj; + +import java.util.ArrayList; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * 数据过滤处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class DataScopeAspect +{ + /** + * 全部数据权限 + */ + public static final String DATA_SCOPE_ALL = "1"; + + /** + * 自定数据权限 + */ + public static final String DATA_SCOPE_CUSTOM = "2"; + + /** + * 部门数据权限 + */ + public static final String DATA_SCOPE_DEPT = "3"; + + /** + * 部门及以下数据权限 + */ + public static final String DATA_SCOPE_DEPT_AND_CHILD = "4"; + + /** + * 仅本人数据权限 + */ + public static final String DATA_SCOPE_SELF = "5"; + + /** + * 数据权限过滤关键字 + */ + public static final String DATA_SCOPE = "dataScope"; + + @Before("@annotation(controllerDataScope)") + public void doBefore(JoinPoint point, DataScope controllerDataScope) throws Throwable + { + clearDataScope(point); + handleDataScope(point, controllerDataScope); + } + + protected void handleDataScope(final JoinPoint joinPoint, DataScope controllerDataScope) + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNotNull(loginUser)) + { + SysUser currentUser = loginUser.getUser(); + // 如果是超级管理员,则不过滤数据 + if (StringUtils.isNotNull(currentUser) && !currentUser.isAdmin()) + { + String permission = StringUtils.defaultIfEmpty(controllerDataScope.permission(), PermissionContextHolder.getContext()); + dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptAlias(), controllerDataScope.userAlias(), permission); + } + } + } + + /** + * 数据范围过滤 + * + * @param joinPoint 切点 + * @param user 用户 + * @param deptAlias 部门别名 + * @param userAlias 用户别名 + * @param permission 权限字符 + */ + public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptAlias, String userAlias, String permission) + { + StringBuilder sqlString = new StringBuilder(); + List conditions = new ArrayList(); + List scopeCustomIds = new ArrayList(); + user.getRoles().forEach(role -> { + if (DATA_SCOPE_CUSTOM.equals(role.getDataScope()) && StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + scopeCustomIds.add(Convert.toStr(role.getRoleId())); + } + }); + + for (SysRole role : user.getRoles()) + { + String dataScope = role.getDataScope(); + if (conditions.contains(dataScope) || StringUtils.equals(role.getStatus(), UserConstants.ROLE_DISABLE)) + { + continue; + } + if (!StringUtils.containsAny(role.getPermissions(), Convert.toStrArray(permission))) + { + continue; + } + if (DATA_SCOPE_ALL.equals(dataScope)) + { + sqlString = new StringBuilder(); + conditions.add(dataScope); + break; + } + else if (DATA_SCOPE_CUSTOM.equals(dataScope)) + { + if (scopeCustomIds.size() > 1) + { + // 多个自定数据权限使用in查询,避免多次拼接。 + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id in ({}) ) ", deptAlias, String.join(",", scopeCustomIds))); + } + else + { + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = {} ) ", deptAlias, role.getRoleId())); + } + } + else if (DATA_SCOPE_DEPT.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = {} ", deptAlias, user.getDeptId())); + } + else if (DATA_SCOPE_DEPT_AND_CHILD.equals(dataScope)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = {} or find_in_set( {} , ancestors ) )", deptAlias, user.getDeptId(), user.getDeptId())); + } + else if (DATA_SCOPE_SELF.equals(dataScope)) + { + if (StringUtils.isNotBlank(userAlias)) + { + sqlString.append(StringUtils.format(" OR {}.user_id = {} ", userAlias, user.getUserId())); + } + else + { + // 数据权限为仅本人且没有userAlias别名不查询任何数据 + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + } + conditions.add(dataScope); + } + + // 角色都不包含传递过来的权限字符,这个时候sqlString也会为空,所以要限制一下,不查询任何数据 + if (StringUtils.isEmpty(conditions)) + { + sqlString.append(StringUtils.format(" OR {}.dept_id = 0 ", deptAlias)); + } + + if (StringUtils.isNotBlank(sqlString.toString())) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlString.substring(4) + ")"); + } + } + } + + /** + * 拼接权限sql前先清空params.dataScope参数防止注入 + */ + private void clearDataScope(final JoinPoint joinPoint) + { + Object params = joinPoint.getArgs()[0]; + if (StringUtils.isNotNull(params) && params instanceof BaseEntity) + { + BaseEntity baseEntity = (BaseEntity) params; + baseEntity.getParams().put(DATA_SCOPE, ""); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java new file mode 100644 index 0000000..8c2c9f4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/DataSourceAspect.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Objects; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.datasource.DynamicDataSourceContextHolder; + +/** + * 多数据源处理 + * + * @author ruoyi + */ +@Aspect +@Order(1) +@Component +public class DataSourceAspect +{ + protected Logger logger = LoggerFactory.getLogger(getClass()); + + @Pointcut("@annotation(com.ruoyi.common.annotation.DataSource)" + + "|| @within(com.ruoyi.common.annotation.DataSource)") + public void dsPointCut() + { + + } + + @Around("dsPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable + { + DataSource dataSource = getDataSource(point); + + if (StringUtils.isNotNull(dataSource)) + { + DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name()); + } + + try + { + return point.proceed(); + } + finally + { + // 销毁数据源 在执行方法之后 + DynamicDataSourceContextHolder.clearDataSourceType(); + } + } + + /** + * 获取需要切换的数据源 + */ + public DataSource getDataSource(ProceedingJoinPoint point) + { + MethodSignature signature = (MethodSignature) point.getSignature(); + DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class); + if (Objects.nonNull(dataSource)) + { + return dataSource; + } + + return AnnotationUtils.findAnnotation(signature.getDeclaringType(), DataSource.class); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java new file mode 100644 index 0000000..bacf820 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/LogAspect.java @@ -0,0 +1,256 @@ +package com.ruoyi.framework.aspectj; + +import java.util.Collection; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.AfterReturning; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.NamedThreadLocal; +import org.springframework.stereotype.Component; +import org.springframework.validation.BindingResult; +import org.springframework.web.multipart.MultipartFile; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.BusinessStatus; +import com.ruoyi.common.enums.HttpMethod; +import com.ruoyi.common.filter.PropertyPreExcludeFilter; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志记录处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class LogAspect +{ + private static final Logger log = LoggerFactory.getLogger(LogAspect.class); + + /** 排除敏感属性字段 */ + public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" }; + + /** 计算操作消耗时间 */ + private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time"); + + /** + * 处理请求前执行 + */ + @Before(value = "@annotation(controllerLog)") + public void doBefore(JoinPoint joinPoint, Log controllerLog) + { + TIME_THREADLOCAL.set(System.currentTimeMillis()); + } + + /** + * 处理完请求后执行 + * + * @param joinPoint 切点 + */ + @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") + public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) + { + handleLog(joinPoint, controllerLog, null, jsonResult); + } + + /** + * 拦截异常操作 + * + * @param joinPoint 切点 + * @param e 异常 + */ + @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") + public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) + { + handleLog(joinPoint, controllerLog, e, null); + } + + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) + { + try + { + // 获取当前的用户 + LoginUser loginUser = SecurityUtils.getLoginUser(); + + // *========数据库日志=========*// + SysOperLog operLog = new SysOperLog(); + operLog.setStatus(BusinessStatus.SUCCESS.ordinal()); + // 请求的地址 + String ip = IpUtils.getIpAddr(); + operLog.setOperIp(ip); + operLog.setOperUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255)); + if (loginUser != null) + { + operLog.setOperName(loginUser.getUsername()); + SysUser currentUser = loginUser.getUser(); + if (StringUtils.isNotNull(currentUser) && StringUtils.isNotNull(currentUser.getDept())) + { + operLog.setDeptName(currentUser.getDept().getDeptName()); + } + } + + if (e != null) + { + operLog.setStatus(BusinessStatus.FAIL.ordinal()); + operLog.setErrorMsg(StringUtils.substring(Convert.toStr(e.getMessage(), ExceptionUtil.getExceptionMessage(e)), 0, 2000)); + } + // 设置方法名称 + String className = joinPoint.getTarget().getClass().getName(); + String methodName = joinPoint.getSignature().getName(); + operLog.setMethod(className + "." + methodName + "()"); + // 设置请求方式 + operLog.setRequestMethod(ServletUtils.getRequest().getMethod()); + // 处理设置注解上的参数 + getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); + // 设置消耗时间 + operLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get()); + // 保存数据库 + AsyncManager.me().execute(AsyncFactory.recordOper(operLog)); + } + catch (Exception exp) + { + // 记录本地异常日志 + log.error("异常信息:{}", exp.getMessage()); + exp.printStackTrace(); + } + finally + { + TIME_THREADLOCAL.remove(); + } + } + + /** + * 获取注解中对方法的描述信息 用于Controller层注解 + * + * @param log 日志 + * @param operLog 操作日志 + * @throws Exception + */ + public void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysOperLog operLog, Object jsonResult) throws Exception + { + // 设置action动作 + operLog.setBusinessType(log.businessType().ordinal()); + // 设置标题 + operLog.setTitle(log.title()); + // 设置操作人类别 + operLog.setOperatorType(log.operatorType().ordinal()); + // 是否需要保存request,参数和值 + if (log.isSaveRequestData()) + { + // 获取参数的信息,传入到数据库中。 + setRequestValue(joinPoint, operLog, log.excludeParamNames()); + } + // 是否需要保存response,参数和值 + if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult)) + { + operLog.setJsonResult(StringUtils.substring(JSON.toJSONString(jsonResult), 0, 2000)); + } + } + + /** + * 获取请求的参数,放到log中 + * + * @param operLog 操作日志 + * @throws Exception 异常 + */ + private void setRequestValue(JoinPoint joinPoint, SysOperLog operLog, String[] excludeParamNames) throws Exception + { + Map paramsMap = ServletUtils.getParamMap(ServletUtils.getRequest()); + String requestMethod = operLog.getRequestMethod(); + if (StringUtils.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) + { + String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); + operLog.setOperParam(StringUtils.substring(params, 0, 2000)); + } + else + { + operLog.setOperParam(StringUtils.substring(JSON.toJSONString(paramsMap, excludePropertyPreFilter(excludeParamNames)), 0, 2000)); + } + } + + /** + * 参数拼装 + */ + private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames) + { + String params = ""; + if (paramsArray != null && paramsArray.length > 0) + { + for (Object o : paramsArray) + { + if (StringUtils.isNotNull(o) && !isFilterObject(o)) + { + try + { + String jsonObj = JSON.toJSONString(o, excludePropertyPreFilter(excludeParamNames)); + params += jsonObj.toString() + " "; + } + catch (Exception e) + { + } + } + } + } + return params.trim(); + } + + /** + * 忽略敏感属性 + */ + public PropertyPreExcludeFilter excludePropertyPreFilter(String[] excludeParamNames) + { + return new PropertyPreExcludeFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames)); + } + + /** + * 判断是否需要过滤的对象。 + * + * @param o 对象信息。 + * @return 如果是需要过滤的对象,则返回true;否则返回false。 + */ + @SuppressWarnings("rawtypes") + public boolean isFilterObject(final Object o) + { + Class clazz = o.getClass(); + if (clazz.isArray()) + { + return clazz.getComponentType().isAssignableFrom(MultipartFile.class); + } + else if (Collection.class.isAssignableFrom(clazz)) + { + Collection collection = (Collection) o; + for (Object value : collection) + { + return value instanceof MultipartFile; + } + } + else if (Map.class.isAssignableFrom(clazz)) + { + Map map = (Map) o; + for (Object value : map.entrySet()) + { + Map.Entry entry = (Map.Entry) value; + return entry.getValue() instanceof MultipartFile; + } + } + return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse + || o instanceof BindingResult; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java new file mode 100644 index 0000000..b720bc1 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/aspectj/RateLimiterAspect.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.aspectj; + +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.RedisScript; +import org.springframework.stereotype.Component; +import com.ruoyi.common.annotation.RateLimiter; +import com.ruoyi.common.enums.LimitType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; + +/** + * 限流处理 + * + * @author ruoyi + */ +@Aspect +@Component +public class RateLimiterAspect +{ + private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); + + private RedisTemplate redisTemplate; + + private RedisScript limitScript; + + @Autowired + public void setRedisTemplate1(RedisTemplate redisTemplate) + { + this.redisTemplate = redisTemplate; + } + + @Autowired + public void setLimitScript(RedisScript limitScript) + { + this.limitScript = limitScript; + } + + @Before("@annotation(rateLimiter)") + public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable + { + int time = rateLimiter.time(); + int count = rateLimiter.count(); + + String combineKey = getCombineKey(rateLimiter, point); + List keys = Collections.singletonList(combineKey); + try + { + Long number = redisTemplate.execute(limitScript, keys, count, time); + if (StringUtils.isNull(number) || number.intValue() > count) + { + throw new ServiceException("访问过于频繁,请稍候再试"); + } + log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); + } + catch (ServiceException e) + { + throw e; + } + catch (Exception e) + { + throw new RuntimeException("服务器限流异常,请稍候再试"); + } + } + + public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) + { + StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); + if (rateLimiter.limitType() == LimitType.IP) + { + stringBuffer.append(IpUtils.getIpAddr()).append("-"); + } + MethodSignature signature = (MethodSignature) point.getSignature(); + Method method = signature.getMethod(); + Class targetClass = method.getDeclaringClass(); + stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); + return stringBuffer.toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java new file mode 100644 index 0000000..1d4dc1f --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ApplicationConfig.java @@ -0,0 +1,30 @@ +package com.ruoyi.framework.config; + +import java.util.TimeZone; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableAspectJAutoProxy; + +/** + * 程序注解配置 + * + * @author ruoyi + */ +@Configuration +// 表示通过aop框架暴露该代理对象,AopContext能够访问 +@EnableAspectJAutoProxy(exposeProxy = true) +// 指定要扫描的Mapper类的包的路径 +@MapperScan("com.ruoyi.**.mapper") +public class ApplicationConfig +{ + /** + * 时区配置 + */ + @Bean + public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() + { + return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(TimeZone.getDefault()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java new file mode 100644 index 0000000..43e78ae --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java @@ -0,0 +1,83 @@ +package com.ruoyi.framework.config; + +import java.util.Properties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.google.code.kaptcha.impl.DefaultKaptcha; +import com.google.code.kaptcha.util.Config; +import static com.google.code.kaptcha.Constants.*; + +/** + * 验证码配置 + * + * @author ruoyi + */ +@Configuration +public class CaptchaConfig +{ + @Bean(name = "captchaProducer") + public DefaultKaptcha getKaptchaBean() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } + + @Bean(name = "captchaProducerMath") + public DefaultKaptcha getKaptchaBeanMath() + { + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); + Properties properties = new Properties(); + // 是否有边框 默认为true 我们可以自己设置yes,no + properties.setProperty(KAPTCHA_BORDER, "yes"); + // 边框颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); + // 验证码文本字符颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); + // 验证码图片宽度 默认为200 + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); + // 验证码图片高度 默认为50 + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); + // 验证码文本字符大小 默认为40 + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); + // KAPTCHA_SESSION_KEY + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); + // 验证码文本生成器 + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); + // 验证码文本字符间距 默认为2 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); + // 验证码文本字符长度 默认为5 + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); + // 验证码噪点颜色 默认为Color.BLACK + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); + // 干扰实现类 + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); + Config config = new Config(properties); + defaultKaptcha.setConfig(config); + return defaultKaptcha; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java new file mode 100644 index 0000000..f6abac1 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/DruidConfig.java @@ -0,0 +1,126 @@ +package com.ruoyi.framework.config; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.sql.DataSource; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import com.alibaba.druid.pool.DruidDataSource; +import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder; +import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties; +import com.alibaba.druid.util.Utils; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.framework.config.properties.DruidProperties; +import com.ruoyi.framework.datasource.DynamicDataSource; + +/** + * druid 配置多数据源 + * + * @author ruoyi + */ +@Configuration +public class DruidConfig +{ + @Bean + @ConfigurationProperties("spring.datasource.druid.master") + public DataSource masterDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean + @ConfigurationProperties("spring.datasource.druid.slave") + @ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true") + public DataSource slaveDataSource(DruidProperties druidProperties) + { + DruidDataSource dataSource = DruidDataSourceBuilder.create().build(); + return druidProperties.dataSource(dataSource); + } + + @Bean(name = "dynamicDataSource") + @Primary + public DynamicDataSource dataSource(DataSource masterDataSource) + { + Map targetDataSources = new HashMap<>(); + targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource); + setDataSource(targetDataSources, DataSourceType.SLAVE.name(), "slaveDataSource"); + return new DynamicDataSource(masterDataSource, targetDataSources); + } + + /** + * 设置数据源 + * + * @param targetDataSources 备选数据源集合 + * @param sourceName 数据源名称 + * @param beanName bean名称 + */ + public void setDataSource(Map targetDataSources, String sourceName, String beanName) + { + try + { + DataSource dataSource = SpringUtils.getBean(beanName); + targetDataSources.put(sourceName, dataSource); + } + catch (Exception e) + { + } + } + + /** + * 去除监控页面底部的广告 + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(name = "spring.datasource.druid.statViewServlet.enabled", havingValue = "true") + public FilterRegistrationBean removeDruidFilterRegistrationBean(DruidStatProperties properties) + { + // 获取web监控页面的参数 + DruidStatProperties.StatViewServlet config = properties.getStatViewServlet(); + // 提取common.js的配置路径 + String pattern = config.getUrlPattern() != null ? config.getUrlPattern() : "/druid/*"; + String commonJsPattern = pattern.replaceAll("\\*", "js/common.js"); + final String filePath = "support/http/resources/js/common.js"; + // 创建filter进行过滤 + Filter filter = new Filter() + { + @Override + public void init(javax.servlet.FilterConfig filterConfig) throws ServletException + { + } + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException + { + chain.doFilter(request, response); + // 重置缓冲区,响应头不会被重置 + response.resetBuffer(); + // 获取common.js + String text = Utils.readFromResource(filePath); + // 正则替换banner, 除去底部的广告信息 + text = text.replaceAll("
", ""); + text = text.replaceAll("powered.*?shrek.wang", ""); + response.getWriter().write(text); + } + @Override + public void destroy() + { + } + }; + FilterRegistrationBean registrationBean = new FilterRegistrationBean(); + registrationBean.setFilter(filter); + registrationBean.addUrlPatterns(commonJsPattern); + return registrationBean; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java new file mode 100644 index 0000000..0b7ad59 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FastJson2JsonRedisSerializer.java @@ -0,0 +1,52 @@ +package com.ruoyi.framework.config; + +import java.nio.charset.Charset; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONReader; +import com.alibaba.fastjson2.JSONWriter; +import com.alibaba.fastjson2.filter.Filter; +import com.ruoyi.common.constant.Constants; + +/** + * Redis使用FastJson序列化 + * + * @author ruoyi + */ +public class FastJson2JsonRedisSerializer implements RedisSerializer +{ + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR); + + private Class clazz; + + public FastJson2JsonRedisSerializer(Class clazz) + { + super(); + this.clazz = clazz; + } + + @Override + public byte[] serialize(T t) throws SerializationException + { + if (t == null) + { + return new byte[0]; + } + return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); + } + + @Override + public T deserialize(byte[] bytes) throws SerializationException + { + if (bytes == null || bytes.length <= 0) + { + return null; + } + String str = new String(bytes, DEFAULT_CHARSET); + + return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java new file mode 100644 index 0000000..bb14c04 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/FilterConfig.java @@ -0,0 +1,58 @@ +package com.ruoyi.framework.config; + +import java.util.HashMap; +import java.util.Map; +import javax.servlet.DispatcherType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import com.ruoyi.common.filter.RepeatableFilter; +import com.ruoyi.common.filter.XssFilter; +import com.ruoyi.common.utils.StringUtils; + +/** + * Filter配置 + * + * @author ruoyi + */ +@Configuration +public class FilterConfig +{ + @Value("${xss.excludes}") + private String excludes; + + @Value("${xss.urlPatterns}") + private String urlPatterns; + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + @ConditionalOnProperty(value = "xss.enabled", havingValue = "true") + public FilterRegistrationBean xssFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns(StringUtils.split(urlPatterns, ",")); + registration.setName("xssFilter"); + registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE); + Map initParameters = new HashMap(); + initParameters.put("excludes", excludes); + registration.setInitParameters(initParameters); + return registration; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Bean + public FilterRegistrationBean someFilterRegistration() + { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new RepeatableFilter()); + registration.addUrlPatterns("/*"); + registration.setName("repeatableFilter"); + registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); + return registration; + } + +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java new file mode 100644 index 0000000..163fd01 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/I18nConfig.java @@ -0,0 +1,43 @@ +package com.ruoyi.framework.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import org.springframework.web.servlet.i18n.SessionLocaleResolver; +import com.ruoyi.common.constant.Constants; + +/** + * 资源文件配置加载 + * + * @author ruoyi + */ +@Configuration +public class I18nConfig implements WebMvcConfigurer +{ + @Bean + public LocaleResolver localeResolver() + { + SessionLocaleResolver slr = new SessionLocaleResolver(); + // 默认语言 + slr.setDefaultLocale(Constants.DEFAULT_LOCALE); + return slr; + } + + @Bean + public LocaleChangeInterceptor localeChangeInterceptor() + { + LocaleChangeInterceptor lci = new LocaleChangeInterceptor(); + // 参数名 + lci.setParamName("lang"); + return lci; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(localeChangeInterceptor()); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java new file mode 100644 index 0000000..7f8e1d5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java @@ -0,0 +1,68 @@ +package com.ruoyi.framework.config; + +import java.util.Random; +import com.google.code.kaptcha.text.impl.DefaultTextCreator; + +/** + * 验证码文本生成器 + * + * @author ruoyi + */ +public class KaptchaTextCreator extends DefaultTextCreator +{ + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); + + @Override + public String getText() + { + Integer result = 0; + Random random = new Random(); + int x = random.nextInt(10); + int y = random.nextInt(10); + StringBuilder suChinese = new StringBuilder(); + int randomoperands = random.nextInt(3); + if (randomoperands == 0) + { + result = x * y; + suChinese.append(CNUMBERS[x]); + suChinese.append("*"); + suChinese.append(CNUMBERS[y]); + } + else if (randomoperands == 1) + { + if ((x != 0) && y % x == 0) + { + result = y / x; + suChinese.append(CNUMBERS[y]); + suChinese.append("/"); + suChinese.append(CNUMBERS[x]); + } + else + { + result = x + y; + suChinese.append(CNUMBERS[x]); + suChinese.append("+"); + suChinese.append(CNUMBERS[y]); + } + } + else + { + if (x >= y) + { + result = x - y; + suChinese.append(CNUMBERS[x]); + suChinese.append("-"); + suChinese.append(CNUMBERS[y]); + } + else + { + result = y - x; + suChinese.append(CNUMBERS[y]); + suChinese.append("-"); + suChinese.append(CNUMBERS[x]); + } + } + suChinese.append("=?@" + result); + return suChinese.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java new file mode 100644 index 0000000..8307a76 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/MyBatisConfig.java @@ -0,0 +1,135 @@ +package com.ruoyi.framework.config; + +import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import javax.sql.DataSource; +import org.apache.ibatis.io.VFS; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.boot.autoconfigure.SpringBootVFS; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.classreading.CachingMetadataReaderFactory; +import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.util.ClassUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * Mybatis支持*匹配扫描包 + * + * @author ruoyi + */ +@Configuration +public class MyBatisConfig +{ + @Autowired + private Environment env; + + static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; + + public static String setTypeAliasesPackage(String typeAliasesPackage) + { + ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); + MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); + List allResult = new ArrayList(); + try + { + for (String aliasesPackage : typeAliasesPackage.split(",")) + { + List result = new ArrayList(); + aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; + Resource[] resources = resolver.getResources(aliasesPackage); + if (resources != null && resources.length > 0) + { + MetadataReader metadataReader = null; + for (Resource resource : resources) + { + if (resource.isReadable()) + { + metadataReader = metadataReaderFactory.getMetadataReader(resource); + try + { + result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); + } + catch (ClassNotFoundException e) + { + e.printStackTrace(); + } + } + } + } + if (result.size() > 0) + { + HashSet hashResult = new HashSet(result); + allResult.addAll(hashResult); + } + } + if (allResult.size() > 0) + { + typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); + } + else + { + throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + return typeAliasesPackage; + } + + public Resource[] resolveMapperLocations(String[] mapperLocations) + { + ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); + List resources = new ArrayList(); + if (mapperLocations != null) + { + for (String mapperLocation : mapperLocations) + { + try + { + Resource[] mappers = resourceResolver.getResources(mapperLocation); + resources.addAll(Arrays.asList(mappers)); + } + catch (IOException e) + { + // ignore + } + } + } + return resources.toArray(new Resource[resources.size()]); + } + + @Bean + public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception + { + String typeAliasesPackage = env.getProperty("mybatis.typeAliasesPackage"); + String mapperLocations = env.getProperty("mybatis.mapperLocations"); + String configLocation = env.getProperty("mybatis.configLocation"); + typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); + VFS.addImplClass(SpringBootVFS.class); + + final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); + +// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); + sessionFactory.setDataSource(dataSource); + sessionFactory.setTypeAliasesPackage(typeAliasesPackage); + sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); + sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); + return sessionFactory.getObject(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java new file mode 100644 index 0000000..3f4f485 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/RedisConfig.java @@ -0,0 +1,69 @@ +package com.ruoyi.framework.config; + +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.script.DefaultRedisScript; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * redis配置 + * + * @author ruoyi + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport +{ + @Bean + @SuppressWarnings(value = { "unchecked", "rawtypes" }) + public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) + { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + + FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); + + // 使用StringRedisSerializer来序列化和反序列化redis的key值 + template.setKeySerializer(new StringRedisSerializer()); + template.setValueSerializer(serializer); + + // Hash的key也采用StringRedisSerializer的序列化方式 + template.setHashKeySerializer(new StringRedisSerializer()); + template.setHashValueSerializer(serializer); + + template.afterPropertiesSet(); + return template; + } + + @Bean + public DefaultRedisScript limitScript() + { + DefaultRedisScript redisScript = new DefaultRedisScript<>(); + redisScript.setScriptText(limitScriptText()); + redisScript.setResultType(Long.class); + return redisScript; + } + + /** + * 限流脚本 + */ + private String limitScriptText() + { + return "local key = KEYS[1]\n" + + "local count = tonumber(ARGV[1])\n" + + "local time = tonumber(ARGV[2])\n" + + "local current = redis.call('get', key);\n" + + "if current and tonumber(current) > count then\n" + + " return tonumber(current);\n" + + "end\n" + + "current = redis.call('incr', key)\n" + + "if tonumber(current) == 1 then\n" + + " redis.call('expire', key, time)\n" + + "end\n" + + "return tonumber(current);"; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java new file mode 100644 index 0000000..0f48b11 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ResourcesConfig.java @@ -0,0 +1,72 @@ +package com.ruoyi.framework.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.CacheControl; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 通用配置 + * + * @author ruoyi + */ +@Configuration +public class ResourcesConfig implements WebMvcConfigurer +{ + @Autowired + private RepeatSubmitInterceptor repeatSubmitInterceptor; + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) + { + /** 本地文件上传路径 */ + registry.addResourceHandler(Constants.RESOURCE_PREFIX + "/**") + .addResourceLocations("file:" + RuoYiConfig.getProfile() + "/"); + + /** swagger配置 */ + registry.addResourceHandler("/swagger-ui/**") + .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/") + .setCacheControl(CacheControl.maxAge(5, TimeUnit.HOURS).cachePublic()); + } + + /** + * 自定义拦截规则 + */ + @Override + public void addInterceptors(InterceptorRegistry registry) + { + registry.addInterceptor(repeatSubmitInterceptor).addPathPatterns("/**"); + } + + /** + * 跨域配置 + */ + @Bean + public CorsFilter corsFilter() + { + CorsConfiguration config = new CorsConfiguration(); + // 设置访问源地址 + config.addAllowedOriginPattern("*"); + // 设置访问源请求头 + config.addAllowedHeader("*"); + // 设置访问源请求方法 + config.addAllowedMethod("*"); + // 有效期 1800秒 + config.setMaxAge(1800L); + // 添加映射路径,拦截一切请求 + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", config); + // 返回新的CorsFilter + return new CorsFilter(source); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java new file mode 100644 index 0000000..5f10a96 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java @@ -0,0 +1,144 @@ +package com.ruoyi.framework.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.logout.LogoutFilter; +import org.springframework.web.filter.CorsFilter; +import com.ruoyi.framework.config.properties.PermitAllUrlProperties; +import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter; +import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl; +import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl; + +/** + * spring security配置 + * + * @author ruoyi + */ +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +@Configuration +public class SecurityConfig +{ + /** + * 自定义用户认证逻辑 + */ + @Autowired + private UserDetailsService userDetailsService; + + /** + * 认证失败处理类 + */ + @Autowired + private AuthenticationEntryPointImpl unauthorizedHandler; + + /** + * 退出处理类 + */ + @Autowired + private LogoutSuccessHandlerImpl logoutSuccessHandler; + + /** + * token认证过滤器 + */ + @Autowired + private JwtAuthenticationTokenFilter authenticationTokenFilter; + + /** + * 跨域过滤器 + */ + @Autowired + private CorsFilter corsFilter; + + /** + * 允许匿名访问的地址 + */ + @Autowired + private PermitAllUrlProperties permitAllUrl; + + /** + * 身份验证实现 + */ + @Bean + public AuthenticationManager authenticationManager() + { + DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); + daoAuthenticationProvider.setUserDetailsService(userDetailsService); + daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder()); + return new ProviderManager(daoAuthenticationProvider); + } + + /** + * anyRequest | 匹配所有请求路径 + * access | SpringEl表达式结果为true时可以访问 + * anonymous | 匿名可以访问 + * denyAll | 用户不能访问 + * fullyAuthenticated | 用户完全认证可以访问(非remember-me下自动登录) + * hasAnyAuthority | 如果有参数,参数表示权限,则其中任何一个权限可以访问 + * hasAnyRole | 如果有参数,参数表示角色,则其中任何一个角色可以访问 + * hasAuthority | 如果有参数,参数表示权限,则其权限可以访问 + * hasIpAddress | 如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问 + * hasRole | 如果有参数,参数表示角色,则其角色可以访问 + * permitAll | 用户可以任意访问 + * rememberMe | 允许通过remember-me登录的用户访问 + * authenticated | 用户登录后可访问 + */ + @Bean + protected SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception + { + return httpSecurity + // CSRF禁用,因为不使用session + .csrf(csrf -> csrf.disable()) + // 禁用HTTP响应标头 + .headers((headersCustomizer) -> { + headersCustomizer.cacheControl(cache -> cache.disable()).frameOptions(options -> options.sameOrigin()); + }) + // 认证失败处理类 + .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) + // 基于token,所以不需要session + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + // 注解标记允许匿名访问的url + .authorizeHttpRequests((requests) -> { + permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); + // 对于登录login 注册register 验证码captchaImage 允许匿名访问 + requests.antMatchers("/login", "/register", "/captchaImage").permitAll() + // 静态资源,可匿名访问 + .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() + .antMatchers("/swagger-ui.html", "/changeLanguage", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**", "/pda/**").permitAll() + .antMatchers("/device/operation/**").permitAll() + .antMatchers("/agv/api/**").permitAll() + .antMatchers("/device/api/**").permitAll() + .antMatchers("/wcs2Wms/api/**").permitAll() + .antMatchers("/largeScreen/monitor/**").permitAll() + // 除上面外的所有请求全部需要鉴权认证 + .anyRequest().authenticated(); + }) + // 添加Logout filter + .logout(logout -> logout.logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler)) + // 添加JWT filter + .addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class) + // 添加CORS filter + .addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class) + .addFilterBefore(corsFilter, LogoutFilter.class) + .build(); + } + + /** + * 强散列哈希加密实现 + */ + @Bean + public BCryptPasswordEncoder bCryptPasswordEncoder() + { + return new BCryptPasswordEncoder(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java new file mode 100644 index 0000000..b5b7de3 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ServerConfig.java @@ -0,0 +1,32 @@ +package com.ruoyi.framework.config; + +import javax.servlet.http.HttpServletRequest; +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 服务相关配置 + * + * @author ruoyi + */ +@Component +public class ServerConfig +{ + /** + * 获取完整的请求路径,包括:域名,端口,上下文访问路径 + * + * @return 服务地址 + */ + public String getUrl() + { + HttpServletRequest request = ServletUtils.getRequest(); + return getDomain(request); + } + + public static String getDomain(HttpServletRequest request) + { + StringBuffer url = request.getRequestURL(); + String contextPath = request.getServletContext().getContextPath(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java new file mode 100644 index 0000000..7840141 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/ThreadPoolConfig.java @@ -0,0 +1,63 @@ +package com.ruoyi.framework.config; + +import com.ruoyi.common.utils.Threads; +import org.apache.commons.lang3.concurrent.BasicThreadFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadPoolExecutor; + +/** + * 线程池配置 + * + * @author ruoyi + **/ +@Configuration +public class ThreadPoolConfig +{ + // 核心线程池大小 + private int corePoolSize = 50; + + // 最大可创建的线程数 + private int maxPoolSize = 200; + + // 队列最大长度 + private int queueCapacity = 1000; + + // 线程池维护线程所允许的空闲时间 + private int keepAliveSeconds = 300; + + @Bean(name = "threadPoolTaskExecutor") + public ThreadPoolTaskExecutor threadPoolTaskExecutor() + { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setMaxPoolSize(maxPoolSize); + executor.setCorePoolSize(corePoolSize); + executor.setQueueCapacity(queueCapacity); + executor.setKeepAliveSeconds(keepAliveSeconds); + // 线程池对拒绝任务(无线程可用)的处理策略 + executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + return executor; + } + + /** + * 执行周期性或定时任务 + */ + @Bean(name = "scheduledExecutorService") + protected ScheduledExecutorService scheduledExecutorService() + { + return new ScheduledThreadPoolExecutor(corePoolSize, + new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(), + new ThreadPoolExecutor.CallerRunsPolicy()) + { + @Override + protected void afterExecute(Runnable r, Throwable t) + { + super.afterExecute(r, t); + Threads.printException(r, t); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java new file mode 100644 index 0000000..c8a5c8a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/DruidProperties.java @@ -0,0 +1,89 @@ +package com.ruoyi.framework.config.properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import com.alibaba.druid.pool.DruidDataSource; + +/** + * druid 配置属性 + * + * @author ruoyi + */ +@Configuration +public class DruidProperties +{ + @Value("${spring.datasource.druid.initialSize}") + private int initialSize; + + @Value("${spring.datasource.druid.minIdle}") + private int minIdle; + + @Value("${spring.datasource.druid.maxActive}") + private int maxActive; + + @Value("${spring.datasource.druid.maxWait}") + private int maxWait; + + @Value("${spring.datasource.druid.connectTimeout}") + private int connectTimeout; + + @Value("${spring.datasource.druid.socketTimeout}") + private int socketTimeout; + + @Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}") + private int timeBetweenEvictionRunsMillis; + + @Value("${spring.datasource.druid.minEvictableIdleTimeMillis}") + private int minEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.maxEvictableIdleTimeMillis}") + private int maxEvictableIdleTimeMillis; + + @Value("${spring.datasource.druid.validationQuery}") + private String validationQuery; + + @Value("${spring.datasource.druid.testWhileIdle}") + private boolean testWhileIdle; + + @Value("${spring.datasource.druid.testOnBorrow}") + private boolean testOnBorrow; + + @Value("${spring.datasource.druid.testOnReturn}") + private boolean testOnReturn; + + public DruidDataSource dataSource(DruidDataSource datasource) + { + /** 配置初始化大小、最小、最大 */ + datasource.setInitialSize(initialSize); + datasource.setMaxActive(maxActive); + datasource.setMinIdle(minIdle); + + /** 配置获取连接等待超时的时间 */ + datasource.setMaxWait(maxWait); + + /** 配置驱动连接超时时间,检测数据库建立连接的超时时间,单位是毫秒 */ + datasource.setConnectTimeout(connectTimeout); + + /** 配置网络超时时间,等待数据库操作完成的网络超时时间,单位是毫秒 */ + datasource.setSocketTimeout(socketTimeout); + + /** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */ + datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); + + /** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */ + datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); + datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis); + + /** + * 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。 + */ + datasource.setValidationQuery(validationQuery); + /** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */ + datasource.setTestWhileIdle(testWhileIdle); + /** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnBorrow(testOnBorrow); + /** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */ + datasource.setTestOnReturn(testOnReturn); + return datasource; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java new file mode 100644 index 0000000..29118fa --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/config/properties/PermitAllUrlProperties.java @@ -0,0 +1,73 @@ +package com.ruoyi.framework.config.properties; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import com.ruoyi.common.annotation.Anonymous; + +/** + * 设置Anonymous注解允许匿名访问的url + * + * @author ruoyi + */ +@Configuration +public class PermitAllUrlProperties implements InitializingBean, ApplicationContextAware +{ + private static final Pattern PATTERN = Pattern.compile("\\{(.*?)\\}"); + + private ApplicationContext applicationContext; + + private List urls = new ArrayList<>(); + + public String ASTERISK = "*"; + + @Override + public void afterPropertiesSet() + { + RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class); + Map map = mapping.getHandlerMethods(); + + map.keySet().forEach(info -> { + HandlerMethod handlerMethod = map.get(info); + + // 获取方法上边的注解 替代path variable 为 * + Anonymous method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), Anonymous.class); + Optional.ofNullable(method).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + + // 获取类上边的注解, 替代path variable 为 * + Anonymous controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), Anonymous.class); + Optional.ofNullable(controller).ifPresent(anonymous -> Objects.requireNonNull(info.getPatternsCondition().getPatterns()) + .forEach(url -> urls.add(RegExUtils.replaceAll(url, PATTERN, ASTERISK)))); + }); + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException + { + this.applicationContext = context; + } + + public List getUrls() + { + return urls; + } + + public void setUrls(List urls) + { + this.urls = urls; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java new file mode 100644 index 0000000..e70b8cf --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSource.java @@ -0,0 +1,26 @@ +package com.ruoyi.framework.datasource; + +import java.util.Map; +import javax.sql.DataSource; +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +/** + * 动态数据源 + * + * @author ruoyi + */ +public class DynamicDataSource extends AbstractRoutingDataSource +{ + public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) + { + super.setDefaultTargetDataSource(defaultTargetDataSource); + super.setTargetDataSources(targetDataSources); + super.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() + { + return DynamicDataSourceContextHolder.getDataSourceType(); + } +} \ No newline at end of file diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..9770af6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/datasource/DynamicDataSourceContextHolder.java @@ -0,0 +1,45 @@ +package com.ruoyi.framework.datasource; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * 数据源切换处理 + * + * @author ruoyi + */ +public class DynamicDataSourceContextHolder +{ + public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); + + /** + * 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本, + * 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。 + */ + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + /** + * 设置数据源的变量 + */ + public static void setDataSourceType(String dsType) + { + log.info("切换到{}数据源", dsType); + CONTEXT_HOLDER.set(dsType); + } + + /** + * 获得数据源的变量 + */ + public static String getDataSourceType() + { + return CONTEXT_HOLDER.get(); + } + + /** + * 清空数据源变量 + */ + public static void clearDataSourceType() + { + CONTEXT_HOLDER.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java new file mode 100644 index 0000000..c49eaf4 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/RepeatSubmitInterceptor.java @@ -0,0 +1,56 @@ +package com.ruoyi.framework.interceptor; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.stereotype.Component; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerInterceptor; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; + +/** + * 防止重复提交拦截器 + * + * @author ruoyi + */ +@Component +public abstract class RepeatSubmitInterceptor implements HandlerInterceptor +{ + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception + { + if (handler instanceof HandlerMethod) + { + HandlerMethod handlerMethod = (HandlerMethod) handler; + Method method = handlerMethod.getMethod(); + RepeatSubmit annotation = method.getAnnotation(RepeatSubmit.class); + if (annotation != null) + { + if (this.isRepeatSubmit(request, annotation)) + { + AjaxResult ajaxResult = AjaxResult.error(annotation.message()); + ServletUtils.renderString(response, JSON.toJSONString(ajaxResult)); + return false; + } + } + return true; + } + else + { + return true; + } + } + + /** + * 验证是否重复提交由子类实现具体的防重复提交的规则 + * + * @param request 请求信息 + * @param annotation 防重复注解参数 + * @return 结果 + * @throws Exception + */ + public abstract boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation); +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java new file mode 100644 index 0000000..9dc9511 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/interceptor/impl/SameUrlDataInterceptor.java @@ -0,0 +1,110 @@ +package com.ruoyi.framework.interceptor.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.annotation.RepeatSubmit; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.filter.RepeatedlyRequestWrapper; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.http.HttpHelper; +import com.ruoyi.framework.interceptor.RepeatSubmitInterceptor; + +/** + * 判断请求url和数据是否和上一次相同, + * 如果和上次相同,则是重复提交表单。 有效时间为10秒内。 + * + * @author ruoyi + */ +@Component +public class SameUrlDataInterceptor extends RepeatSubmitInterceptor +{ + public final String REPEAT_PARAMS = "repeatParams"; + + public final String REPEAT_TIME = "repeatTime"; + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + @Autowired + private RedisCache redisCache; + + @SuppressWarnings("unchecked") + @Override + public boolean isRepeatSubmit(HttpServletRequest request, RepeatSubmit annotation) + { + String nowParams = ""; + if (request instanceof RepeatedlyRequestWrapper) + { + RepeatedlyRequestWrapper repeatedlyRequest = (RepeatedlyRequestWrapper) request; + nowParams = HttpHelper.getBodyString(repeatedlyRequest); + } + + // body参数为空,获取Parameter的数据 + if (StringUtils.isEmpty(nowParams)) + { + nowParams = JSON.toJSONString(request.getParameterMap()); + } + Map nowDataMap = new HashMap(); + nowDataMap.put(REPEAT_PARAMS, nowParams); + nowDataMap.put(REPEAT_TIME, System.currentTimeMillis()); + + // 请求地址(作为存放cache的key值) + String url = request.getRequestURI(); + + // 唯一值(没有消息头则使用请求地址) + String submitKey = StringUtils.trimToEmpty(request.getHeader(header)); + + // 唯一标识(指定key + url + 消息头) + String cacheRepeatKey = CacheConstants.REPEAT_SUBMIT_KEY + url + submitKey; + + Object sessionObj = redisCache.getCacheObject(cacheRepeatKey); + if (sessionObj != null) + { + Map sessionMap = (Map) sessionObj; + if (sessionMap.containsKey(url)) + { + Map preDataMap = (Map) sessionMap.get(url); + if (compareParams(nowDataMap, preDataMap) && compareTime(nowDataMap, preDataMap, annotation.interval())) + { + return true; + } + } + } + Map cacheMap = new HashMap(); + cacheMap.put(url, nowDataMap); + redisCache.setCacheObject(cacheRepeatKey, cacheMap, annotation.interval(), TimeUnit.MILLISECONDS); + return false; + } + + /** + * 判断参数是否相同 + */ + private boolean compareParams(Map nowMap, Map preMap) + { + String nowParams = (String) nowMap.get(REPEAT_PARAMS); + String preParams = (String) preMap.get(REPEAT_PARAMS); + return nowParams.equals(preParams); + } + + /** + * 判断两次间隔时间 + */ + private boolean compareTime(Map nowMap, Map preMap, int interval) + { + long time1 = (Long) nowMap.get(REPEAT_TIME); + long time2 = (Long) preMap.get(REPEAT_TIME); + if ((time1 - time2) < interval) + { + return true; + } + return false; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java new file mode 100644 index 0000000..7387a02 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/AsyncManager.java @@ -0,0 +1,55 @@ +package com.ruoyi.framework.manager; + +import java.util.TimerTask; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import com.ruoyi.common.utils.Threads; +import com.ruoyi.common.utils.spring.SpringUtils; + +/** + * 异步任务管理器 + * + * @author ruoyi + */ +public class AsyncManager +{ + /** + * 操作延迟10毫秒 + */ + private final int OPERATE_DELAY_TIME = 10; + + /** + * 异步操作任务调度线程池 + */ + private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService"); + + /** + * 单例模式 + */ + private AsyncManager(){} + + private static AsyncManager me = new AsyncManager(); + + public static AsyncManager me() + { + return me; + } + + /** + * 执行任务 + * + * @param task 任务 + */ + public void execute(TimerTask task) + { + executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); + } + + /** + * 停止任务线程池 + */ + public void shutdown() + { + Threads.shutdownAndAwaitTermination(executor); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java new file mode 100644 index 0000000..e36ca3c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/ShutdownManager.java @@ -0,0 +1,39 @@ +package com.ruoyi.framework.manager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import javax.annotation.PreDestroy; + +/** + * 确保应用退出时能关闭后台线程 + * + * @author ruoyi + */ +@Component +public class ShutdownManager +{ + private static final Logger logger = LoggerFactory.getLogger("sys-user"); + + @PreDestroy + public void destroy() + { + shutdownAsyncManager(); + } + + /** + * 停止异步执行任务 + */ + private void shutdownAsyncManager() + { + try + { + logger.info("====关闭后台任务任务线程池===="); + AsyncManager.me().shutdown(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java new file mode 100644 index 0000000..267e305 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/manager/factory/AsyncFactory.java @@ -0,0 +1,102 @@ +package com.ruoyi.framework.manager.factory; + +import java.util.TimerTask; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.utils.LogUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.service.ISysLogininforService; +import com.ruoyi.system.service.ISysOperLogService; +import eu.bitwalker.useragentutils.UserAgent; + +/** + * 异步工厂(产生任务用) + * + * @author ruoyi + */ +public class AsyncFactory +{ + private static final Logger sys_user_logger = LoggerFactory.getLogger("sys-user"); + + /** + * 记录登录信息 + * + * @param username 用户名 + * @param status 状态 + * @param message 消息 + * @param args 列表 + * @return 任务task + */ + public static TimerTask recordLogininfor(final String username, final String status, final String message, + final Object... args) + { + final UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + final String ip = IpUtils.getIpAddr(); + return new TimerTask() + { + @Override + public void run() + { + String address = AddressUtils.getRealAddressByIP(ip); + StringBuilder s = new StringBuilder(); + s.append(LogUtils.getBlock(ip)); + s.append(address); + s.append(LogUtils.getBlock(username)); + s.append(LogUtils.getBlock(status)); + s.append(LogUtils.getBlock(message)); + // 打印信息到日志 + sys_user_logger.info(s.toString(), args); + // 获取客户端操作系统 + String os = userAgent.getOperatingSystem().getName(); + // 获取客户端浏览器 + String browser = userAgent.getBrowser().getName(); + // 封装对象 + SysLogininfor logininfor = new SysLogininfor(); + logininfor.setUserName(username); + logininfor.setIpaddr(ip); + logininfor.setLoginLocation(address); + logininfor.setBrowser(browser); + logininfor.setOs(os); + logininfor.setMsg(message); + // 日志状态 + if (StringUtils.equalsAny(status, Constants.LOGIN_SUCCESS, Constants.LOGOUT, Constants.REGISTER)) + { + logininfor.setStatus(Constants.SUCCESS); + } + else if (Constants.LOGIN_FAIL.equals(status)) + { + logininfor.setStatus(Constants.FAIL); + } + // 插入数据 + SpringUtils.getBean(ISysLogininforService.class).insertLogininfor(logininfor); + } + }; + } + + /** + * 操作日志记录 + * + * @param operLog 操作日志信息 + * @return 任务task + */ + public static TimerTask recordOper(final SysOperLog operLog) + { + return new TimerTask() + { + @Override + public void run() + { + // 远程查询操作地点 + operLog.setOperLocation(AddressUtils.getRealAddressByIP(operLog.getOperIp())); + SpringUtils.getBean(ISysOperLogService.class).insertOperlog(operLog); + } + }; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java new file mode 100644 index 0000000..6c776ce --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/AuthenticationContextHolder.java @@ -0,0 +1,28 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.security.core.Authentication; + +/** + * 身份验证信息 + * + * @author ruoyi + */ +public class AuthenticationContextHolder +{ + private static final ThreadLocal contextHolder = new ThreadLocal<>(); + + public static Authentication getContext() + { + return contextHolder.get(); + } + + public static void setContext(Authentication context) + { + contextHolder.set(context); + } + + public static void clearContext() + { + contextHolder.remove(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java new file mode 100644 index 0000000..5472f3d --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/context/PermissionContextHolder.java @@ -0,0 +1,27 @@ +package com.ruoyi.framework.security.context; + +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import com.ruoyi.common.core.text.Convert; + +/** + * 权限信息 + * + * @author ruoyi + */ +public class PermissionContextHolder +{ + private static final String PERMISSION_CONTEXT_ATTRIBUTES = "PERMISSION_CONTEXT"; + + public static void setContext(String permission) + { + RequestContextHolder.currentRequestAttributes().setAttribute(PERMISSION_CONTEXT_ATTRIBUTES, permission, + RequestAttributes.SCOPE_REQUEST); + } + + public static String getContext() + { + return Convert.toStr(RequestContextHolder.currentRequestAttributes().getAttribute(PERMISSION_CONTEXT_ATTRIBUTES, + RequestAttributes.SCOPE_REQUEST)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java new file mode 100644 index 0000000..3eb2495 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/filter/JwtAuthenticationTokenFilter.java @@ -0,0 +1,44 @@ +package com.ruoyi.framework.security.filter; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.TokenService; + +/** + * token过滤器 验证token有效性 + * + * @author ruoyi + */ +@Component +public class JwtAuthenticationTokenFilter extends OncePerRequestFilter +{ + @Autowired + private TokenService tokenService; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) + { + tokenService.verifyToken(loginUser); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); + authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + SecurityContextHolder.getContext().setAuthentication(authenticationToken); + } + chain.doFilter(request, response); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java new file mode 100644 index 0000000..93b7032 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/AuthenticationEntryPointImpl.java @@ -0,0 +1,34 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import java.io.Serializable; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; + +/** + * 认证失败处理类 返回未授权 + * + * @author ruoyi + */ +@Component +public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable +{ + private static final long serialVersionUID = -8970718410437077606L; + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) + throws IOException + { + int code = HttpStatus.UNAUTHORIZED; + String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI()); + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java new file mode 100644 index 0000000..2f89a91 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/security/handle/LogoutSuccessHandlerImpl.java @@ -0,0 +1,53 @@ +package com.ruoyi.framework.security.handle; + +import java.io.IOException; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; +import com.alibaba.fastjson2.JSON; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.web.service.TokenService; + +/** + * 自定义退出处理类 返回成功 + * + * @author ruoyi + */ +@Configuration +public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler +{ + @Autowired + private TokenService tokenService; + + /** + * 退出处理 + * + * @return + */ + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException + { + LoginUser loginUser = tokenService.getLoginUser(request); + if (StringUtils.isNotNull(loginUser)) + { + String userName = loginUser.getUsername(); + // 删除用户缓存记录 + tokenService.delLoginUser(loginUser.getToken()); + // 记录用户退出日志 + AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, MessageUtils.message("user.logout.success"))); + } + ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.success(MessageUtils.message("user.logout.success")))); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java new file mode 100644 index 0000000..63b03da --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/Server.java @@ -0,0 +1,240 @@ +package com.ruoyi.framework.web.domain; + +import java.net.UnknownHostException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.web.domain.server.Cpu; +import com.ruoyi.framework.web.domain.server.Jvm; +import com.ruoyi.framework.web.domain.server.Mem; +import com.ruoyi.framework.web.domain.server.Sys; +import com.ruoyi.framework.web.domain.server.SysFile; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.CentralProcessor.TickType; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.software.os.FileSystem; +import oshi.software.os.OSFileStore; +import oshi.software.os.OperatingSystem; +import oshi.util.Util; + +/** + * 服务器相关信息 + * + * @author ruoyi + */ +public class Server +{ + private static final int OSHI_WAIT_SECOND = 1000; + + /** + * CPU相关信息 + */ + private Cpu cpu = new Cpu(); + + /** + * 內存相关信息 + */ + private Mem mem = new Mem(); + + /** + * JVM相关信息 + */ + private Jvm jvm = new Jvm(); + + /** + * 服务器相关信息 + */ + private Sys sys = new Sys(); + + /** + * 磁盘相关信息 + */ + private List sysFiles = new LinkedList(); + + public Cpu getCpu() + { + return cpu; + } + + public void setCpu(Cpu cpu) + { + this.cpu = cpu; + } + + public Mem getMem() + { + return mem; + } + + public void setMem(Mem mem) + { + this.mem = mem; + } + + public Jvm getJvm() + { + return jvm; + } + + public void setJvm(Jvm jvm) + { + this.jvm = jvm; + } + + public Sys getSys() + { + return sys; + } + + public void setSys(Sys sys) + { + this.sys = sys; + } + + public List getSysFiles() + { + return sysFiles; + } + + public void setSysFiles(List sysFiles) + { + this.sysFiles = sysFiles; + } + + public void copyTo() throws Exception + { + SystemInfo si = new SystemInfo(); + HardwareAbstractionLayer hal = si.getHardware(); + + setCpuInfo(hal.getProcessor()); + + setMemInfo(hal.getMemory()); + + setSysInfo(); + + setJvmInfo(); + + setSysFiles(si.getOperatingSystem()); + } + + /** + * 设置CPU信息 + */ + private void setCpuInfo(CentralProcessor processor) + { + // CPU信息 + long[] prevTicks = processor.getSystemCpuLoadTicks(); + Util.sleep(OSHI_WAIT_SECOND); + long[] ticks = processor.getSystemCpuLoadTicks(); + long nice = ticks[TickType.NICE.getIndex()] - prevTicks[TickType.NICE.getIndex()]; + long irq = ticks[TickType.IRQ.getIndex()] - prevTicks[TickType.IRQ.getIndex()]; + long softirq = ticks[TickType.SOFTIRQ.getIndex()] - prevTicks[TickType.SOFTIRQ.getIndex()]; + long steal = ticks[TickType.STEAL.getIndex()] - prevTicks[TickType.STEAL.getIndex()]; + long cSys = ticks[TickType.SYSTEM.getIndex()] - prevTicks[TickType.SYSTEM.getIndex()]; + long user = ticks[TickType.USER.getIndex()] - prevTicks[TickType.USER.getIndex()]; + long iowait = ticks[TickType.IOWAIT.getIndex()] - prevTicks[TickType.IOWAIT.getIndex()]; + long idle = ticks[TickType.IDLE.getIndex()] - prevTicks[TickType.IDLE.getIndex()]; + long totalCpu = user + nice + cSys + idle + iowait + irq + softirq + steal; + cpu.setCpuNum(processor.getLogicalProcessorCount()); + cpu.setTotal(totalCpu); + cpu.setSys(cSys); + cpu.setUsed(user); + cpu.setWait(iowait); + cpu.setFree(idle); + } + + /** + * 设置内存信息 + */ + private void setMemInfo(GlobalMemory memory) + { + mem.setTotal(memory.getTotal()); + mem.setUsed(memory.getTotal() - memory.getAvailable()); + mem.setFree(memory.getAvailable()); + } + + /** + * 设置服务器信息 + */ + private void setSysInfo() + { + Properties props = System.getProperties(); + sys.setComputerName(IpUtils.getHostName()); + sys.setComputerIp(IpUtils.getHostIp()); + sys.setOsName(props.getProperty("os.name")); + sys.setOsArch(props.getProperty("os.arch")); + sys.setUserDir(props.getProperty("user.dir")); + } + + /** + * 设置Java虚拟机 + */ + private void setJvmInfo() throws UnknownHostException + { + Properties props = System.getProperties(); + jvm.setTotal(Runtime.getRuntime().totalMemory()); + jvm.setMax(Runtime.getRuntime().maxMemory()); + jvm.setFree(Runtime.getRuntime().freeMemory()); + jvm.setVersion(props.getProperty("java.version")); + jvm.setHome(props.getProperty("java.home")); + } + + /** + * 设置磁盘信息 + */ + private void setSysFiles(OperatingSystem os) + { + FileSystem fileSystem = os.getFileSystem(); + List fsArray = fileSystem.getFileStores(); + for (OSFileStore fs : fsArray) + { + long free = fs.getUsableSpace(); + long total = fs.getTotalSpace(); + long used = total - free; + SysFile sysFile = new SysFile(); + sysFile.setDirName(fs.getMount()); + sysFile.setSysTypeName(fs.getType()); + sysFile.setTypeName(fs.getName()); + sysFile.setTotal(convertFileSize(total)); + sysFile.setFree(convertFileSize(free)); + sysFile.setUsed(convertFileSize(used)); + sysFile.setUsage(Arith.mul(Arith.div(used, total, 4), 100)); + sysFiles.add(sysFile); + } + } + + /** + * 字节转换 + * + * @param size 字节大小 + * @return 转换后值 + */ + public String convertFileSize(long size) + { + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + if (size >= gb) + { + return String.format("%.1f GB", (float) size / gb); + } + else if (size >= mb) + { + float f = (float) size / mb; + return String.format(f > 100 ? "%.0f MB" : "%.1f MB", f); + } + else if (size >= kb) + { + float f = (float) size / kb; + return String.format(f > 100 ? "%.0f KB" : "%.1f KB", f); + } + else + { + return String.format("%d B", size); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java new file mode 100644 index 0000000..a13a66c --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Cpu.java @@ -0,0 +1,101 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * CPU相关信息 + * + * @author ruoyi + */ +public class Cpu +{ + /** + * 核心数 + */ + private int cpuNum; + + /** + * CPU总的使用率 + */ + private double total; + + /** + * CPU系统使用率 + */ + private double sys; + + /** + * CPU用户使用率 + */ + private double used; + + /** + * CPU当前等待率 + */ + private double wait; + + /** + * CPU当前空闲率 + */ + private double free; + + public int getCpuNum() + { + return cpuNum; + } + + public void setCpuNum(int cpuNum) + { + this.cpuNum = cpuNum; + } + + public double getTotal() + { + return Arith.round(Arith.mul(total, 100), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getSys() + { + return Arith.round(Arith.mul(sys / total, 100), 2); + } + + public void setSys(double sys) + { + this.sys = sys; + } + + public double getUsed() + { + return Arith.round(Arith.mul(used / total, 100), 2); + } + + public void setUsed(double used) + { + this.used = used; + } + + public double getWait() + { + return Arith.round(Arith.mul(wait / total, 100), 2); + } + + public void setWait(double wait) + { + this.wait = wait; + } + + public double getFree() + { + return Arith.round(Arith.mul(free / total, 100), 2); + } + + public void setFree(double free) + { + this.free = free; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java new file mode 100644 index 0000000..1fdc6ac --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Jvm.java @@ -0,0 +1,130 @@ +package com.ruoyi.framework.web.domain.server; + +import java.lang.management.ManagementFactory; +import com.ruoyi.common.utils.Arith; +import com.ruoyi.common.utils.DateUtils; + +/** + * JVM相关信息 + * + * @author ruoyi + */ +public class Jvm +{ + /** + * 当前JVM占用的内存总数(M) + */ + private double total; + + /** + * JVM最大可用内存总数(M) + */ + private double max; + + /** + * JVM空闲内存(M) + */ + private double free; + + /** + * JDK版本 + */ + private String version; + + /** + * JDK路径 + */ + private String home; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024), 2); + } + + public void setTotal(double total) + { + this.total = total; + } + + public double getMax() + { + return Arith.div(max, (1024 * 1024), 2); + } + + public void setMax(double max) + { + this.max = max; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024), 2); + } + + public void setFree(double free) + { + this.free = free; + } + + public double getUsed() + { + return Arith.div(total - free, (1024 * 1024), 2); + } + + public double getUsage() + { + return Arith.mul(Arith.div(total - free, total, 4), 100); + } + + /** + * 获取JDK名称 + */ + public String getName() + { + return ManagementFactory.getRuntimeMXBean().getVmName(); + } + + public String getVersion() + { + return version; + } + + public void setVersion(String version) + { + this.version = version; + } + + public String getHome() + { + return home; + } + + public void setHome(String home) + { + this.home = home; + } + + /** + * JDK启动时间 + */ + public String getStartTime() + { + return DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, DateUtils.getServerStartDate()); + } + + /** + * JDK运行时间 + */ + public String getRunTime() + { + return DateUtils.timeDistance(DateUtils.getNowDate(), DateUtils.getServerStartDate()); + } + + /** + * 运行参数 + */ + public String getInputArgs() + { + return ManagementFactory.getRuntimeMXBean().getInputArguments().toString(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java new file mode 100644 index 0000000..13eec52 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Mem.java @@ -0,0 +1,61 @@ +package com.ruoyi.framework.web.domain.server; + +import com.ruoyi.common.utils.Arith; + +/** + * 內存相关信息 + * + * @author ruoyi + */ +public class Mem +{ + /** + * 内存总量 + */ + private double total; + + /** + * 已用内存 + */ + private double used; + + /** + * 剩余内存 + */ + private double free; + + public double getTotal() + { + return Arith.div(total, (1024 * 1024 * 1024), 2); + } + + public void setTotal(long total) + { + this.total = total; + } + + public double getUsed() + { + return Arith.div(used, (1024 * 1024 * 1024), 2); + } + + public void setUsed(long used) + { + this.used = used; + } + + public double getFree() + { + return Arith.div(free, (1024 * 1024 * 1024), 2); + } + + public void setFree(long free) + { + this.free = free; + } + + public double getUsage() + { + return Arith.mul(Arith.div(used, total, 4), 100); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java new file mode 100644 index 0000000..45d64d9 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/Sys.java @@ -0,0 +1,84 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统相关信息 + * + * @author ruoyi + */ +public class Sys +{ + /** + * 服务器名称 + */ + private String computerName; + + /** + * 服务器Ip + */ + private String computerIp; + + /** + * 项目路径 + */ + private String userDir; + + /** + * 操作系统 + */ + private String osName; + + /** + * 系统架构 + */ + private String osArch; + + public String getComputerName() + { + return computerName; + } + + public void setComputerName(String computerName) + { + this.computerName = computerName; + } + + public String getComputerIp() + { + return computerIp; + } + + public void setComputerIp(String computerIp) + { + this.computerIp = computerIp; + } + + public String getUserDir() + { + return userDir; + } + + public void setUserDir(String userDir) + { + this.userDir = userDir; + } + + public String getOsName() + { + return osName; + } + + public void setOsName(String osName) + { + this.osName = osName; + } + + public String getOsArch() + { + return osArch; + } + + public void setOsArch(String osArch) + { + this.osArch = osArch; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java new file mode 100644 index 0000000..1320cde --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/domain/server/SysFile.java @@ -0,0 +1,114 @@ +package com.ruoyi.framework.web.domain.server; + +/** + * 系统文件相关信息 + * + * @author ruoyi + */ +public class SysFile +{ + /** + * 盘符路径 + */ + private String dirName; + + /** + * 盘符类型 + */ + private String sysTypeName; + + /** + * 文件类型 + */ + private String typeName; + + /** + * 总大小 + */ + private String total; + + /** + * 剩余大小 + */ + private String free; + + /** + * 已经使用量 + */ + private String used; + + /** + * 资源的使用率 + */ + private double usage; + + public String getDirName() + { + return dirName; + } + + public void setDirName(String dirName) + { + this.dirName = dirName; + } + + public String getSysTypeName() + { + return sysTypeName; + } + + public void setSysTypeName(String sysTypeName) + { + this.sysTypeName = sysTypeName; + } + + public String getTypeName() + { + return typeName; + } + + public void setTypeName(String typeName) + { + this.typeName = typeName; + } + + public String getTotal() + { + return total; + } + + public void setTotal(String total) + { + this.total = total; + } + + public String getFree() + { + return free; + } + + public void setFree(String free) + { + this.free = free; + } + + public String getUsed() + { + return used; + } + + public void setUsed(String used) + { + this.used = used; + } + + public double getUsage() + { + return usage; + } + + public void setUsage(double usage) + { + this.usage = usage; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..3cb17d6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/exception/GlobalExceptionHandler.java @@ -0,0 +1,145 @@ +package com.ruoyi.framework.web.exception; + +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindException; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingPathVariableException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; +import com.ruoyi.common.constant.HttpStatus; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.DemoModeException; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.html.EscapeUtil; + +/** + * 全局异常处理器 + * + * @author ruoyi + */ +@RestControllerAdvice +public class GlobalExceptionHandler +{ + private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + /** + * 权限校验异常 + */ + @ExceptionHandler(AccessDeniedException.class) + public AjaxResult handleAccessDeniedException(AccessDeniedException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',权限校验失败'{}'", requestURI, e.getMessage()); + return AjaxResult.error(HttpStatus.FORBIDDEN, "没有权限,请联系管理员授权"); + } + + /** + * 请求方式不支持 + */ + @ExceptionHandler(HttpRequestMethodNotSupportedException.class) + public AjaxResult handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException e, + HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',不支持'{}'请求", requestURI, e.getMethod()); + return AjaxResult.error(e.getMessage()); + } + + /** + * 业务异常 + */ + @ExceptionHandler(ServiceException.class) + public AjaxResult handleServiceException(ServiceException e, HttpServletRequest request) + { + log.error(e.getMessage(), e); + Integer code = e.getCode(); + return StringUtils.isNotNull(code) ? AjaxResult.error(code, e.getMessage()) : AjaxResult.error(e.getMessage()); + } + + /** + * 请求路径中缺少必需的路径变量 + */ + @ExceptionHandler(MissingPathVariableException.class) + public AjaxResult handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName())); + } + + /** + * 请求参数类型不匹配 + */ + @ExceptionHandler(MethodArgumentTypeMismatchException.class) + public AjaxResult handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + String value = Convert.toStr(e.getValue()); + if (StringUtils.isNotEmpty(value)) + { + value = EscapeUtil.clean(value); + } + log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), value)); + } + + /** + * 拦截未知的运行时异常 + */ + @ExceptionHandler(RuntimeException.class) + public AjaxResult handleRuntimeException(RuntimeException e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生未知异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 系统异常 + */ + @ExceptionHandler(Exception.class) + public AjaxResult handleException(Exception e, HttpServletRequest request) + { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',发生系统异常.", requestURI, e); + return AjaxResult.error(e.getMessage()); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(BindException.class) + public AjaxResult handleBindException(BindException e) + { + log.error(e.getMessage(), e); + String message = e.getAllErrors().get(0).getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 自定义验证异常 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) + { + log.error(e.getMessage(), e); + String message = e.getBindingResult().getFieldError().getDefaultMessage(); + return AjaxResult.error(message); + } + + /** + * 演示模式异常 + */ + @ExceptionHandler(DemoModeException.class) + public AjaxResult handleDemoModeException(DemoModeException e) + { + return AjaxResult.error("演示模式,不允许操作"); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java new file mode 100644 index 0000000..07d259a --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/PermissionService.java @@ -0,0 +1,159 @@ +package com.ruoyi.framework.web.service; + +import java.util.Set; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.security.context.PermissionContextHolder; + +/** + * RuoYi首创 自定义权限实现,ss取自SpringSecurity首字母 + * + * @author ruoyi + */ +@Service("ss") +public class PermissionService +{ + /** + * 验证用户是否具备某权限 + * + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + public boolean hasPermi(String permission) + { + if (StringUtils.isEmpty(permission)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permission); + return hasPermissions(loginUser.getPermissions(), permission); + } + + /** + * 验证用户是否不具备某权限,与 hasPermi逻辑相反 + * + * @param permission 权限字符串 + * @return 用户是否不具备某权限 + */ + public boolean lacksPermi(String permission) + { + return hasPermi(permission) != true; + } + + /** + * 验证用户是否具有以下任意一个权限 + * + * @param permissions 以 PERMISSION_DELIMETER 为分隔符的权限列表 + * @return 用户是否具有以下任意一个权限 + */ + public boolean hasAnyPermi(String permissions) + { + if (StringUtils.isEmpty(permissions)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getPermissions())) + { + return false; + } + PermissionContextHolder.setContext(permissions); + Set authorities = loginUser.getPermissions(); + for (String permission : permissions.split(Constants.PERMISSION_DELIMETER)) + { + if (permission != null && hasPermissions(authorities, permission)) + { + return true; + } + } + return false; + } + + /** + * 判断用户是否拥有某个角色 + * + * @param role 角色字符串 + * @return 用户是否具备某角色 + */ + public boolean hasRole(String role) + { + if (StringUtils.isEmpty(role)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (SysRole sysRole : loginUser.getUser().getRoles()) + { + String roleKey = sysRole.getRoleKey(); + if (Constants.SUPER_ADMIN.equals(roleKey) || roleKey.equals(StringUtils.trim(role))) + { + return true; + } + } + return false; + } + + /** + * 验证用户是否不具备某角色,与 isRole逻辑相反。 + * + * @param role 角色名称 + * @return 用户是否不具备某角色 + */ + public boolean lacksRole(String role) + { + return hasRole(role) != true; + } + + /** + * 验证用户是否具有以下任意一个角色 + * + * @param roles 以 ROLE_NAMES_DELIMETER 为分隔符的角色列表 + * @return 用户是否具有以下任意一个角色 + */ + public boolean hasAnyRoles(String roles) + { + if (StringUtils.isEmpty(roles)) + { + return false; + } + LoginUser loginUser = SecurityUtils.getLoginUser(); + if (StringUtils.isNull(loginUser) || CollectionUtils.isEmpty(loginUser.getUser().getRoles())) + { + return false; + } + for (String role : roles.split(Constants.ROLE_DELIMETER)) + { + if (hasRole(role)) + { + return true; + } + } + return false; + } + + /** + * 判断是否包含权限 + * + * @param permissions 权限列表 + * @param permission 权限字符串 + * @return 用户是否具备某权限 + */ + private boolean hasPermissions(Set permissions, String permission) + { + return permissions.contains(Constants.ALL_PERMISSION) || permissions.contains(StringUtils.trim(permission)); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java new file mode 100644 index 0000000..fe16427 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java @@ -0,0 +1,181 @@ +package com.ruoyi.framework.web.service; + +import javax.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.exception.user.BlackListException; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.exception.user.UserNotExistsException; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 登录校验方法 + * + * @author ruoyi + */ +@Component +public class SysLoginService +{ + @Autowired + private TokenService tokenService; + + @Resource + private AuthenticationManager authenticationManager; + + @Autowired + private RedisCache redisCache; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + /** + * 登录验证 + * + * @param username 用户名 + * @param password 密码 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public String login(String username, String password, String code, String uuid) + { + // 验证码校验 + validateCaptcha(username, code, uuid); + // 登录前置校验 + loginPreCheck(username, password); + // 用户验证 + Authentication authentication = null; + try + { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); + AuthenticationContextHolder.setContext(authenticationToken); + // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername + authentication = authenticationManager.authenticate(authenticationToken); + } + catch (Exception e) + { + if (e instanceof BadCredentialsException) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage())); + throw new ServiceException(e.getMessage()); + } + } + finally + { + AuthenticationContextHolder.clearContext(); + } + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success"))); + LoginUser loginUser = (LoginUser) authentication.getPrincipal(); + recordLoginInfo(loginUser.getUserId()); + // 生成token + return tokenService.createToken(loginUser); + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + if (captcha == null) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire"))); + throw new CaptchaExpireException(); + } + redisCache.deleteObject(verifyKey); + if (!code.equalsIgnoreCase(captcha)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error"))); + throw new CaptchaException(); + } + } + } + + /** + * 登录前置校验 + * @param username 用户名 + * @param password 用户密码 + */ + public void loginPreCheck(String username, String password) + { + // 用户名或密码为空 错误 + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null"))); + throw new UserNotExistsException(); + } + // 密码如果不在指定范围内 错误 + if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // 用户名不在指定范围内 错误 + if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match"))); + throw new UserPasswordNotMatchException(); + } + // IP黑名单校验 + String blackStr = configService.selectConfigByKey("sys.login.blackIPList"); + if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked"))); + throw new BlackListException(); + } + } + + /** + * 记录登录信息 + * + * @param userId 用户ID + */ + public void recordLoginInfo(Long userId) + { + SysUser sysUser = new SysUser(); + sysUser.setUserId(userId); + sysUser.setLoginIp(IpUtils.getIpAddr()); + sysUser.setLoginDate(DateUtils.getNowDate()); + userService.updateUserProfile(sysUser); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java new file mode 100644 index 0000000..6728c7b --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPasswordService.java @@ -0,0 +1,86 @@ +package com.ruoyi.framework.web.service; + +import java.util.concurrent.TimeUnit; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.UserPasswordNotMatchException; +import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.framework.security.context.AuthenticationContextHolder; + +/** + * 登录密码方法 + * + * @author ruoyi + */ +@Component +public class SysPasswordService +{ + @Autowired + private RedisCache redisCache; + + @Value(value = "${user.password.maxRetryCount}") + private int maxRetryCount; + + @Value(value = "${user.password.lockTime}") + private int lockTime; + + /** + * 登录账户密码错误次数缓存键名 + * + * @param username 用户名 + * @return 缓存键key + */ + private String getCacheKey(String username) + { + return CacheConstants.PWD_ERR_CNT_KEY + username; + } + + public void validate(SysUser user) + { + Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext(); + String username = usernamePasswordAuthenticationToken.getName(); + String password = usernamePasswordAuthenticationToken.getCredentials().toString(); + + Integer retryCount = redisCache.getCacheObject(getCacheKey(username)); + + if (retryCount == null) + { + retryCount = 0; + } + + if (retryCount >= Integer.valueOf(maxRetryCount).intValue()) + { + throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime); + } + + if (!matches(user, password)) + { + retryCount = retryCount + 1; + redisCache.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES); + throw new UserPasswordNotMatchException(); + } + else + { + clearLoginRecordCache(username); + } + } + + public boolean matches(SysUser user, String rawPassword) + { + return SecurityUtils.matchesPassword(rawPassword, user.getPassword()); + } + + public void clearLoginRecordCache(String loginName) + { + if (redisCache.hasKey(getCacheKey(loginName))) + { + redisCache.deleteObject(getCacheKey(loginName)); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java new file mode 100644 index 0000000..c4d0fa5 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysPermissionService.java @@ -0,0 +1,88 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysMenuService; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 用户权限处理 + * + * @author ruoyi + */ +@Component +public class SysPermissionService +{ + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysMenuService menuService; + + /** + * 获取角色数据权限 + * + * @param user 用户信息 + * @return 角色权限信息 + */ + public Set getRolePermission(SysUser user) + { + Set roles = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + roles.add("admin"); + } + else + { + roles.addAll(roleService.selectRolePermissionByUserId(user.getUserId())); + } + return roles; + } + + /** + * 获取菜单数据权限 + * + * @param user 用户信息 + * @return 菜单权限信息 + */ + public Set getMenuPermission(SysUser user) + { + Set perms = new HashSet(); + // 管理员拥有所有权限 + if (user.isAdmin()) + { + perms.add("*:*:*"); + } + else + { + List roles = user.getRoles(); + if (!CollectionUtils.isEmpty(roles)) + { + // 多角色设置permissions属性,以便数据权限匹配权限 + for (SysRole role : roles) + { + if (StringUtils.equals(role.getStatus(), UserConstants.ROLE_NORMAL) && !role.isAdmin()) + { + Set rolePerms = menuService.selectMenuPermsByRoleId(role.getRoleId()); + role.setPermissions(rolePerms); + perms.addAll(rolePerms); + } + } + } + else + { + perms.addAll(menuService.selectMenuPermsByUserId(user.getUserId())); + } + } + return perms; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java new file mode 100644 index 0000000..f2afe31 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysRegisterService.java @@ -0,0 +1,115 @@ +package com.ruoyi.framework.web.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.RegisterBody; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.exception.user.CaptchaException; +import com.ruoyi.common.exception.user.CaptchaExpireException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.manager.AsyncManager; +import com.ruoyi.framework.manager.factory.AsyncFactory; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 注册校验方法 + * + * @author ruoyi + */ +@Component +public class SysRegisterService +{ + @Autowired + private ISysUserService userService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private RedisCache redisCache; + + /** + * 注册 + */ + public String register(RegisterBody registerBody) + { + String msg = "", username = registerBody.getUsername(), password = registerBody.getPassword(); + SysUser sysUser = new SysUser(); + sysUser.setUserName(username); + + // 验证码开关 + boolean captchaEnabled = configService.selectCaptchaEnabled(); + if (captchaEnabled) + { + validateCaptcha(username, registerBody.getCode(), registerBody.getUuid()); + } + + if (StringUtils.isEmpty(username)) + { + msg = "用户名不能为空"; + } + else if (StringUtils.isEmpty(password)) + { + msg = "用户密码不能为空"; + } + else if (username.length() < UserConstants.USERNAME_MIN_LENGTH + || username.length() > UserConstants.USERNAME_MAX_LENGTH) + { + msg = "账户长度必须在2到20个字符之间"; + } + else if (password.length() < UserConstants.PASSWORD_MIN_LENGTH + || password.length() > UserConstants.PASSWORD_MAX_LENGTH) + { + msg = "密码长度必须在5到20个字符之间"; + } + else if (!userService.checkUserNameUnique(sysUser)) + { + msg = "保存用户'" + username + "'失败,注册账号已存在"; + } + else + { + sysUser.setNickName(username); + sysUser.setPassword(SecurityUtils.encryptPassword(password)); + boolean regFlag = userService.registerUser(sysUser); + if (!regFlag) + { + msg = "注册失败,请联系系统管理人员"; + } + else + { + AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.REGISTER, MessageUtils.message("user.register.success"))); + } + } + return msg; + } + + /** + * 校验验证码 + * + * @param username 用户名 + * @param code 验证码 + * @param uuid 唯一标识 + * @return 结果 + */ + public void validateCaptcha(String username, String code, String uuid) + { + String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, ""); + String captcha = redisCache.getCacheObject(verifyKey); + redisCache.deleteObject(verifyKey); + if (captcha == null) + { + throw new CaptchaExpireException(); + } + if (!code.equalsIgnoreCase(captcha)) + { + throw new CaptchaException(); + } + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java new file mode 100644 index 0000000..680cab6 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/TokenService.java @@ -0,0 +1,232 @@ +package com.ruoyi.framework.web.service; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.ip.AddressUtils; +import com.ruoyi.common.utils.ip.IpUtils; +import com.ruoyi.common.utils.uuid.IdUtils; +import eu.bitwalker.useragentutils.UserAgent; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; + +/** + * token验证处理 + * + * @author ruoyi + */ +@Component +public class TokenService +{ + private static final Logger log = LoggerFactory.getLogger(TokenService.class); + + // 令牌自定义标识 + @Value("${token.header}") + private String header; + + // 令牌秘钥 + @Value("${token.secret}") + private String secret; + + // 令牌有效期(默认30分钟) + @Value("${token.expireTime}") + private int expireTime; + + protected static final long MILLIS_SECOND = 1000; + + protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND; + + private static final Long MILLIS_MINUTE_TWENTY = 20 * 60 * 1000L; + + @Autowired + private RedisCache redisCache; + + /** + * 获取用户身份信息 + * + * @return 用户信息 + */ + public LoginUser getLoginUser(HttpServletRequest request) + { + // 获取请求携带的令牌 + String token = getToken(request); + if (StringUtils.isNotEmpty(token)) + { + try + { + Claims claims = parseToken(token); + // 解析对应的权限以及用户信息 + String uuid = (String) claims.get(Constants.LOGIN_USER_KEY); + String userKey = getTokenKey(uuid); + LoginUser user = redisCache.getCacheObject(userKey); + return user; + } + catch (Exception e) + { + log.error("获取用户信息异常'{}'", e.getMessage()); + } + } + return null; + } + + /** + * 设置用户身份信息 + */ + public void setLoginUser(LoginUser loginUser) + { + if (StringUtils.isNotNull(loginUser) && StringUtils.isNotEmpty(loginUser.getToken())) + { + refreshToken(loginUser); + } + } + + /** + * 删除用户身份信息 + */ + public void delLoginUser(String token) + { + if (StringUtils.isNotEmpty(token)) + { + String userKey = getTokenKey(token); + redisCache.deleteObject(userKey); + } + } + + /** + * 创建令牌 + * + * @param loginUser 用户信息 + * @return 令牌 + */ + public String createToken(LoginUser loginUser) + { + String token = IdUtils.fastUUID(); + loginUser.setToken(token); + setUserAgent(loginUser); + refreshToken(loginUser); + + Map claims = new HashMap<>(); + claims.put(Constants.LOGIN_USER_KEY, token); + claims.put(Constants.JWT_USERNAME, loginUser.getUsername()); + return createToken(claims); + } + + /** + * 验证令牌有效期,相差不足20分钟,自动刷新缓存 + * + * @param loginUser 登录信息 + * @return 令牌 + */ + public void verifyToken(LoginUser loginUser) + { + long expireTime = loginUser.getExpireTime(); + long currentTime = System.currentTimeMillis(); + if (expireTime - currentTime <= MILLIS_MINUTE_TWENTY) + { + refreshToken(loginUser); + } + } + + /** + * 刷新令牌有效期 + * + * @param loginUser 登录信息 + */ + public void refreshToken(LoginUser loginUser) + { + loginUser.setLoginTime(System.currentTimeMillis()); + loginUser.setExpireTime(loginUser.getLoginTime() + expireTime * MILLIS_MINUTE); + // 根据uuid将loginUser缓存 + String userKey = getTokenKey(loginUser.getToken()); + redisCache.setCacheObject(userKey, loginUser, expireTime, TimeUnit.MINUTES); + } + + /** + * 设置用户代理信息 + * + * @param loginUser 登录信息 + */ + public void setUserAgent(LoginUser loginUser) + { + UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = IpUtils.getIpAddr(); + loginUser.setIpaddr(ip); + loginUser.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + loginUser.setBrowser(userAgent.getBrowser().getName()); + loginUser.setOs(userAgent.getOperatingSystem().getName()); + } + + /** + * 从数据声明生成令牌 + * + * @param claims 数据声明 + * @return 令牌 + */ + private String createToken(Map claims) + { + String token = Jwts.builder() + .setClaims(claims) + .signWith(SignatureAlgorithm.HS512, secret).compact(); + return token; + } + + /** + * 从令牌中获取数据声明 + * + * @param token 令牌 + * @return 数据声明 + */ + private Claims parseToken(String token) + { + return Jwts.parser() + .setSigningKey(secret) + .parseClaimsJws(token) + .getBody(); + } + + /** + * 从令牌中获取用户名 + * + * @param token 令牌 + * @return 用户名 + */ + public String getUsernameFromToken(String token) + { + Claims claims = parseToken(token); + return claims.getSubject(); + } + + /** + * 获取请求token + * + * @param request + * @return token + */ + private String getToken(HttpServletRequest request) + { + String token = request.getHeader(header); + if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX)) + { + token = token.replace(Constants.TOKEN_PREFIX, ""); + } + return token; + } + + private String getTokenKey(String uuid) + { + return CacheConstants.LOGIN_TOKEN_KEY + uuid; + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java new file mode 100644 index 0000000..5dcdf90 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/UserDetailsServiceImpl.java @@ -0,0 +1,66 @@ +package com.ruoyi.framework.web.service; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.enums.UserStatus; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.MessageUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户验证处理 + * + * @author ruoyi + */ +@Service +public class UserDetailsServiceImpl implements UserDetailsService +{ + private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private SysPermissionService permissionService; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException + { + SysUser user = userService.selectUserByUserName(username); + if (StringUtils.isNull(user)) + { + log.info("登录用户:{} 不存在.", username); + throw new ServiceException(MessageUtils.message("user.not.exists")); + } + else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) + { + log.info("登录用户:{} 已被删除.", username); + throw new ServiceException(MessageUtils.message("user.password.delete")); + } + else if (UserStatus.DISABLE.getCode().equals(user.getStatus())) + { + log.info("登录用户:{} 已被停用.", username); + throw new ServiceException(MessageUtils.message("user.blocked")); + } + + passwordService.validate(user); + + return createLoginUser(user); + } + + public UserDetails createLoginUser(SysUser user) + { + return new LoginUser(user.getUserId(), user.getDeptId(), user, permissionService.getMenuPermission(user)); + } +} diff --git a/ruoyi-generator/pom.xml b/ruoyi-generator/pom.xml new file mode 100644 index 0000000..188cd90 --- /dev/null +++ b/ruoyi-generator/pom.xml @@ -0,0 +1,40 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + + ruoyi-generator + + + generator代码生成 + + + + + + + org.apache.velocity + velocity-engine-core + + + + + com.ruoyi + ruoyi-common + + + + + com.alibaba + druid-spring-boot-starter + + + + + \ No newline at end of file diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java new file mode 100644 index 0000000..c01857c --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/config/GenConfig.java @@ -0,0 +1,87 @@ +package com.ruoyi.generator.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * 读取代码生成相关配置 + * + * @author ruoyi + */ +@Component +@ConfigurationProperties(prefix = "gen") +@PropertySource(value = { "classpath:generator.yml" }) +public class GenConfig +{ + /** 作者 */ + public static String author; + + /** 生成包路径 */ + public static String packageName; + + /** 自动去除表前缀 */ + public static boolean autoRemovePre; + + /** 表前缀 */ + public static String tablePrefix; + + /** 是否允许生成文件覆盖到本地(自定义路径) */ + public static boolean allowOverwrite; + + public static String getAuthor() + { + return author; + } + + @Value("${author}") + public void setAuthor(String author) + { + GenConfig.author = author; + } + + public static String getPackageName() + { + return packageName; + } + + @Value("${packageName}") + public void setPackageName(String packageName) + { + GenConfig.packageName = packageName; + } + + public static boolean getAutoRemovePre() + { + return autoRemovePre; + } + + @Value("${autoRemovePre}") + public void setAutoRemovePre(boolean autoRemovePre) + { + GenConfig.autoRemovePre = autoRemovePre; + } + + public static String getTablePrefix() + { + return tablePrefix; + } + + @Value("${tablePrefix}") + public void setTablePrefix(String tablePrefix) + { + GenConfig.tablePrefix = tablePrefix; + } + + public static boolean isAllowOverwrite() + { + return allowOverwrite; + } + + @Value("${allowOverwrite}") + public void setAllowOverwrite(boolean allowOverwrite) + { + GenConfig.allowOverwrite = allowOverwrite; + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java new file mode 100644 index 0000000..a05df8a --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/controller/GenController.java @@ -0,0 +1,263 @@ +package com.ruoyi.generator.controller; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.alibaba.druid.DbType; +import com.alibaba.druid.sql.SQLUtils; +import com.alibaba.druid.sql.ast.SQLStatement; +import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlCreateTableStatement; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.sql.SqlUtil; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.service.IGenTableColumnService; +import com.ruoyi.generator.service.IGenTableService; + +/** + * 代码生成 操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/tool/gen") +public class GenController extends BaseController +{ + @Autowired + private IGenTableService genTableService; + + @Autowired + private IGenTableColumnService genTableColumnService; + + /** + * 查询代码生成列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/list") + public TableDataInfo genList(GenTable genTable) + { + startPage(); + List list = genTableService.selectGenTableList(genTable); + return getDataTable(list); + } + + /** + * 获取代码生成信息 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:query')") + @GetMapping(value = "/{tableId}") + public AjaxResult getInfo(@PathVariable Long tableId) + { + GenTable table = genTableService.selectGenTableById(tableId); + List tables = genTableService.selectGenTableAll(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + Map map = new HashMap(); + map.put("info", table); + map.put("rows", list); + map.put("tables", tables); + return success(map); + } + + /** + * 查询数据库列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping("/db/list") + public TableDataInfo dataList(GenTable genTable) + { + startPage(); + List list = genTableService.selectDbTableList(genTable); + return getDataTable(list); + } + + /** + * 查询数据表字段列表 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:list')") + @GetMapping(value = "/column/{tableId}") + public TableDataInfo columnList(Long tableId) + { + TableDataInfo dataInfo = new TableDataInfo(); + List list = genTableColumnService.selectGenTableColumnListByTableId(tableId); + dataInfo.setRows(list); + dataInfo.setTotal(list.size()); + return dataInfo; + } + + /** + * 导入表结构(保存) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:import')") + @Log(title = "代码生成", businessType = BusinessType.IMPORT) + @PostMapping("/importTable") + public AjaxResult importTableSave(String tables) + { + String[] tableNames = Convert.toStrArray(tables); + // 查询表信息 + List tableList = genTableService.selectDbTableListByNames(tableNames); + genTableService.importGenTable(tableList, SecurityUtils.getUsername()); + return success(); + } + + /** + * 创建表结构(保存) + */ + @PreAuthorize("@ss.hasRole('admin')") + @Log(title = "创建表", businessType = BusinessType.OTHER) + @PostMapping("/createTable") + public AjaxResult createTableSave(String sql) + { + try + { + SqlUtil.filterKeyword(sql); + List sqlStatements = SQLUtils.parseStatements(sql, DbType.mysql); + List tableNames = new ArrayList<>(); + for (SQLStatement sqlStatement : sqlStatements) + { + if (sqlStatement instanceof MySqlCreateTableStatement) + { + MySqlCreateTableStatement createTableStatement = (MySqlCreateTableStatement) sqlStatement; + if (genTableService.createTable(createTableStatement.toString())) + { + String tableName = createTableStatement.getTableName().replaceAll("`", ""); + tableNames.add(tableName); + } + } + } + List tableList = genTableService.selectDbTableListByNames(tableNames.toArray(new String[tableNames.size()])); + String operName = SecurityUtils.getUsername(); + genTableService.importGenTable(tableList, operName); + return AjaxResult.success(); + } + catch (Exception e) + { + logger.error(e.getMessage(), e); + return AjaxResult.error("创建表结构异常"); + } + } + + /** + * 修改保存代码生成业务 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult editSave(@Validated @RequestBody GenTable genTable) + { + genTableService.validateEdit(genTable); + genTableService.updateGenTable(genTable); + return success(); + } + + /** + * 删除代码生成 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:remove')") + @Log(title = "代码生成", businessType = BusinessType.DELETE) + @DeleteMapping("/{tableIds}") + public AjaxResult remove(@PathVariable Long[] tableIds) + { + genTableService.deleteGenTableByIds(tableIds); + return success(); + } + + /** + * 预览代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:preview')") + @GetMapping("/preview/{tableId}") + public AjaxResult preview(@PathVariable("tableId") Long tableId) throws IOException + { + Map dataMap = genTableService.previewCode(tableId); + return success(dataMap); + } + + /** + * 生成代码(下载方式) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/download/{tableName}") + public void download(HttpServletResponse response, @PathVariable("tableName") String tableName) throws IOException + { + byte[] data = genTableService.downloadCode(tableName); + genCode(response, data); + } + + /** + * 生成代码(自定义路径) + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/genCode/{tableName}") + public AjaxResult genCode(@PathVariable("tableName") String tableName) + { + if (!GenConfig.isAllowOverwrite()) + { + return AjaxResult.error("【系统预设】不允许生成文件覆盖到本地"); + } + genTableService.generatorCode(tableName); + return success(); + } + + /** + * 同步数据库 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:edit')") + @Log(title = "代码生成", businessType = BusinessType.UPDATE) + @GetMapping("/synchDb/{tableName}") + public AjaxResult synchDb(@PathVariable("tableName") String tableName) + { + genTableService.synchDb(tableName); + return success(); + } + + /** + * 批量生成代码 + */ + @PreAuthorize("@ss.hasPermi('tool:gen:code')") + @Log(title = "代码生成", businessType = BusinessType.GENCODE) + @GetMapping("/batchGenCode") + public void batchGenCode(HttpServletResponse response, String tables) throws IOException + { + String[] tableNames = Convert.toStrArray(tables); + byte[] data = genTableService.downloadCode(tableNames); + genCode(response, data); + } + + /** + * 生成zip文件 + */ + private void genCode(HttpServletResponse response, byte[] data) throws IOException + { + response.reset(); + response.addHeader("Access-Control-Allow-Origin", "*"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + response.setHeader("Content-Disposition", "attachment; filename=\"ruoyi.zip\""); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java new file mode 100644 index 0000000..db8ab72 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTable.java @@ -0,0 +1,390 @@ +package com.ruoyi.generator.domain; + +import java.util.List; +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.ArrayUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 业务表 gen_table + * + * @author ruoyi + */ +public class GenTable extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long tableId; + + /** 表名称 */ + @NotBlank(message = "表名称不能为空") + private String tableName; + + /** 表描述 */ + @NotBlank(message = "表描述不能为空") + private String tableComment; + + /** 关联父表的表名 */ + private String subTableName; + + /** 本表关联父表的外键名 */ + private String subTableFkName; + + /** 实体类名称(首字母大写) */ + @NotBlank(message = "实体类名称不能为空") + private String className; + + /** 使用的模板(crud单表操作 tree树表操作 sub主子表操作) */ + private String tplCategory; + + /** 前端类型(element-ui模版 element-plus模版) */ + private String tplWebType; + + /** 生成包路径 */ + @NotBlank(message = "生成包路径不能为空") + private String packageName; + + /** 生成模块名 */ + @NotBlank(message = "生成模块名不能为空") + private String moduleName; + + /** 生成业务名 */ + @NotBlank(message = "生成业务名不能为空") + private String businessName; + + /** 生成功能名 */ + @NotBlank(message = "生成功能名不能为空") + private String functionName; + + /** 生成作者 */ + @NotBlank(message = "作者不能为空") + private String functionAuthor; + + /** 生成代码方式(0zip压缩包 1自定义路径) */ + private String genType; + + /** 生成路径(不填默认项目路径) */ + private String genPath; + + /** 主键信息 */ + private GenTableColumn pkColumn; + + /** 子表信息 */ + private GenTable subTable; + + /** 表列信息 */ + @Valid + private List columns; + + /** 其它生成选项 */ + private String options; + + /** 树编码字段 */ + private String treeCode; + + /** 树父编码字段 */ + private String treeParentCode; + + /** 树名称字段 */ + private String treeName; + + /** 上级菜单ID字段 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long parentMenuId; + + /** 上级菜单名称字段 */ + private String parentMenuName; + + public Long getTableId() + { + return tableId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public String getTableName() + { + return tableName; + } + + public void setTableName(String tableName) + { + this.tableName = tableName; + } + + public String getTableComment() + { + return tableComment; + } + + public void setTableComment(String tableComment) + { + this.tableComment = tableComment; + } + + public String getSubTableName() + { + return subTableName; + } + + public void setSubTableName(String subTableName) + { + this.subTableName = subTableName; + } + + public String getSubTableFkName() + { + return subTableFkName; + } + + public void setSubTableFkName(String subTableFkName) + { + this.subTableFkName = subTableFkName; + } + + public String getClassName() + { + return className; + } + + public void setClassName(String className) + { + this.className = className; + } + + public String getTplCategory() + { + return tplCategory; + } + + public void setTplCategory(String tplCategory) + { + this.tplCategory = tplCategory; + } + + public String getTplWebType() + { + return tplWebType; + } + + public void setTplWebType(String tplWebType) + { + this.tplWebType = tplWebType; + } + + public String getPackageName() + { + return packageName; + } + + public void setPackageName(String packageName) + { + this.packageName = packageName; + } + + public String getModuleName() + { + return moduleName; + } + + public void setModuleName(String moduleName) + { + this.moduleName = moduleName; + } + + public String getBusinessName() + { + return businessName; + } + + public void setBusinessName(String businessName) + { + this.businessName = businessName; + } + + public String getFunctionName() + { + return functionName; + } + + public void setFunctionName(String functionName) + { + this.functionName = functionName; + } + + public String getFunctionAuthor() + { + return functionAuthor; + } + + public void setFunctionAuthor(String functionAuthor) + { + this.functionAuthor = functionAuthor; + } + + public String getGenType() + { + return genType; + } + + public void setGenType(String genType) + { + this.genType = genType; + } + + public String getGenPath() + { + return genPath; + } + + public void setGenPath(String genPath) + { + this.genPath = genPath; + } + + public GenTableColumn getPkColumn() + { + return pkColumn; + } + + public void setPkColumn(GenTableColumn pkColumn) + { + this.pkColumn = pkColumn; + } + + public GenTable getSubTable() + { + return subTable; + } + + public void setSubTable(GenTable subTable) + { + this.subTable = subTable; + } + + public List getColumns() + { + return columns; + } + + public void setColumns(List columns) + { + this.columns = columns; + } + + public String getOptions() + { + return options; + } + + public void setOptions(String options) + { + this.options = options; + } + + public String getTreeCode() + { + return treeCode; + } + + public void setTreeCode(String treeCode) + { + this.treeCode = treeCode; + } + + public String getTreeParentCode() + { + return treeParentCode; + } + + public void setTreeParentCode(String treeParentCode) + { + this.treeParentCode = treeParentCode; + } + + public String getTreeName() + { + return treeName; + } + + public void setTreeName(String treeName) + { + this.treeName = treeName; + } + + public Long getParentMenuId() + { + return parentMenuId; + } + + public void setParentMenuId(Long parentMenuId) + { + this.parentMenuId = parentMenuId; + } + + public String getParentMenuName() + { + return parentMenuName; + } + + public void setParentMenuName(String parentMenuName) + { + this.parentMenuName = parentMenuName; + } + + public boolean isSub() + { + return isSub(this.tplCategory); + } + + public static boolean isSub(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_SUB, tplCategory); + } + + public boolean isTree() + { + return isTree(this.tplCategory); + } + + public static boolean isTree(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_TREE, tplCategory); + } + + public boolean isCrud() + { + return isCrud(this.tplCategory); + } + + public static boolean isCrud(String tplCategory) + { + return tplCategory != null && StringUtils.equals(GenConstants.TPL_CRUD, tplCategory); + } + + public boolean isSuperColumn(String javaField) + { + return isSuperColumn(this.tplCategory, javaField); + } + + public static boolean isSuperColumn(String tplCategory, String javaField) + { + if (isTree(tplCategory)) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + ArrayUtils.addAll(GenConstants.TREE_ENTITY, GenConstants.BASE_ENTITY)); + } + return StringUtils.equalsAnyIgnoreCase(javaField, GenConstants.BASE_ENTITY); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java new file mode 100644 index 0000000..eb89231 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/domain/GenTableColumn.java @@ -0,0 +1,378 @@ +package com.ruoyi.generator.domain; + +import javax.validation.constraints.NotBlank; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; + +/** + * 代码生成业务字段表 gen_table_column + * + * @author ruoyi + */ +public class GenTableColumn extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 编号 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long columnId; + + /** 归属表编号 */ + @JsonSerialize(using = ToStringSerializer.class) + private Long tableId; + + /** 列名称 */ + private String columnName; + + /** 列描述 */ + private String columnComment; + + /** 列类型 */ + private String columnType; + + /** JAVA类型 */ + private String javaType; + + /** JAVA字段名 */ + @NotBlank(message = "Java属性不能为空") + private String javaField; + + /** 是否主键(1是) */ + private String isPk; + + /** 是否自增(1是) */ + private String isIncrement; + + /** 是否必填(1是) */ + private String isRequired; + + /** 是否为插入字段(1是) */ + private String isInsert; + + /** 是否编辑字段(1是) */ + private String isEdit; + + /** 是否列表字段(1是) */ + private String isList; + + /** 是否查询字段(1是) */ + private String isQuery; + + /** 查询方式(EQ等于、NE不等于、GT大于、LT小于、LIKE模糊、BETWEEN范围) */ + private String queryType; + + /** 显示类型(input文本框、textarea文本域、select下拉框、checkbox复选框、radio单选框、datetime日期控件、image图片上传控件、upload文件上传控件、editor富文本控件) */ + private String htmlType; + + /** 字典类型 */ + private String dictType; + + /** 排序 */ + private Integer sort; + + public void setColumnId(Long columnId) + { + this.columnId = columnId; + } + + public Long getColumnId() + { + return columnId; + } + + public void setTableId(Long tableId) + { + this.tableId = tableId; + } + + public Long getTableId() + { + return tableId; + } + + public void setColumnName(String columnName) + { + this.columnName = columnName; + } + + public String getColumnName() + { + return columnName; + } + + public void setColumnComment(String columnComment) + { + this.columnComment = columnComment; + } + + public String getColumnComment() + { + return columnComment; + } + + public void setColumnType(String columnType) + { + this.columnType = columnType; + } + + public String getColumnType() + { + return columnType; + } + + public void setJavaType(String javaType) + { + this.javaType = javaType; + } + + public String getJavaType() + { + return javaType; + } + + public void setJavaField(String javaField) + { + this.javaField = javaField; + } + + public String getJavaField() + { + return javaField; + } + + public String getCapJavaField() + { + return StringUtils.capitalize(javaField); + } + + public void setIsPk(String isPk) + { + this.isPk = isPk; + } + + public String getIsPk() + { + return isPk; + } + + public boolean isPk() + { + return isPk(this.isPk); + } + + public boolean isPk(String isPk) + { + return isPk != null && StringUtils.equals("1", isPk); + } + + public String getIsIncrement() + { + return isIncrement; + } + + public void setIsIncrement(String isIncrement) + { + this.isIncrement = isIncrement; + } + + public boolean isIncrement() + { + return isIncrement(this.isIncrement); + } + + public boolean isIncrement(String isIncrement) + { + return isIncrement != null && StringUtils.equals("1", isIncrement); + } + + public void setIsRequired(String isRequired) + { + this.isRequired = isRequired; + } + + public String getIsRequired() + { + return isRequired; + } + + public boolean isRequired() + { + return isRequired(this.isRequired); + } + + public boolean isRequired(String isRequired) + { + return isRequired != null && StringUtils.equals("1", isRequired); + } + + public void setIsInsert(String isInsert) + { + this.isInsert = isInsert; + } + + public String getIsInsert() + { + return isInsert; + } + + public boolean isInsert() + { + return isInsert(this.isInsert); + } + + public boolean isInsert(String isInsert) + { + return isInsert != null && StringUtils.equals("1", isInsert); + } + + public void setIsEdit(String isEdit) + { + this.isEdit = isEdit; + } + + public String getIsEdit() + { + return isEdit; + } + + public boolean isEdit() + { + return isInsert(this.isEdit); + } + + public boolean isEdit(String isEdit) + { + return isEdit != null && StringUtils.equals("1", isEdit); + } + + public void setIsList(String isList) + { + this.isList = isList; + } + + public String getIsList() + { + return isList; + } + + public boolean isList() + { + return isList(this.isList); + } + + public boolean isList(String isList) + { + return isList != null && StringUtils.equals("1", isList); + } + + public void setIsQuery(String isQuery) + { + this.isQuery = isQuery; + } + + public String getIsQuery() + { + return isQuery; + } + + public boolean isQuery() + { + return isQuery(this.isQuery); + } + + public boolean isQuery(String isQuery) + { + return isQuery != null && StringUtils.equals("1", isQuery); + } + + public void setQueryType(String queryType) + { + this.queryType = queryType; + } + + public String getQueryType() + { + return queryType; + } + + public String getHtmlType() + { + return htmlType; + } + + public void setHtmlType(String htmlType) + { + this.htmlType = htmlType; + } + + public void setDictType(String dictType) + { + this.dictType = dictType; + } + + public String getDictType() + { + return dictType; + } + + public void setSort(Integer sort) + { + this.sort = sort; + } + + public Integer getSort() + { + return sort; + } + + public boolean isSuperColumn() + { + return isSuperColumn(this.javaField); + } + + public static boolean isSuperColumn(String javaField) + { + return StringUtils.equalsAnyIgnoreCase(javaField, + // BaseEntity + "createBy", "createTime", "updateBy", "updateTime", "remark", + // TreeEntity + "parentName", "parentId", "orderNum", "ancestors"); + } + + public boolean isUsableColumn() + { + return isUsableColumn(javaField); + } + + public static boolean isUsableColumn(String javaField) + { + // isSuperColumn()中的名单用于避免生成多余Domain属性,若某些属性在生成页面时需要用到不能忽略,则放在此处白名单 + return StringUtils.equalsAnyIgnoreCase(javaField, "parentId", "orderNum", "remark"); + } + + public String readConverterExp() + { + String remarks = StringUtils.substringBetween(this.columnComment, "(", ")"); + StringBuffer sb = new StringBuffer(); + if (StringUtils.isNotEmpty(remarks)) + { + for (String value : remarks.split(" ")) + { + if (StringUtils.isNotEmpty(value)) + { + Object startStr = value.subSequence(0, 1); + String endStr = value.substring(1); + sb.append("").append(startStr).append("=").append(endStr).append(","); + } + } + return sb.deleteCharAt(sb.length() - 1).toString(); + } + else + { + return this.columnComment; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java new file mode 100644 index 0000000..951e166 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableColumnMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 数据层 + * + * @author ruoyi + */ +public interface GenTableColumnMapper +{ + /** + * 根据表名称查询列信息 + * + * @param tableName 表名称 + * @return 列信息 + */ + public List selectDbTableColumnsByName(String tableName); + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段 + * + * @param genTableColumns 列数据 + * @return 结果 + */ + public int deleteGenTableColumns(List genTableColumns); + + /** + * 批量删除业务字段 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(Long[] ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java new file mode 100644 index 0000000..937656d --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/mapper/GenTableMapper.java @@ -0,0 +1,91 @@ +package com.ruoyi.generator.mapper; + +import java.util.List; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 数据层 + * + * @author ruoyi + */ +public interface GenTableMapper +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询表ID业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 查询表名称业务信息 + * + * @param tableName 表名称 + * @return 业务信息 + */ + public GenTable selectGenTableByName(String tableName); + + /** + * 新增业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int insertGenTable(GenTable genTable); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public int updateGenTable(GenTable genTable); + + /** + * 批量删除业务 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableByIds(Long[] ids); + + /** + * 创建表 + * + * @param sql 表结构 + * @return 结果 + */ + public int createTable(String sql); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java new file mode 100644 index 0000000..936659d --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableColumnServiceImpl.java @@ -0,0 +1,70 @@ +package com.ruoyi.generator.service; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; + +/** + * 业务字段 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableColumnServiceImpl implements IGenTableColumnService +{ + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + @Override + public List selectGenTableColumnListByTableId(Long tableId) + { + return genTableColumnMapper.selectGenTableColumnListByTableId(tableId); + } + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int insertGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.insertGenTableColumn(genTableColumn); + } + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + @Override + public int updateGenTableColumn(GenTableColumn genTableColumn) + { + return genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + + /** + * 删除业务字段对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteGenTableColumnByIds(String ids) + { + return genTableColumnMapper.deleteGenTableColumnByIds(Convert.toLongArray(ids)); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java new file mode 100644 index 0000000..6df135b --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/GenTableServiceImpl.java @@ -0,0 +1,533 @@ +package com.ruoyi.generator.service; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import cn.hutool.core.util.IdUtil; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.velocity.Template; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.Velocity; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.core.text.CharsetKit; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; +import com.ruoyi.generator.mapper.GenTableColumnMapper; +import com.ruoyi.generator.mapper.GenTableMapper; +import com.ruoyi.generator.util.GenUtils; +import com.ruoyi.generator.util.VelocityInitializer; +import com.ruoyi.generator.util.VelocityUtils; + +/** + * 业务 服务层实现 + * + * @author ruoyi + */ +@Service +public class GenTableServiceImpl implements IGenTableService +{ + private static final Logger log = LoggerFactory.getLogger(GenTableServiceImpl.class); + + @Autowired + private GenTableMapper genTableMapper; + + @Autowired + private GenTableColumnMapper genTableColumnMapper; + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + @Override + public GenTable selectGenTableById(Long id) + { + GenTable genTable = genTableMapper.selectGenTableById(id); + setTableFromOptions(genTable); + return genTable; + } + + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + @Override + public List selectGenTableList(GenTable genTable) + { + return genTableMapper.selectGenTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + @Override + public List selectDbTableList(GenTable genTable) + { + return genTableMapper.selectDbTableList(genTable); + } + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + @Override + public List selectDbTableListByNames(String[] tableNames) + { + return genTableMapper.selectDbTableListByNames(tableNames); + } + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + @Override + public List selectGenTableAll() + { + return genTableMapper.selectGenTableAll(); + } + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + @Override + @Transactional + public void updateGenTable(GenTable genTable) + { + String options = JSON.toJSONString(genTable.getParams()); + genTable.setOptions(options); + int row = genTableMapper.updateGenTable(genTable); + if (row > 0) + { + for (GenTableColumn genTableColumn : genTable.getColumns()) + { + genTableColumnMapper.updateGenTableColumn(genTableColumn); + } + } + } + + /** + * 删除业务对象 + * + * @param tableIds 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public void deleteGenTableByIds(Long[] tableIds) + { + genTableMapper.deleteGenTableByIds(tableIds); + genTableColumnMapper.deleteGenTableColumnByIds(tableIds); + } + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + @Override + public boolean createTable(String sql) + { + return genTableMapper.createTable(sql) == 0; + } + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + */ + @Override + @Transactional + public void importGenTable(List tableList, String operName) + { + try + { + for (GenTable table : tableList) + { + String tableName = table.getTableName(); + GenUtils.initTable(table, operName); + int row = genTableMapper.insertGenTable(table); + if (row > 0) + { + // 保存列信息 + List genTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + for (GenTableColumn column : genTableColumns) + { + GenUtils.initColumnField(column, table); + genTableColumnMapper.insertGenTableColumn(column); + } + } + } + } + catch (Exception e) + { + throw new ServiceException("导入失败:" + e.getMessage()); + } + } + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + @Override + public Map previewCode(Long tableId) + { + Map dataMap = new LinkedHashMap<>(); + // 查询表信息 + GenTable table = genTableMapper.selectGenTableById(tableId); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + dataMap.put(template, sw.toString()); + } + return dataMap; + } + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + @Override + public byte[] downloadCode(String tableName) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + generatorCode(tableName, zip); + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + */ + @Override + public void generatorCode(String tableName) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + if (!StringUtils.containsAny(template, "sql.vm", "api.js.vm", "index.vue.vm", "index-tree.vue.vm")) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + String path = getGenPath(table, template); + FileUtils.writeStringToFile(new File(path), sw.toString(), CharsetKit.UTF_8); + } + catch (IOException e) + { + throw new ServiceException("渲染模板失败,表名:" + table.getTableName()); + } + } + } + } + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + @Override + @Transactional + public void synchDb(String tableName) + { + GenTable table = genTableMapper.selectGenTableByName(tableName); + List tableColumns = table.getColumns(); + Map tableColumnMap = tableColumns.stream().collect(Collectors.toMap(GenTableColumn::getColumnName, Function.identity())); + + List dbTableColumns = genTableColumnMapper.selectDbTableColumnsByName(tableName); + if (StringUtils.isEmpty(dbTableColumns)) + { + throw new ServiceException("同步数据失败,原表结构不存在"); + } + List dbTableColumnNames = dbTableColumns.stream().map(GenTableColumn::getColumnName).collect(Collectors.toList()); + + dbTableColumns.forEach(column -> { + GenUtils.initColumnField(column, table); + if (tableColumnMap.containsKey(column.getColumnName())) + { + GenTableColumn prevColumn = tableColumnMap.get(column.getColumnName()); + column.setColumnId(prevColumn.getColumnId()); + if (column.isList()) + { + // 如果是列表,继续保留查询方式/字典类型选项 + column.setDictType(prevColumn.getDictType()); + column.setQueryType(prevColumn.getQueryType()); + } + if (StringUtils.isNotEmpty(prevColumn.getIsRequired()) && !column.isPk() + && (column.isInsert() || column.isEdit()) + && ((column.isUsableColumn()) || (!column.isSuperColumn()))) + { + // 如果是(新增/修改&非主键/非忽略及父属性),继续保留必填/显示类型选项 + column.setIsRequired(prevColumn.getIsRequired()); + column.setHtmlType(prevColumn.getHtmlType()); + } + genTableColumnMapper.updateGenTableColumn(column); + } + else + { + genTableColumnMapper.insertGenTableColumn(column); + } + }); + + List delColumns = tableColumns.stream().filter(column -> !dbTableColumnNames.contains(column.getColumnName())).collect(Collectors.toList()); + if (StringUtils.isNotEmpty(delColumns)) + { + genTableColumnMapper.deleteGenTableColumns(delColumns); + } + } + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + @Override + public byte[] downloadCode(String[] tableNames) + { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ZipOutputStream zip = new ZipOutputStream(outputStream); + for (String tableName : tableNames) + { + generatorCode(tableName, zip); + } + IOUtils.closeQuietly(zip); + return outputStream.toByteArray(); + } + + /** + * 查询表信息并生成代码 + */ + private void generatorCode(String tableName, ZipOutputStream zip) + { + // 查询表信息 + GenTable table = genTableMapper.selectGenTableByName(tableName); + // 设置主子表信息 + setSubTable(table); + // 设置主键列信息 + setPkColumn(table); + + VelocityInitializer.initVelocity(); + + VelocityContext context = VelocityUtils.prepareContext(table); + + // 获取模板列表 + List templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType()); + for (String template : templates) + { + // 渲染模板 + StringWriter sw = new StringWriter(); + Template tpl = Velocity.getTemplate(template, Constants.UTF8); + tpl.merge(context, sw); + try + { + // 添加到zip + zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table))); + IOUtils.write(sw.toString(), zip, Constants.UTF8); + IOUtils.closeQuietly(sw); + zip.flush(); + zip.closeEntry(); + } + catch (IOException e) + { + log.error("渲染模板失败,表名:" + table.getTableName(), e); + } + } + } + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + @Override + public void validateEdit(GenTable genTable) + { + if (GenConstants.TPL_TREE.equals(genTable.getTplCategory())) + { + String options = JSON.toJSONString(genTable.getParams()); + JSONObject paramsObj = JSON.parseObject(options); + if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_CODE))) + { + throw new ServiceException("树编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_PARENT_CODE))) + { + throw new ServiceException("树父编码字段不能为空"); + } + else if (StringUtils.isEmpty(paramsObj.getString(GenConstants.TREE_NAME))) + { + throw new ServiceException("树名称字段不能为空"); + } + } + else if (GenConstants.TPL_SUB.equals(genTable.getTplCategory())) + { + if (StringUtils.isEmpty(genTable.getSubTableName())) + { + throw new ServiceException("关联子表的表名不能为空"); + } + else if (StringUtils.isEmpty(genTable.getSubTableFkName())) + { + throw new ServiceException("子表关联的外键名不能为空"); + } + } + } + + /** + * 设置主键列信息 + * + * @param table 业务表信息 + */ + public void setPkColumn(GenTable table) + { + for (GenTableColumn column : table.getColumns()) + { + if (column.isPk()) + { + table.setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getPkColumn())) + { + table.setPkColumn(table.getColumns().get(0)); + } + if (GenConstants.TPL_SUB.equals(table.getTplCategory())) + { + for (GenTableColumn column : table.getSubTable().getColumns()) + { + if (column.isPk()) + { + table.getSubTable().setPkColumn(column); + break; + } + } + if (StringUtils.isNull(table.getSubTable().getPkColumn())) + { + table.getSubTable().setPkColumn(table.getSubTable().getColumns().get(0)); + } + } + } + + /** + * 设置主子表信息 + * + * @param table 业务表信息 + */ + public void setSubTable(GenTable table) + { + String subTableName = table.getSubTableName(); + if (StringUtils.isNotEmpty(subTableName)) + { + table.setSubTable(genTableMapper.selectGenTableByName(subTableName)); + } + } + + /** + * 设置代码生成其他选项值 + * + * @param genTable 设置后的生成对象 + */ + public void setTableFromOptions(GenTable genTable) + { + JSONObject paramsObj = JSON.parseObject(genTable.getOptions()); + if (StringUtils.isNotNull(paramsObj)) + { + String treeCode = paramsObj.getString(GenConstants.TREE_CODE); + String treeParentCode = paramsObj.getString(GenConstants.TREE_PARENT_CODE); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + Long parentMenuId = paramsObj.getLongValue(GenConstants.PARENT_MENU_ID); + String parentMenuName = paramsObj.getString(GenConstants.PARENT_MENU_NAME); + + genTable.setTreeCode(treeCode); + genTable.setTreeParentCode(treeParentCode); + genTable.setTreeName(treeName); + genTable.setParentMenuId(parentMenuId); + genTable.setParentMenuName(parentMenuName); + } + } + + /** + * 获取代码生成地址 + * + * @param table 业务表信息 + * @param template 模板文件路径 + * @return 生成地址 + */ + public static String getGenPath(GenTable table, String template) + { + String genPath = table.getGenPath(); + if (StringUtils.equals(genPath, "/")) + { + return System.getProperty("user.dir") + File.separator + "src" + File.separator + VelocityUtils.getFileName(template, table); + } + return genPath + File.separator + VelocityUtils.getFileName(template, table); + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java new file mode 100644 index 0000000..3037f70 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableColumnService.java @@ -0,0 +1,44 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 业务字段 服务层 + * + * @author ruoyi + */ +public interface IGenTableColumnService +{ + /** + * 查询业务字段列表 + * + * @param tableId 业务字段编号 + * @return 业务字段集合 + */ + public List selectGenTableColumnListByTableId(Long tableId); + + /** + * 新增业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int insertGenTableColumn(GenTableColumn genTableColumn); + + /** + * 修改业务字段 + * + * @param genTableColumn 业务字段信息 + * @return 结果 + */ + public int updateGenTableColumn(GenTableColumn genTableColumn); + + /** + * 删除业务字段信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteGenTableColumnByIds(String ids); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java new file mode 100644 index 0000000..695426e --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/service/IGenTableService.java @@ -0,0 +1,130 @@ +package com.ruoyi.generator.service; + +import java.util.List; +import java.util.Map; +import com.ruoyi.generator.domain.GenTable; + +/** + * 业务 服务层 + * + * @author ruoyi + */ +public interface IGenTableService +{ + /** + * 查询业务列表 + * + * @param genTable 业务信息 + * @return 业务集合 + */ + public List selectGenTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param genTable 业务信息 + * @return 数据库表集合 + */ + public List selectDbTableList(GenTable genTable); + + /** + * 查询据库列表 + * + * @param tableNames 表名称组 + * @return 数据库表集合 + */ + public List selectDbTableListByNames(String[] tableNames); + + /** + * 查询所有表信息 + * + * @return 表信息集合 + */ + public List selectGenTableAll(); + + /** + * 查询业务信息 + * + * @param id 业务ID + * @return 业务信息 + */ + public GenTable selectGenTableById(Long id); + + /** + * 修改业务 + * + * @param genTable 业务信息 + * @return 结果 + */ + public void updateGenTable(GenTable genTable); + + /** + * 删除业务信息 + * + * @param tableIds 需要删除的表数据ID + * @return 结果 + */ + public void deleteGenTableByIds(Long[] tableIds); + + /** + * 创建表 + * + * @param sql 创建表语句 + * @return 结果 + */ + public boolean createTable(String sql); + + /** + * 导入表结构 + * + * @param tableList 导入表列表 + * @param operName 操作人员 + */ + public void importGenTable(List tableList, String operName); + + /** + * 预览代码 + * + * @param tableId 表编号 + * @return 预览数据列表 + */ + public Map previewCode(Long tableId); + + /** + * 生成代码(下载方式) + * + * @param tableName 表名称 + * @return 数据 + */ + public byte[] downloadCode(String tableName); + + /** + * 生成代码(自定义路径) + * + * @param tableName 表名称 + * @return 数据 + */ + public void generatorCode(String tableName); + + /** + * 同步数据库 + * + * @param tableName 表名称 + */ + public void synchDb(String tableName); + + /** + * 批量生成代码(下载方式) + * + * @param tableNames 表数组 + * @return 数据 + */ + public byte[] downloadCode(String[] tableNames); + + /** + * 修改保存参数校验 + * + * @param genTable 业务信息 + */ + public void validateEdit(GenTable genTable); +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java new file mode 100644 index 0000000..e7ebc20 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/GenUtils.java @@ -0,0 +1,257 @@ +package com.ruoyi.generator.util; + +import java.util.Arrays; +import org.apache.commons.lang3.RegExUtils; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.config.GenConfig; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 代码生成器 工具类 + * + * @author ruoyi + */ +public class GenUtils +{ + /** + * 初始化表信息 + */ + public static void initTable(GenTable genTable, String operName) + { + genTable.setClassName(convertClassName(genTable.getTableName())); + genTable.setPackageName(GenConfig.getPackageName()); + genTable.setModuleName(getModuleName(GenConfig.getPackageName())); + genTable.setBusinessName(getBusinessName(genTable.getTableName())); + genTable.setFunctionName(replaceText(genTable.getTableComment())); + genTable.setFunctionAuthor(GenConfig.getAuthor()); + genTable.setCreateBy(operName); + } + + /** + * 初始化列属性字段 + */ + public static void initColumnField(GenTableColumn column, GenTable table) + { + String dataType = getDbType(column.getColumnType()); + String columnName = column.getColumnName(); + column.setTableId(table.getTableId()); + column.setCreateBy(table.getCreateBy()); + // 设置java字段名 + column.setJavaField(StringUtils.toCamelCase(columnName)); + // 设置默认类型 + column.setJavaType(GenConstants.TYPE_STRING); + column.setQueryType(GenConstants.QUERY_EQ); + + if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType)) + { + // 字符串长度超过500设置为文本域 + Integer columnLength = getColumnLength(column.getColumnType()); + String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT; + column.setHtmlType(htmlType); + } + else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType)) + { + column.setJavaType(GenConstants.TYPE_DATE); + column.setHtmlType(GenConstants.HTML_DATETIME); + } + else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType)) + { + column.setHtmlType(GenConstants.HTML_INPUT); + + // 如果是浮点型 统一用BigDecimal + String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ","); + if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0) + { + column.setJavaType(GenConstants.TYPE_BIGDECIMAL); + } + // 如果是整形 + else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10) + { + column.setJavaType(GenConstants.TYPE_INTEGER); + } + // 长整形 + else + { + column.setJavaType(GenConstants.TYPE_LONG); + } + } + + // 插入字段(默认所有字段都需要插入) + column.setIsInsert(GenConstants.REQUIRE); + + // 编辑字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_EDIT, columnName) && !column.isPk()) + { + column.setIsEdit(GenConstants.REQUIRE); + } + // 列表字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_LIST, columnName) && !column.isPk()) + { + column.setIsList(GenConstants.REQUIRE); + } + // 查询字段 + if (!arraysContains(GenConstants.COLUMNNAME_NOT_QUERY, columnName) && !column.isPk()) + { + column.setIsQuery(GenConstants.REQUIRE); + } + + // 查询字段类型 + if (StringUtils.endsWithIgnoreCase(columnName, "name")) + { + column.setQueryType(GenConstants.QUERY_LIKE); + } + // 状态字段设置单选框 + if (StringUtils.endsWithIgnoreCase(columnName, "status")) + { + column.setHtmlType(GenConstants.HTML_RADIO); + } + // 类型&性别字段设置下拉框 + else if (StringUtils.endsWithIgnoreCase(columnName, "type") + || StringUtils.endsWithIgnoreCase(columnName, "sex")) + { + column.setHtmlType(GenConstants.HTML_SELECT); + } + // 图片字段设置图片上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "image")) + { + column.setHtmlType(GenConstants.HTML_IMAGE_UPLOAD); + } + // 文件字段设置文件上传控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "file")) + { + column.setHtmlType(GenConstants.HTML_FILE_UPLOAD); + } + // 内容字段设置富文本控件 + else if (StringUtils.endsWithIgnoreCase(columnName, "content")) + { + column.setHtmlType(GenConstants.HTML_EDITOR); + } + } + + /** + * 校验数组是否包含指定值 + * + * @param arr 数组 + * @param targetValue 值 + * @return 是否包含 + */ + public static boolean arraysContains(String[] arr, String targetValue) + { + return Arrays.asList(arr).contains(targetValue); + } + + /** + * 获取模块名 + * + * @param packageName 包名 + * @return 模块名 + */ + public static String getModuleName(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + int nameLength = packageName.length(); + return StringUtils.substring(packageName, lastIndex + 1, nameLength); + } + + /** + * 获取业务名 + * + * @param tableName 表名 + * @return 业务名 + */ + public static String getBusinessName(String tableName) + { + int lastIndex = tableName.lastIndexOf("_"); + int nameLength = tableName.length(); + return StringUtils.substring(tableName, lastIndex + 1, nameLength); + } + + /** + * 表名转换成Java类名 + * + * @param tableName 表名称 + * @return 类名 + */ + public static String convertClassName(String tableName) + { + boolean autoRemovePre = GenConfig.getAutoRemovePre(); + String tablePrefix = GenConfig.getTablePrefix(); + if (autoRemovePre && StringUtils.isNotEmpty(tablePrefix)) + { + String[] searchList = StringUtils.split(tablePrefix, ","); + tableName = replaceFirst(tableName, searchList); + } + return StringUtils.convertToCamelCase(tableName); + } + + /** + * 批量替换前缀 + * + * @param replacementm 替换值 + * @param searchList 替换列表 + * @return + */ + public static String replaceFirst(String replacementm, String[] searchList) + { + String text = replacementm; + for (String searchString : searchList) + { + if (replacementm.startsWith(searchString)) + { + text = replacementm.replaceFirst(searchString, ""); + break; + } + } + return text; + } + + /** + * 关键字替换 + * + * @param text 需要被替换的名字 + * @return 替换后的名字 + */ + public static String replaceText(String text) + { + return RegExUtils.replaceAll(text, "(?:表|若依)", ""); + } + + /** + * 获取数据库类型字段 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static String getDbType(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + return StringUtils.substringBefore(columnType, "("); + } + else + { + return columnType; + } + } + + /** + * 获取字段长度 + * + * @param columnType 列类型 + * @return 截取后的列类型 + */ + public static Integer getColumnLength(String columnType) + { + if (StringUtils.indexOf(columnType, "(") > 0) + { + String length = StringUtils.substringBetween(columnType, "(", ")"); + return Integer.valueOf(length); + } + else + { + return 0; + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java new file mode 100644 index 0000000..9f69403 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityInitializer.java @@ -0,0 +1,34 @@ +package com.ruoyi.generator.util; + +import java.util.Properties; +import org.apache.velocity.app.Velocity; +import com.ruoyi.common.constant.Constants; + +/** + * VelocityEngine工厂 + * + * @author ruoyi + */ +public class VelocityInitializer +{ + /** + * 初始化vm方法 + */ + public static void initVelocity() + { + Properties p = new Properties(); + try + { + // 加载classpath目录下的vm文件 + p.setProperty("resource.loader.file.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); + // 定义字符集 + p.setProperty(Velocity.INPUT_ENCODING, Constants.UTF8); + // 初始化Velocity引擎,指定配置Properties + Velocity.init(p); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } +} diff --git a/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java new file mode 100644 index 0000000..1a14681 --- /dev/null +++ b/ruoyi-generator/src/main/java/com/ruoyi/generator/util/VelocityUtils.java @@ -0,0 +1,408 @@ +package com.ruoyi.generator.util; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.velocity.VelocityContext; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.common.constant.GenConstants; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.generator.domain.GenTable; +import com.ruoyi.generator.domain.GenTableColumn; + +/** + * 模板处理工具类 + * + * @author ruoyi + */ +public class VelocityUtils +{ + /** 项目空间路径 */ + private static final String PROJECT_PATH = "main/java"; + + /** mybatis空间路径 */ + private static final String MYBATIS_PATH = "main/resources/mapper"; + + /** 默认上级菜单,系统工具 */ + private static final String DEFAULT_PARENT_MENU_ID = "3"; + + /** + * 设置模板变量信息 + * + * @return 模板列表 + */ + public static VelocityContext prepareContext(GenTable genTable) + { + String moduleName = genTable.getModuleName(); + String businessName = genTable.getBusinessName(); + String packageName = genTable.getPackageName(); + String tplCategory = genTable.getTplCategory(); + String functionName = genTable.getFunctionName(); + + VelocityContext velocityContext = new VelocityContext(); + velocityContext.put("tplCategory", genTable.getTplCategory()); + velocityContext.put("tableName", genTable.getTableName()); + velocityContext.put("functionName", StringUtils.isNotEmpty(functionName) ? functionName : "【请填写功能名称】"); + velocityContext.put("ClassName", genTable.getClassName()); + velocityContext.put("className", StringUtils.uncapitalize(genTable.getClassName())); + velocityContext.put("moduleName", genTable.getModuleName()); + velocityContext.put("BusinessName", StringUtils.capitalize(genTable.getBusinessName())); + velocityContext.put("businessName", genTable.getBusinessName()); + velocityContext.put("basePackage", getPackagePrefix(packageName)); + velocityContext.put("packageName", packageName); + velocityContext.put("author", genTable.getFunctionAuthor()); + velocityContext.put("datetime", DateUtils.getDate()); + velocityContext.put("pkColumn", genTable.getPkColumn()); + velocityContext.put("importList", getImportList(genTable)); + velocityContext.put("permissionPrefix", getPermissionPrefix(moduleName, businessName)); + velocityContext.put("columns", genTable.getColumns()); + velocityContext.put("table", genTable); + velocityContext.put("dicts", getDicts(genTable)); + setMenuVelocityContext(velocityContext, genTable); + if (GenConstants.TPL_TREE.equals(tplCategory)) + { + setTreeVelocityContext(velocityContext, genTable); + } + if (GenConstants.TPL_SUB.equals(tplCategory)) + { + setSubVelocityContext(velocityContext, genTable); + } + return velocityContext; + } + + public static void setMenuVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String parentMenuId = getParentMenuId(paramsObj); + context.put("parentMenuId", parentMenuId); + } + + public static void setTreeVelocityContext(VelocityContext context, GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeCode = getTreecode(paramsObj); + String treeParentCode = getTreeParentCode(paramsObj); + String treeName = getTreeName(paramsObj); + + context.put("treeCode", treeCode); + context.put("treeParentCode", treeParentCode); + context.put("treeName", treeName); + context.put("expandColumn", getExpandColumn(genTable)); + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + context.put("tree_parent_code", paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + context.put("tree_name", paramsObj.getString(GenConstants.TREE_NAME)); + } + } + + public static void setSubVelocityContext(VelocityContext context, GenTable genTable) + { + GenTable subTable = genTable.getSubTable(); + String subTableName = genTable.getSubTableName(); + String subTableFkName = genTable.getSubTableFkName(); + String subClassName = genTable.getSubTable().getClassName(); + String subTableFkClassName = StringUtils.convertToCamelCase(subTableFkName); + + context.put("subTable", subTable); + context.put("subTableName", subTableName); + context.put("subTableFkName", subTableFkName); + context.put("subTableFkClassName", subTableFkClassName); + context.put("subTableFkclassName", StringUtils.uncapitalize(subTableFkClassName)); + context.put("subClassName", subClassName); + context.put("subclassName", StringUtils.uncapitalize(subClassName)); + context.put("subImportList", getImportList(genTable.getSubTable())); + } + + /** + * 获取模板信息 + * @param tplCategory 生成的模板 + * @param tplWebType 前端类型 + * @return 模板列表 + */ + public static List getTemplateList(String tplCategory, String tplWebType) + { + String useWebType = "vm/vue"; + if ("element-plus".equals(tplWebType)) + { + useWebType = "vm/vue/v3"; + } + List templates = new ArrayList(); + templates.add("vm/java/domain.java.vm"); + templates.add("vm/java/mapper.java.vm"); + templates.add("vm/java/service.java.vm"); + templates.add("vm/java/serviceImpl.java.vm"); + templates.add("vm/java/controller.java.vm"); + templates.add("vm/xml/mapper.xml.vm"); + templates.add("vm/sql/sql.vm"); + templates.add("vm/js/api.js.vm"); + if (GenConstants.TPL_CRUD.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + } + else if (GenConstants.TPL_TREE.equals(tplCategory)) + { + templates.add(useWebType + "/index-tree.vue.vm"); + } + else if (GenConstants.TPL_SUB.equals(tplCategory)) + { + templates.add(useWebType + "/index.vue.vm"); + templates.add("vm/java/sub-domain.java.vm"); + } + return templates; + } + + /** + * 获取文件名 + */ + public static String getFileName(String template, GenTable genTable) + { + // 文件名称 + String fileName = ""; + // 包路径 + String packageName = genTable.getPackageName(); + // 模块名 + String moduleName = genTable.getModuleName(); + // 大写类名 + String className = genTable.getClassName(); + // 业务名称 + String businessName = genTable.getBusinessName(); + + String javaPath = PROJECT_PATH + "/" + StringUtils.replace(packageName, ".", "/"); + String mybatisPath = MYBATIS_PATH + "/" + moduleName; + String vuePath = "vue"; + + if (template.contains("domain.java.vm")) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, className); + } + if (template.contains("sub-domain.java.vm") && StringUtils.equals(GenConstants.TPL_SUB, genTable.getTplCategory())) + { + fileName = StringUtils.format("{}/domain/{}.java", javaPath, genTable.getSubTable().getClassName()); + } + else if (template.contains("mapper.java.vm")) + { + fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className); + } + else if (template.contains("service.java.vm")) + { + fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className); + } + else if (template.contains("serviceImpl.java.vm")) + { + fileName = StringUtils.format("{}/service/impl/{}ServiceImpl.java", javaPath, className); + } + else if (template.contains("controller.java.vm")) + { + fileName = StringUtils.format("{}/controller/{}Controller.java", javaPath, className); + } + else if (template.contains("mapper.xml.vm")) + { + fileName = StringUtils.format("{}/{}Mapper.xml", mybatisPath, className); + } + else if (template.contains("sql.vm")) + { + fileName = businessName + "Menu.sql"; + } + else if (template.contains("api.js.vm")) + { + fileName = StringUtils.format("{}/api/{}/{}.js", vuePath, moduleName, businessName); + } + else if (template.contains("index.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + else if (template.contains("index-tree.vue.vm")) + { + fileName = StringUtils.format("{}/views/{}/{}/index.vue", vuePath, moduleName, businessName); + } + return fileName; + } + + /** + * 获取包前缀 + * + * @param packageName 包名称 + * @return 包前缀名称 + */ + public static String getPackagePrefix(String packageName) + { + int lastIndex = packageName.lastIndexOf("."); + return StringUtils.substring(packageName, 0, lastIndex); + } + + /** + * 根据列类型获取导入包 + * + * @param genTable 业务表对象 + * @return 返回需要导入的包列表 + */ + public static HashSet getImportList(GenTable genTable) + { + List columns = genTable.getColumns(); + GenTable subGenTable = genTable.getSubTable(); + HashSet importList = new HashSet(); + if (StringUtils.isNotNull(subGenTable)) + { + importList.add("java.util.List"); + } + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) + { + importList.add("java.util.Date"); + importList.add("com.fasterxml.jackson.annotation.JsonFormat"); + } + else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) + { + importList.add("java.math.BigDecimal"); + } + } + return importList; + } + + /** + * 根据列类型获取字典组 + * + * @param genTable 业务表对象 + * @return 返回字典组 + */ + public static String getDicts(GenTable genTable) + { + List columns = genTable.getColumns(); + Set dicts = new HashSet(); + addDicts(dicts, columns); + if (StringUtils.isNotNull(genTable.getSubTable())) + { + List subColumns = genTable.getSubTable().getColumns(); + addDicts(dicts, subColumns); + } + return StringUtils.join(dicts, ", "); + } + + /** + * 添加字典列表 + * + * @param dicts 字典列表 + * @param columns 列集合 + */ + public static void addDicts(Set dicts, List columns) + { + for (GenTableColumn column : columns) + { + if (!column.isSuperColumn() && StringUtils.isNotEmpty(column.getDictType()) && StringUtils.equalsAny( + column.getHtmlType(), + new String[] { GenConstants.HTML_SELECT, GenConstants.HTML_RADIO, GenConstants.HTML_CHECKBOX })) + { + dicts.add("'" + column.getDictType() + "'"); + } + } + } + + /** + * 获取权限前缀 + * + * @param moduleName 模块名称 + * @param businessName 业务名称 + * @return 返回权限前缀 + */ + public static String getPermissionPrefix(String moduleName, String businessName) + { + return StringUtils.format("{}:{}", moduleName, businessName); + } + + /** + * 获取上级菜单ID字段 + * + * @param paramsObj 生成其他选项 + * @return 上级菜单ID字段 + */ + public static String getParentMenuId(JSONObject paramsObj) + { + if (StringUtils.isNotEmpty(paramsObj) && paramsObj.containsKey(GenConstants.PARENT_MENU_ID) + && StringUtils.isNotEmpty(paramsObj.getString(GenConstants.PARENT_MENU_ID))) + { + return paramsObj.getString(GenConstants.PARENT_MENU_ID); + } + return DEFAULT_PARENT_MENU_ID; + } + + /** + * 获取树编码 + * + * @param paramsObj 生成其他选项 + * @return 树编码 + */ + public static String getTreecode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树父编码 + * + * @param paramsObj 生成其他选项 + * @return 树父编码 + */ + public static String getTreeParentCode(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_PARENT_CODE)); + } + return StringUtils.EMPTY; + } + + /** + * 获取树名称 + * + * @param paramsObj 生成其他选项 + * @return 树名称 + */ + public static String getTreeName(JSONObject paramsObj) + { + if (paramsObj.containsKey(GenConstants.TREE_NAME)) + { + return StringUtils.toCamelCase(paramsObj.getString(GenConstants.TREE_NAME)); + } + return StringUtils.EMPTY; + } + + /** + * 获取需要在哪一列上面显示展开按钮 + * + * @param genTable 业务表对象 + * @return 展开按钮列序号 + */ + public static int getExpandColumn(GenTable genTable) + { + String options = genTable.getOptions(); + JSONObject paramsObj = JSON.parseObject(options); + String treeName = paramsObj.getString(GenConstants.TREE_NAME); + int num = 0; + for (GenTableColumn column : genTable.getColumns()) + { + if (column.isList()) + { + num++; + String columnName = column.getColumnName(); + if (columnName.equals(treeName)) + { + break; + } + } + } + return num; + } +} diff --git a/ruoyi-generator/src/main/resources/generator.yml b/ruoyi-generator/src/main/resources/generator.yml new file mode 100644 index 0000000..d1cbf48 --- /dev/null +++ b/ruoyi-generator/src/main/resources/generator.yml @@ -0,0 +1,12 @@ +# 代码生成 +gen: + # 作者 + author: ruoyi + # 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool + packageName: com.ruoyi.system + # 自动去除表前缀,默认是false + autoRemovePre: false + # 表前缀(生成类名不会包含表前缀,多个用逗号分隔) + tablePrefix: sys_ + # 是否允许生成文件覆盖到本地(自定义路径),默认不允许 + allowOverwrite: false \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml new file mode 100644 index 0000000..b403fb9 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/GenTableColumnMapper.xml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select column_id, table_id, column_name, column_comment, column_type, java_type, java_field, is_pk, is_increment, is_required, is_insert, is_edit, is_list, is_query, query_type, html_type, dict_type, sort, create_by, create_time, update_by, update_time from gen_table_column + + + + + + + + insert into gen_table_column ( + column_id, + table_id, + column_name, + column_comment, + column_type, + java_type, + java_field, + is_pk, + is_increment, + is_required, + is_insert, + is_edit, + is_list, + is_query, + query_type, + html_type, + dict_type, + sort, + create_by, + create_time + )values( + #{columnId}, + #{tableId}, + #{columnName}, + #{columnComment}, + #{columnType}, + #{javaType}, + #{javaField}, + #{isPk}, + #{isIncrement}, + #{isRequired}, + #{isInsert}, + #{isEdit}, + #{isList}, + #{isQuery}, + #{queryType}, + #{htmlType}, + #{dictType}, + #{sort}, + #{createBy}, + GETDATE() + ) + + + + update gen_table_column + + column_comment = #{columnComment}, + java_type = #{javaType}, + java_field = #{javaField}, + is_insert = #{isInsert}, + is_edit = #{isEdit}, + is_list = #{isList}, + is_query = #{isQuery}, + is_required = #{isRequired}, + query_type = #{queryType}, + html_type = #{htmlType}, + dict_type = #{dictType}, + sort = #{sort}, + update_by = #{updateBy}, + update_time = GETDATE() + + where column_id = #{columnId} + + + + delete from gen_table_column where table_id in + + #{tableId} + + + + + delete from gen_table_column where column_id in + + #{item.columnId} + + + + diff --git a/ruoyi-generator/src/main/resources/mapper/generator/IDynamicMonitoringService.xml b/ruoyi-generator/src/main/resources/mapper/generator/IDynamicMonitoringService.xml new file mode 100644 index 0000000..914e750 --- /dev/null +++ b/ruoyi-generator/src/main/resources/mapper/generator/IDynamicMonitoringService.xml @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select table_id, table_name, table_comment, sub_table_name, sub_table_fk_name, class_name, tpl_category, tpl_web_type, package_name, module_name, business_name, function_name, function_author, gen_type, gen_path, options, create_by, create_time, update_by, update_time, remark from gen_table + + + + + + + + + + + + + + + + + + insert into gen_table ( + table_id, + table_name, + table_comment, + class_name, + tpl_category, + tpl_web_type, + package_name, + module_name, + business_name, + function_name, + function_author, + gen_type, + gen_path, + remark, + create_by, + create_time + )values( + #{tableId}, + #{tableName}, + #{tableComment}, + #{className}, + #{tplCategory}, + #{tplWebType}, + #{packageName}, + #{moduleName}, + #{businessName}, + #{functionName}, + #{functionAuthor}, + #{genType}, + #{genPath}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + + ${sql} + + + + update gen_table + + table_name = #{tableName}, + table_comment = #{tableComment}, + sub_table_name = #{subTableName}, + sub_table_fk_name = #{subTableFkName}, + class_name = #{className}, + function_author = #{functionAuthor}, + gen_type = #{genType}, + gen_path = #{genPath}, + tpl_category = #{tplCategory}, + tpl_web_type = #{tplWebType}, + package_name = #{packageName}, + module_name = #{moduleName}, + business_name = #{businessName}, + function_name = #{functionName}, + options = #{options}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = GETDATE() + + where table_id = #{tableId} + + + + delete from gen_table where table_id in + + #{tableId} + + + + diff --git a/ruoyi-generator/src/main/resources/vm/java/controller.java.vm b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm new file mode 100644 index 0000000..bf88988 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/controller.java.vm @@ -0,0 +1,115 @@ +package ${packageName}.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; +import com.ruoyi.common.utils.poi.ExcelUtil; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.page.TableDataInfo; +#elseif($table.tree) +#end + +/** + * ${functionName}Controller + * + * @author ${author} + * @date ${datetime} + */ +@RestController +@RequestMapping("/${moduleName}/${businessName}") +public class ${ClassName}Controller extends BaseController +{ + @Autowired + private I${ClassName}Service ${className}Service; + + /** + * 查询${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')") + @GetMapping("/list") +#if($table.crud || $table.sub) + public TableDataInfo list(${ClassName} ${className}) + { + startPage(); + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return getDataTable(list); + } +#elseif($table.tree) + public AjaxResult list(${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + return success(list); + } +#end + + /** + * 导出${functionName}列表 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')") + @Log(title = "${functionName}", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, ${ClassName} ${className}) + { + List<${ClassName}> list = ${className}Service.select${ClassName}List(${className}); + ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class); + util.exportExcel(response, list, "${functionName}数据"); + } + + /** + * 获取${functionName}详细信息 + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')") + @GetMapping(value = "/{${pkColumn.javaField}}") + public AjaxResult getInfo(@PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField}) + { + return success(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField})); + } + + /** + * 新增${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')") + @Log(title = "${functionName}", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.insert${ClassName}(${className})); + } + + /** + * 修改${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')") + @Log(title = "${functionName}", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody ${ClassName} ${className}) + { + return toAjax(${className}Service.update${ClassName}(${className})); + } + + /** + * 删除${functionName} + */ + @PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')") + @Log(title = "${functionName}", businessType = BusinessType.DELETE) + @DeleteMapping("/{${pkColumn.javaField}s}") + public AjaxResult remove(@PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s) + { + return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s)); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm new file mode 100644 index 0000000..b236ef0 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/domain.java.vm @@ -0,0 +1,105 @@ +package ${packageName}.domain; + +#foreach ($import in $importList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +#if($table.crud || $table.sub) +import com.ruoyi.common.core.domain.BaseEntity; +#elseif($table.tree) +import com.ruoyi.common.core.domain.TreeEntity; +#end + +/** + * ${functionName}对象 ${tableName} + * + * @author ${author} + * @date ${datetime} + */ +#if($table.crud || $table.sub) +#set($Entity="BaseEntity") +#elseif($table.tree) +#set($Entity="TreeEntity") +#end +public class ${ClassName} extends ${Entity} +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#if($table.sub) + /** $table.subTable.functionName信息 */ + private List<${subClassName}> ${subclassName}List; + +#end +#foreach ($column in $columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } + +#end +#end +#if($table.sub) + public List<${subClassName}> get${subClassName}List() + { + return ${subclassName}List; + } + + public void set${subClassName}List(List<${subClassName}> ${subclassName}List) + { + this.${subclassName}List = ${subclassName}List; + } + +#end + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end +#if($table.sub) + .append("${subclassName}List", get${subClassName}List()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm new file mode 100644 index 0000000..7e7d7c2 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/mapper.java.vm @@ -0,0 +1,91 @@ +package ${packageName}.mapper; + +import java.util.List; +import ${packageName}.domain.${ClassName}; +#if($table.sub) +import ${packageName}.domain.${subClassName}; +#end + +/** + * ${functionName}Mapper接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface ${ClassName}Mapper +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 删除${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); +#if($table.sub) + + /** + * 批量删除${subTable.functionName} + * + * @param ${pkColumn.javaField}s 需要删除的数据主键集合 + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 批量新增${subTable.functionName} + * + * @param ${subclassName}List ${subTable.functionName}列表 + * @return 结果 + */ + public int batch${subClassName}(List<${subClassName}> ${subclassName}List); + + + /** + * 通过${functionName}主键删除${subTable.functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}ID + * @return 结果 + */ + public int delete${subClassName}By${subTableFkClassName}(${pkColumn.javaType} ${pkColumn.javaField}); +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/service.java.vm b/ruoyi-generator/src/main/resources/vm/java/service.java.vm new file mode 100644 index 0000000..264882b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/service.java.vm @@ -0,0 +1,61 @@ +package ${packageName}.service; + +import java.util.List; +import ${packageName}.domain.${ClassName}; + +/** + * ${functionName}Service接口 + * + * @author ${author} + * @date ${datetime} + */ +public interface I${ClassName}Service +{ + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName}集合 + */ + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}); + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int insert${ClassName}(${ClassName} ${className}); + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ + public int update${ClassName}(${ClassName} ${className}); + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键集合 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s); + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}); +} diff --git a/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm new file mode 100644 index 0000000..14746e1 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/serviceImpl.java.vm @@ -0,0 +1,169 @@ +package ${packageName}.service.impl; + +import java.util.List; +#foreach ($column in $columns) +#if($column.javaField == 'createTime' || $column.javaField == 'updateTime') +import com.ruoyi.common.utils.DateUtils; +#break +#end +#end +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +#if($table.sub) +import java.util.ArrayList; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.transaction.annotation.Transactional; +import ${packageName}.domain.${subClassName}; +#end +import ${packageName}.mapper.${ClassName}Mapper; +import ${packageName}.domain.${ClassName}; +import ${packageName}.service.I${ClassName}Service; + +/** + * ${functionName}Service业务层处理 + * + * @author ${author} + * @date ${datetime} + */ +@Service +public class ${ClassName}ServiceImpl implements I${ClassName}Service +{ + @Autowired + private ${ClassName}Mapper ${className}Mapper; + + /** + * 查询${functionName} + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return ${functionName} + */ + @Override + public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { + return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } + + /** + * 查询${functionName}列表 + * + * @param ${className} ${functionName} + * @return ${functionName} + */ + @Override + public List<${ClassName}> select${ClassName}List(${ClassName} ${className}) + { + return ${className}Mapper.select${ClassName}List(${className}); + } + + /** + * 新增${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int insert${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'createTime') + ${className}.setCreateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + int rows = ${className}Mapper.insert${ClassName}(${className}); + insert${subClassName}(${className}); + return rows; +#else + return ${className}Mapper.insert${ClassName}(${className}); +#end + } + + /** + * 修改${functionName} + * + * @param ${className} ${functionName} + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int update${ClassName}(${ClassName} ${className}) + { +#foreach ($column in $columns) +#if($column.javaField == 'updateTime') + ${className}.setUpdateTime(DateUtils.getNowDate()); +#end +#end +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}()); + insert${subClassName}(${className}); +#end + return ${className}Mapper.update${ClassName}(${className}); + } + + /** + * 批量删除${functionName} + * + * @param ${pkColumn.javaField}s 需要删除的${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s); + } + + /** + * 删除${functionName}信息 + * + * @param ${pkColumn.javaField} ${functionName}主键 + * @return 结果 + */ +#if($table.sub) + @Transactional +#end + @Override + public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField}) + { +#if($table.sub) + ${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField}); +#end + return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}); + } +#if($table.sub) + + /** + * 新增${subTable.functionName}信息 + * + * @param ${className} ${functionName}对象 + */ + public void insert${subClassName}(${ClassName} ${className}) + { + List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List(); + ${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}(); + if (StringUtils.isNotNull(${subclassName}List)) + { + List<${subClassName}> list = new ArrayList<${subClassName}>(); + for (${subClassName} ${subclassName} : ${subclassName}List) + { + ${subclassName}.set${subTableFkClassName}(${pkColumn.javaField}); + list.add(${subclassName}); + } + if (list.size() > 0) + { + ${className}Mapper.batch${subClassName}(list); + } + } + } +#end +} diff --git a/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm new file mode 100644 index 0000000..a3f53eb --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/java/sub-domain.java.vm @@ -0,0 +1,76 @@ +package ${packageName}.domain; + +#foreach ($import in $subImportList) +import ${import}; +#end +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * ${subTable.functionName}对象 ${subTableName} + * + * @author ${author} + * @date ${datetime} + */ +public class ${subClassName} extends BaseEntity +{ + private static final long serialVersionUID = 1L; + +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) + /** $column.columnComment */ +#if($column.list) +#set($parentheseIndex=$column.columnComment.indexOf("(")) +#if($parentheseIndex != -1) +#set($comment=$column.columnComment.substring(0, $parentheseIndex)) +#else +#set($comment=$column.columnComment) +#end +#if($parentheseIndex != -1) + @Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()") +#elseif($column.javaType == 'Date') + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd") +#else + @Excel(name = "${comment}") +#end +#end + private $column.javaType $column.javaField; + +#end +#end +#foreach ($column in $subTable.columns) +#if(!$table.isSuperColumn($column.javaField)) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + public void set${AttrName}($column.javaType $column.javaField) + { + this.$column.javaField = $column.javaField; + } + + public $column.javaType get${AttrName}() + { + return $column.javaField; + } +#end +#end + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) +#foreach ($column in $subTable.columns) +#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]")) +#set($AttrName=$column.javaField) +#else +#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) +#end + .append("${column.javaField}", get${AttrName}()) +#end + .toString(); + } +} diff --git a/ruoyi-generator/src/main/resources/vm/js/api.js.vm b/ruoyi-generator/src/main/resources/vm/js/api.js.vm new file mode 100644 index 0000000..9295524 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/js/api.js.vm @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询${functionName}列表 +export function list${BusinessName}(query) { + return request({ + url: '/${moduleName}/${businessName}/list', + method: 'get', + params: query + }) +} + +// 查询${functionName}详细 +export function get${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'get' + }) +} + +// 新增${functionName} +export function add${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'post', + data: data + }) +} + +// 修改${functionName} +export function update${BusinessName}(data) { + return request({ + url: '/${moduleName}/${businessName}', + method: 'put', + data: data + }) +} + +// 删除${functionName} +export function del${BusinessName}(${pkColumn.javaField}) { + return request({ + url: '/${moduleName}/${businessName}/' + ${pkColumn.javaField}, + method: 'delete' + }) +} diff --git a/ruoyi-generator/src/main/resources/vm/sql/sql.vm b/ruoyi-generator/src/main/resources/vm/sql/sql.vm new file mode 100644 index 0000000..0575583 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/sql/sql.vm @@ -0,0 +1,22 @@ +-- 菜单 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}', '${parentMenuId}', '1', '${businessName}', '${moduleName}/${businessName}/index', 1, 0, 'C', '0', '0', '${permissionPrefix}:list', '#', 'admin', sysdate(), '', null, '${functionName}菜单'); + +-- 按钮父菜单ID +SELECT @parentId := LAST_INSERT_ID(); + +-- 按钮 SQL +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}查询', @parentId, '1', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:query', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}新增', @parentId, '2', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:add', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}修改', @parentId, '3', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:edit', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}删除', @parentId, '4', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:remove', '#', 'admin', sysdate(), '', null, ''); + +insert into sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) +values('${functionName}导出', @parentId, '5', '#', '', 1, 0, 'F', '0', '0', '${permissionPrefix}:export', '#', 'admin', sysdate(), '', null, ''); \ No newline at end of file diff --git a/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm new file mode 100644 index 0000000..4e35fc9 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index-tree.vue.vm @@ -0,0 +1,505 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm new file mode 100644 index 0000000..04ebe16 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/index.vue.vm @@ -0,0 +1,602 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm new file mode 100644 index 0000000..765a5e3 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index-tree.vue.vm @@ -0,0 +1,474 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm new file mode 100644 index 0000000..936b465 --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/index.vue.vm @@ -0,0 +1,590 @@ + + + diff --git a/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm new file mode 100644 index 0000000..456755b --- /dev/null +++ b/ruoyi-generator/src/main/resources/vm/xml/mapper.xml.vm @@ -0,0 +1,140 @@ + + + + + +#foreach ($column in $columns) + +#end + +#if($table.sub) + + + + + + +#foreach ($column in $subTable.columns) + +#end + +#end + + + select#foreach($column in $columns) $column.columnName#if($foreach.count != $columns.size()),#end#end from ${tableName} + + + + + +#if($table.sub) + + +#end + + + insert into ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + $column.columnName, +#end +#end + + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName || !$pkColumn.increment) + #{$column.javaField}, +#end +#end + + + + + update ${tableName} + +#foreach($column in $columns) +#if($column.columnName != $pkColumn.columnName) + $column.columnName = #{$column.javaField}, +#end +#end + + where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} = #{${pkColumn.javaField}} + + + + delete from ${tableName} where ${pkColumn.columnName} in + + #{${pkColumn.javaField}} + + +#if($table.sub) + + + delete from ${subTableName} where ${subTableFkName} in + + #{${subTableFkclassName}} + + + + + delete from ${subTableName} where ${subTableFkName} = #{${subTableFkclassName}} + + + + insert into ${subTableName}(#foreach($column in $subTable.columns) $column.columnName#if($foreach.count != $subTable.columns.size()),#end#end) values + + (#foreach($column in $subTable.columns) #{item.$column.javaField}#if($foreach.count != $subTable.columns.size()),#end#end) + + +#end + \ No newline at end of file diff --git a/ruoyi-quartz/pom.xml b/ruoyi-quartz/pom.xml new file mode 100644 index 0000000..0421087 --- /dev/null +++ b/ruoyi-quartz/pom.xml @@ -0,0 +1,40 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + + ruoyi-quartz + + + quartz定时任务 + + + + + + + org.quartz-scheduler + quartz + + + com.mchange + c3p0 + + + + + + + com.ruoyi + ruoyi-common + + + + + \ No newline at end of file diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java new file mode 100644 index 0000000..d4e065a --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/config/ScheduleConfig.java @@ -0,0 +1,57 @@ +//package com.ruoyi.quartz.config; +// +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +//import org.springframework.scheduling.quartz.SchedulerFactoryBean; +//import javax.sql.DataSource; +//import java.util.Properties; +// +///** +// * 定时任务配置(单机部署建议删除此类和qrtz数据库表,默认走内存会最高效) +// * +// * @author ruoyi +// */ +//@Configuration +//public class ScheduleConfig +//{ +// @Bean +// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) +// { +// SchedulerFactoryBean factory = new SchedulerFactoryBean(); +// factory.setDataSource(dataSource); +// +// // quartz参数 +// Properties prop = new Properties(); +// prop.put("org.quartz.scheduler.instanceName", "RuoyiScheduler"); +// prop.put("org.quartz.scheduler.instanceId", "AUTO"); +// // 线程池配置 +// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); +// prop.put("org.quartz.threadPool.threadCount", "20"); +// prop.put("org.quartz.threadPool.threadPriority", "5"); +// // JobStore配置 +// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); +// // 集群配置 +// prop.put("org.quartz.jobStore.isClustered", "true"); +// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); +// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "10"); +// prop.put("org.quartz.jobStore.txIsolationLevelSerializable", "true"); +// +// // sqlserver 启用 +// // prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); +// prop.put("org.quartz.jobStore.misfireThreshold", "12000"); +// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); +// factory.setQuartzProperties(prop); +// +// factory.setSchedulerName("RuoyiScheduler"); +// // 延时启动 +// factory.setStartupDelay(1); +// factory.setApplicationContextSchedulerContextKey("applicationContextKey"); +// // 可选,QuartzScheduler +// // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 +// factory.setOverwriteExistingJobs(true); +// // 设置自动启动,默认为true +// factory.setAutoStartup(true); +// +// return factory; +// } +//} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java new file mode 100644 index 0000000..f11aebf --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobController.java @@ -0,0 +1,185 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 调度任务信息操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/job") +public class SysJobController extends BaseController +{ + @Autowired + private ISysJobService jobService; + + /** + * 查询定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJob sysJob) + { + startPage(); + List list = jobService.selectJobList(sysJob); + return getDataTable(list); + } + + /** + * 导出定时任务列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "定时任务", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJob sysJob) + { + List list = jobService.selectJobList(sysJob); + ExcelUtil util = new ExcelUtil(SysJob.class); + util.exportExcel(response, list, "定时任务"); + } + + /** + * 获取定时任务详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobId}") + public AjaxResult getInfo(@PathVariable("jobId") Long jobId) + { + return success(jobService.selectJobById(jobId)); + } + + /** + * 新增定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:add')") + @Log(title = "定时任务", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("新增任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("新增任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setCreateBy(getUsername()); + return toAjax(jobService.insertJob(job)); + } + + /** + * 修改定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:edit')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysJob job) throws SchedulerException, TaskException + { + if (!CronUtils.isValid(job.getCronExpression())) + { + return error("修改任务'" + job.getJobName() + "'失败,Cron表达式不正确"); + } + else if (StringUtils.containsIgnoreCase(job.getInvokeTarget(), Constants.LOOKUP_RMI)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'rmi'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.LOOKUP_LDAP, Constants.LOOKUP_LDAPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'ldap(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), new String[] { Constants.HTTP, Constants.HTTPS })) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不允许'http(s)'调用"); + } + else if (StringUtils.containsAnyIgnoreCase(job.getInvokeTarget(), Constants.JOB_ERROR_STR)) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串存在违规"); + } + else if (!ScheduleUtils.whiteList(job.getInvokeTarget())) + { + return error("修改任务'" + job.getJobName() + "'失败,目标字符串不在白名单内"); + } + job.setUpdateBy(getUsername()); + return toAjax(jobService.updateJob(job)); + } + + /** + * 定时任务状态修改 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/changeStatus") + public AjaxResult changeStatus(@RequestBody SysJob job) throws SchedulerException + { + SysJob newJob = jobService.selectJobById(job.getJobId()); + newJob.setStatus(job.getStatus()); + return toAjax(jobService.changeStatus(newJob)); + } + + /** + * 定时任务立即执行一次 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:changeStatus')") + @Log(title = "定时任务", businessType = BusinessType.UPDATE) + @PutMapping("/run") + public AjaxResult run(@RequestBody SysJob job) throws SchedulerException + { + boolean result = jobService.run(job); + return result ? success() : error("任务不存在或已过期!"); + } + + /** + * 删除定时任务 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobIds}") + public AjaxResult remove(@PathVariable Long[] jobIds) throws SchedulerException + { + jobService.deleteJobByIds(jobIds); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java new file mode 100644 index 0000000..62ecbab --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/controller/SysJobLogController.java @@ -0,0 +1,92 @@ +package com.ruoyi.quartz.controller; + +import java.util.List; +import javax.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 调度日志操作处理 + * + * @author ruoyi + */ +@RestController +@RequestMapping("/monitor/jobLog") +public class SysJobLogController extends BaseController +{ + @Autowired + private ISysJobLogService jobLogService; + + /** + * 查询定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:list')") + @GetMapping("/list") + public TableDataInfo list(SysJobLog sysJobLog) + { + startPage(); + List list = jobLogService.selectJobLogList(sysJobLog); + return getDataTable(list); + } + + /** + * 导出定时任务调度日志列表 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:export')") + @Log(title = "任务调度日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(HttpServletResponse response, SysJobLog sysJobLog) + { + List list = jobLogService.selectJobLogList(sysJobLog); + ExcelUtil util = new ExcelUtil(SysJobLog.class); + util.exportExcel(response, list, "调度日志"); + } + + /** + * 根据调度编号获取详细信息 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:query')") + @GetMapping(value = "/{jobLogId}") + public AjaxResult getInfo(@PathVariable Long jobLogId) + { + return success(jobLogService.selectJobLogById(jobLogId)); + } + + + /** + * 删除定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "定时任务调度日志", businessType = BusinessType.DELETE) + @DeleteMapping("/{jobLogIds}") + public AjaxResult remove(@PathVariable Long[] jobLogIds) + { + return toAjax(jobLogService.deleteJobLogByIds(jobLogIds)); + } + + /** + * 清空定时任务调度日志 + */ + @PreAuthorize("@ss.hasPermi('monitor:job:remove')") + @Log(title = "调度日志", businessType = BusinessType.CLEAN) + @DeleteMapping("/clean") + public AjaxResult clean() + { + jobLogService.cleanJobLog(); + return success(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java new file mode 100644 index 0000000..4efff27 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java @@ -0,0 +1,175 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.quartz.util.CronUtils; + +/** + * 定时任务调度表 sys_job + * + * @author ruoyi + */ +public class SysJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 任务ID */ + @Excel(name = "任务序号", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long jobId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** cron执行表达式 */ + @Excel(name = "执行表达式 ") + private String cronExpression; + + /** cron计划策略 */ + @Excel(name = "计划策略 ", readConverterExp = "0=默认,1=立即触发执行,2=触发一次执行,3=不触发立即执行") + private String misfirePolicy = ScheduleConstants.MISFIRE_DEFAULT; + + /** 是否并发执行(0允许 1禁止) */ + @Excel(name = "并发执行", readConverterExp = "0=允许,1=禁止") + private String concurrent; + + /** 任务状态(0正常 1暂停) */ + @Excel(name = "任务状态", readConverterExp = "0=正常,1=暂停") + private String status; + + public Long getJobId() + { + return jobId; + } + + public void setJobId(Long jobId) + { + this.jobId = jobId; + } + + @NotBlank(message = "任务名称不能为空") + @Size(min = 0, max = 64, message = "任务名称不能超过64个字符") + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + @NotBlank(message = "调用目标字符串不能为空") + @Size(min = 0, max = 500, message = "调用目标字符串长度不能超过500个字符") + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + @NotBlank(message = "Cron执行表达式不能为空") + @Size(min = 0, max = 255, message = "Cron执行表达式不能超过255个字符") + public String getCronExpression() + { + return cronExpression; + } + + public void setCronExpression(String cronExpression) + { + this.cronExpression = cronExpression; + } + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + public Date getNextValidTime() + { + if (StringUtils.isNotEmpty(cronExpression)) + { + return CronUtils.getNextExecution(cronExpression); + } + return null; + } + + public String getMisfirePolicy() + { + return misfirePolicy; + } + + public void setMisfirePolicy(String misfirePolicy) + { + this.misfirePolicy = misfirePolicy; + } + + public String getConcurrent() + { + return concurrent; + } + + public void setConcurrent(String concurrent) + { + this.concurrent = concurrent; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobId", getJobId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("cronExpression", getCronExpression()) + .append("nextValidTime", getNextValidTime()) + .append("misfirePolicy", getMisfirePolicy()) + .append("concurrent", getConcurrent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java new file mode 100644 index 0000000..995e54e --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java @@ -0,0 +1,159 @@ +package com.ruoyi.quartz.domain; + +import java.util.Date; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 定时任务调度日志表 sys_job_log + * + * @author ruoyi + */ +public class SysJobLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "日志序号") + @JsonSerialize(using = ToStringSerializer.class) + private Long jobLogId; + + /** 任务名称 */ + @Excel(name = "任务名称") + private String jobName; + + /** 任务组名 */ + @Excel(name = "任务组名") + private String jobGroup; + + /** 调用目标字符串 */ + @Excel(name = "调用目标字符串") + private String invokeTarget; + + /** 日志信息 */ + @Excel(name = "日志信息") + private String jobMessage; + + /** 执行状态(0正常 1失败) */ + @Excel(name = "执行状态", readConverterExp = "0=正常,1=失败") + private String status; + + /** 异常信息 */ + @Excel(name = "异常信息") + private String exceptionInfo; + + /** 开始时间 */ + private Date startTime; + + /** 停止时间 */ + private Date stopTime; + + public Long getJobLogId() + { + return jobLogId; + } + + public void setJobLogId(Long jobLogId) + { + this.jobLogId = jobLogId; + } + + public String getJobName() + { + return jobName; + } + + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobGroup() + { + return jobGroup; + } + + public void setJobGroup(String jobGroup) + { + this.jobGroup = jobGroup; + } + + public String getInvokeTarget() + { + return invokeTarget; + } + + public void setInvokeTarget(String invokeTarget) + { + this.invokeTarget = invokeTarget; + } + + public String getJobMessage() + { + return jobMessage; + } + + public void setJobMessage(String jobMessage) + { + this.jobMessage = jobMessage; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getExceptionInfo() + { + return exceptionInfo; + } + + public void setExceptionInfo(String exceptionInfo) + { + this.exceptionInfo = exceptionInfo; + } + + public Date getStartTime() + { + return startTime; + } + + public void setStartTime(Date startTime) + { + this.startTime = startTime; + } + + public Date getStopTime() + { + return stopTime; + } + + public void setStopTime(Date stopTime) + { + this.stopTime = stopTime; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("jobLogId", getJobLogId()) + .append("jobName", getJobName()) + .append("jobGroup", getJobGroup()) + .append("jobMessage", getJobMessage()) + .append("status", getStatus()) + .append("exceptionInfo", getExceptionInfo()) + .append("startTime", getStartTime()) + .append("stopTime", getStopTime()) + .toString(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java new file mode 100644 index 0000000..727d916 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobLogMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 调度任务日志信息 数据层 + * + * @author ruoyi + */ +public interface SysJobLogMapper +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 查询所有调度任务日志 + * + * @return 调度任务日志列表 + */ + public List selectJobLogAll(); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + * @return 结果 + */ + public int insertJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java new file mode 100644 index 0000000..20f45db --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/SysJobMapper.java @@ -0,0 +1,67 @@ +package com.ruoyi.quartz.mapper; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 调度任务信息 数据层 + * + * @author ruoyi + */ +public interface SysJobMapper +{ + /** + * 查询调度任务日志集合 + * + * @param job 调度信息 + * @return 操作日志集合 + */ + public List selectJobList(SysJob job); + + /** + * 查询所有调度任务 + * + * @return 调度任务列表 + */ + public List selectJobAll(); + + /** + * 通过调度ID查询调度任务信息 + * + * @param jobId 调度ID + * @return 角色对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 通过调度ID删除调度任务信息 + * + * @param jobId 调度ID + * @return 结果 + */ + public int deleteJobById(Long jobId); + + /** + * 批量删除调度任务信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteJobByIds(Long[] ids); + + /** + * 修改调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int updateJob(SysJob job); + + /** + * 新增调度任务信息 + * + * @param job 调度任务信息 + * @return 结果 + */ + public int insertJob(SysJob job); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java new file mode 100644 index 0000000..8546792 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java @@ -0,0 +1,56 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import com.ruoyi.quartz.domain.SysJobLog; + +/** + * 定时任务调度日志信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobLogService +{ + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + public List selectJobLogList(SysJobLog jobLog); + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + public SysJobLog selectJobLogById(Long jobLogId); + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + public void addJobLog(SysJobLog jobLog); + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的日志ID + * @return 结果 + */ + public int deleteJobLogByIds(Long[] logIds); + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + * @return 结果 + */ + public int deleteJobLogById(Long jobId); + + /** + * 清空任务日志 + */ + public void cleanJobLog(); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java new file mode 100644 index 0000000..437ade8 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobService.java @@ -0,0 +1,102 @@ +package com.ruoyi.quartz.service; + +import java.util.List; +import org.quartz.SchedulerException; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务调度信息信息 服务层 + * + * @author ruoyi + */ +public interface ISysJobService +{ + /** + * 获取quartz调度器的计划任务 + * + * @param job 调度信息 + * @return 调度任务集合 + */ + public List selectJobList(SysJob job); + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + public SysJob selectJobById(Long jobId); + + /** + * 暂停任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int pauseJob(SysJob job) throws SchedulerException; + + /** + * 恢复任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int resumeJob(SysJob job) throws SchedulerException; + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + * @return 结果 + */ + public int deleteJob(SysJob job) throws SchedulerException; + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + public void deleteJobByIds(Long[] jobIds) throws SchedulerException; + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + * @return 结果 + */ + public int changeStatus(SysJob job) throws SchedulerException; + + /** + * 立即运行任务 + * + * @param job 调度信息 + * @return 结果 + */ + public boolean run(SysJob job) throws SchedulerException; + + /** + * 新增任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int insertJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 更新任务 + * + * @param job 调度信息 + * @return 结果 + */ + public int updateJob(SysJob job) throws SchedulerException, TaskException; + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + public boolean checkCronExpressionIsValid(String cronExpression); +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java new file mode 100644 index 0000000..7be6289 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java @@ -0,0 +1,91 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import com.ruoyi.common.utils.uuid.IdUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.mapper.SysJobLogMapper; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 定时任务调度日志信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobLogServiceImpl implements ISysJobLogService +{ + @Autowired + private SysJobLogMapper jobLogMapper; + + /** + * 获取quartz调度器日志的计划任务 + * + * @param jobLog 调度日志信息 + * @return 调度任务日志集合 + */ + @Override + public List selectJobLogList(SysJobLog jobLog) + { + return jobLogMapper.selectJobLogList(jobLog); + } + + /** + * 通过调度任务日志ID查询调度信息 + * + * @param jobLogId 调度任务日志ID + * @return 调度任务日志对象信息 + */ + @Override + public SysJobLog selectJobLogById(Long jobLogId) + { + return jobLogMapper.selectJobLogById(jobLogId); + } + + /** + * 新增任务日志 + * + * @param jobLog 调度日志信息 + */ + @Override + public void addJobLog(SysJobLog jobLog) + { + jobLog.setJobLogId(IdUtil.getSnowflake().nextId()); + jobLogMapper.insertJobLog(jobLog); + } + + /** + * 批量删除调度日志信息 + * + * @param logIds 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteJobLogByIds(Long[] logIds) + { + return jobLogMapper.deleteJobLogByIds(logIds); + } + + /** + * 删除任务日志 + * + * @param jobId 调度日志ID + */ + @Override + public int deleteJobLogById(Long jobId) + { + return jobLogMapper.deleteJobLogById(jobId); + } + + /** + * 清空任务日志 + */ + @Override + public void cleanJobLog() + { + jobLogMapper.cleanJobLog(); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java new file mode 100644 index 0000000..b5538b7 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobServiceImpl.java @@ -0,0 +1,263 @@ +package com.ruoyi.quartz.service.impl; + +import java.util.List; +import javax.annotation.PostConstruct; + +import cn.hutool.core.util.IdUtil; +import org.quartz.JobDataMap; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.mapper.SysJobMapper; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.quartz.util.CronUtils; +import com.ruoyi.quartz.util.ScheduleUtils; + +/** + * 定时任务调度信息 服务层 + * + * @author ruoyi + */ +@Service +public class SysJobServiceImpl implements ISysJobService +{ + @Autowired + private Scheduler scheduler; + + @Autowired + private SysJobMapper jobMapper; + + /** + * 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据) + */ + @PostConstruct + public void init() throws SchedulerException, TaskException + { + scheduler.clear(); + List jobList = jobMapper.selectJobAll(); + for (SysJob job : jobList) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + } + + /** + * 获取quartz调度器的计划任务列表 + * + * @param job 调度信息 + * @return + */ + @Override + public List selectJobList(SysJob job) + { + return jobMapper.selectJobList(job); + } + + /** + * 通过调度任务ID查询调度信息 + * + * @param jobId 调度任务ID + * @return 调度任务对象信息 + */ + @Override + public SysJob selectJobById(Long jobId) + { + return jobMapper.selectJobById(jobId); + } + + /** + * 暂停任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int pauseJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 恢复任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int resumeJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + job.setStatus(ScheduleConstants.Status.NORMAL.getValue()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 删除任务后,所对应的trigger也将被删除 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int deleteJob(SysJob job) throws SchedulerException + { + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + int rows = jobMapper.deleteJobById(jobId); + if (rows > 0) + { + scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + return rows; + } + + /** + * 批量删除调度信息 + * + * @param jobIds 需要删除的任务ID + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteJobByIds(Long[] jobIds) throws SchedulerException + { + for (Long jobId : jobIds) + { + SysJob job = jobMapper.selectJobById(jobId); + deleteJob(job); + } + } + + /** + * 任务调度状态修改 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int changeStatus(SysJob job) throws SchedulerException + { + int rows = 0; + String status = job.getStatus(); + if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) + { + rows = resumeJob(job); + } + else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) + { + rows = pauseJob(job); + } + return rows; + } + + /** + * 立即运行任务 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean run(SysJob job) throws SchedulerException + { + boolean result = false; + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + SysJob properties = selectJobById(job.getJobId()); + // 参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties); + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + result = true; + scheduler.triggerJob(jobKey, dataMap); + } + return result; + } + + /** + * 新增任务 + * + * @param job 调度信息 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int insertJob(SysJob job) throws SchedulerException, TaskException + { + job.setStatus(ScheduleConstants.Status.PAUSE.getValue()); + int rows = jobMapper.insertJob(job); + if (rows > 0) + { + ScheduleUtils.createScheduleJob(scheduler, job); + } + return rows; + } + + /** + * 更新任务的时间表达式 + * + * @param job 调度信息 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public int updateJob(SysJob job) throws SchedulerException, TaskException + { + SysJob properties = selectJobById(job.getJobId()); + int rows = jobMapper.updateJob(job); + if (rows > 0) + { + updateSchedulerJob(job, properties.getJobGroup()); + } + return rows; + } + + /** + * 更新任务 + * + * @param job 任务对象 + * @param jobGroup 任务组名 + */ + public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException + { + Long jobId = job.getJobId(); + // 判断是否存在 + JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup); + if (scheduler.checkExists(jobKey)) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(jobKey); + } + ScheduleUtils.createScheduleJob(scheduler, job); + } + + /** + * 校验cron表达式是否有效 + * + * @param cronExpression 表达式 + * @return 结果 + */ + @Override + public boolean checkCronExpressionIsValid(String cronExpression) + { + return CronUtils.isValid(cronExpression); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java new file mode 100644 index 0000000..853243b --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java @@ -0,0 +1,28 @@ +package com.ruoyi.quartz.task; + +import org.springframework.stereotype.Component; +import com.ruoyi.common.utils.StringUtils; + +/** + * 定时任务调度测试 + * + * @author ruoyi + */ +@Component("ryTask") +public class RyTask +{ + public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) + { + System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); + } + + public void ryParams(String params) + { + System.out.println("执行有参方法:" + params); + } + + public void ryNoParams() + { + System.out.println("执行无参方法"); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java new file mode 100644 index 0000000..eec1faf --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java @@ -0,0 +1,106 @@ +package com.ruoyi.quartz.util; + +import java.util.Date; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.utils.ExceptionUtil; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; +import com.ruoyi.quartz.domain.SysJobLog; +import com.ruoyi.quartz.service.ISysJobLogService; + +/** + * 抽象quartz调用 + * + * @author ruoyi + */ +public abstract class AbstractQuartzJob implements Job +{ + private static final Logger log = LoggerFactory.getLogger(AbstractQuartzJob.class); + + /** + * 线程本地变量 + */ + private static ThreadLocal threadLocal = new ThreadLocal<>(); + + @Override + public void execute(JobExecutionContext context) + { + SysJob sysJob = new SysJob(); + BeanUtils.copyBeanProp(sysJob, context.getMergedJobDataMap().get(ScheduleConstants.TASK_PROPERTIES)); + try + { + before(context, sysJob); + if (sysJob != null) + { + doExecute(context, sysJob); + } + after(context, sysJob, null); + } + catch (Exception e) + { + log.error("任务执行异常 - :", e); + after(context, sysJob, e); + } + } + + /** + * 执行前 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void before(JobExecutionContext context, SysJob sysJob) + { + threadLocal.set(new Date()); + } + + /** + * 执行后 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + */ + protected void after(JobExecutionContext context, SysJob sysJob, Exception e) + { + Date startTime = threadLocal.get(); + threadLocal.remove(); + + final SysJobLog sysJobLog = new SysJobLog(); + sysJobLog.setJobName(sysJob.getJobName()); + sysJobLog.setJobGroup(sysJob.getJobGroup()); + sysJobLog.setInvokeTarget(sysJob.getInvokeTarget()); + sysJobLog.setStartTime(startTime); + sysJobLog.setStopTime(new Date()); + long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); + sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); + if (e != null) + { + sysJobLog.setStatus(Constants.FAIL); + String errorMsg = StringUtils.substring(ExceptionUtil.getExceptionMessage(e), 0, 2000); + sysJobLog.setExceptionInfo(errorMsg); + } + else + { + sysJobLog.setStatus(Constants.SUCCESS); + } + + // 写入数据库当中 + SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog); + } + + /** + * 执行方法,由子类重载 + * + * @param context 工作执行上下文对象 + * @param sysJob 系统计划任务 + * @throws Exception 执行过程中的异常 + */ + protected abstract void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception; +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java new file mode 100644 index 0000000..dd53839 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/CronUtils.java @@ -0,0 +1,63 @@ +package com.ruoyi.quartz.util; + +import java.text.ParseException; +import java.util.Date; +import org.quartz.CronExpression; + +/** + * cron表达式工具类 + * + * @author ruoyi + * + */ +public class CronUtils +{ + /** + * 返回一个布尔值代表一个给定的Cron表达式的有效性 + * + * @param cronExpression Cron表达式 + * @return boolean 表达式是否有效 + */ + public static boolean isValid(String cronExpression) + { + return CronExpression.isValidExpression(cronExpression); + } + + /** + * 返回一个字符串值,表示该消息无效Cron表达式给出有效性 + * + * @param cronExpression Cron表达式 + * @return String 无效时返回表达式错误描述,如果有效返回null + */ + public static String getInvalidMessage(String cronExpression) + { + try + { + new CronExpression(cronExpression); + return null; + } + catch (ParseException pe) + { + return pe.getMessage(); + } + } + + /** + * 返回下一个执行时间根据给定的Cron表达式 + * + * @param cronExpression Cron表达式 + * @return Date 下次Cron表达式执行时间 + */ + public static Date getNextExecution(String cronExpression) + { + try + { + CronExpression cron = new CronExpression(cronExpression); + return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis())); + } + catch (ParseException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java new file mode 100644 index 0000000..dea8cde --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java @@ -0,0 +1,182 @@ +package com.ruoyi.quartz.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.LinkedList; +import java.util.List; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 任务执行工具 + * + * @author ruoyi + */ +public class JobInvokeUtil +{ + /** + * 执行方法 + * + * @param sysJob 系统任务 + */ + public static void invokeMethod(SysJob sysJob) throws Exception + { + String invokeTarget = sysJob.getInvokeTarget(); + String beanName = getBeanName(invokeTarget); + String methodName = getMethodName(invokeTarget); + List methodParams = getMethodParams(invokeTarget); + + if (!isValidClassName(beanName)) + { + Object bean = SpringUtils.getBean(beanName); + invokeMethod(bean, methodName, methodParams); + } + else + { + Object bean = Class.forName(beanName).getDeclaredConstructor().newInstance(); + invokeMethod(bean, methodName, methodParams); + } + } + + /** + * 调用任务方法 + * + * @param bean 目标对象 + * @param methodName 方法名称 + * @param methodParams 方法参数 + */ + private static void invokeMethod(Object bean, String methodName, List methodParams) + throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, + InvocationTargetException + { + if (StringUtils.isNotNull(methodParams) && methodParams.size() > 0) + { + Method method = bean.getClass().getMethod(methodName, getMethodParamsType(methodParams)); + method.invoke(bean, getMethodParamsValue(methodParams)); + } + else + { + Method method = bean.getClass().getMethod(methodName); + method.invoke(bean); + } + } + + /** + * 校验是否为为class包名 + * + * @param invokeTarget 名称 + * @return true是 false否 + */ + public static boolean isValidClassName(String invokeTarget) + { + return StringUtils.countMatches(invokeTarget, ".") > 1; + } + + /** + * 获取bean名称 + * + * @param invokeTarget 目标字符串 + * @return bean名称 + */ + public static String getBeanName(String invokeTarget) + { + String beanName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringBeforeLast(beanName, "."); + } + + /** + * 获取bean方法 + * + * @param invokeTarget 目标字符串 + * @return method方法 + */ + public static String getMethodName(String invokeTarget) + { + String methodName = StringUtils.substringBefore(invokeTarget, "("); + return StringUtils.substringAfterLast(methodName, "."); + } + + /** + * 获取method方法参数相关列表 + * + * @param invokeTarget 目标字符串 + * @return method方法相关参数列表 + */ + public static List getMethodParams(String invokeTarget) + { + String methodStr = StringUtils.substringBetweenLast(invokeTarget, "(", ")"); + if (StringUtils.isEmpty(methodStr)) + { + return null; + } + String[] methodParams = methodStr.split(",(?=([^\"']*[\"'][^\"']*[\"'])*[^\"']*$)"); + List classs = new LinkedList<>(); + for (int i = 0; i < methodParams.length; i++) + { + String str = StringUtils.trimToEmpty(methodParams[i]); + // String字符串类型,以'或"开头 + if (StringUtils.startsWithAny(str, "'", "\"")) + { + classs.add(new Object[] { StringUtils.substring(str, 1, str.length() - 1), String.class }); + } + // boolean布尔类型,等于true或者false + else if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) + { + classs.add(new Object[] { Boolean.valueOf(str), Boolean.class }); + } + // long长整形,以L结尾 + else if (StringUtils.endsWith(str, "L")) + { + classs.add(new Object[] { Long.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Long.class }); + } + // double浮点类型,以D结尾 + else if (StringUtils.endsWith(str, "D")) + { + classs.add(new Object[] { Double.valueOf(StringUtils.substring(str, 0, str.length() - 1)), Double.class }); + } + // 其他类型归类为整形 + else + { + classs.add(new Object[] { Integer.valueOf(str), Integer.class }); + } + } + return classs; + } + + /** + * 获取参数类型 + * + * @param methodParams 参数相关列表 + * @return 参数类型列表 + */ + public static Class[] getMethodParamsType(List methodParams) + { + Class[] classs = new Class[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Class) os[1]; + index++; + } + return classs; + } + + /** + * 获取参数值 + * + * @param methodParams 参数相关列表 + * @return 参数值列表 + */ + public static Object[] getMethodParamsValue(List methodParams) + { + Object[] classs = new Object[methodParams.size()]; + int index = 0; + for (Object[] os : methodParams) + { + classs[index] = (Object) os[0]; + index++; + } + return classs; + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java new file mode 100644 index 0000000..5e13558 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java @@ -0,0 +1,21 @@ +package com.ruoyi.quartz.util; + +import org.quartz.DisallowConcurrentExecution; +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(禁止并发执行) + * + * @author ruoyi + * + */ +@DisallowConcurrentExecution +public class QuartzDisallowConcurrentExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java new file mode 100644 index 0000000..e975326 --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java @@ -0,0 +1,19 @@ +package com.ruoyi.quartz.util; + +import org.quartz.JobExecutionContext; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务处理(允许并发执行) + * + * @author ruoyi + * + */ +public class QuartzJobExecution extends AbstractQuartzJob +{ + @Override + protected void doExecute(JobExecutionContext context, SysJob sysJob) throws Exception + { + JobInvokeUtil.invokeMethod(sysJob); + } +} diff --git a/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java new file mode 100644 index 0000000..21fedae --- /dev/null +++ b/ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/ScheduleUtils.java @@ -0,0 +1,141 @@ +package com.ruoyi.quartz.util; + +import org.quartz.CronScheduleBuilder; +import org.quartz.CronTrigger; +import org.quartz.Job; +import org.quartz.JobBuilder; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerBuilder; +import org.quartz.TriggerKey; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.ScheduleConstants; +import com.ruoyi.common.exception.job.TaskException; +import com.ruoyi.common.exception.job.TaskException.Code; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.quartz.domain.SysJob; + +/** + * 定时任务工具类 + * + * @author ruoyi + * + */ +public class ScheduleUtils +{ + /** + * 得到quartz任务类 + * + * @param sysJob 执行计划 + * @return 具体执行任务类 + */ + private static Class getQuartzJobClass(SysJob sysJob) + { + boolean isConcurrent = "0".equals(sysJob.getConcurrent()); + return isConcurrent ? QuartzJobExecution.class : QuartzDisallowConcurrentExecution.class; + } + + /** + * 构建任务触发对象 + */ + public static TriggerKey getTriggerKey(Long jobId, String jobGroup) + { + return TriggerKey.triggerKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 构建任务键对象 + */ + public static JobKey getJobKey(Long jobId, String jobGroup) + { + return JobKey.jobKey(ScheduleConstants.TASK_CLASS_NAME + jobId, jobGroup); + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, SysJob job) throws SchedulerException, TaskException + { + Class jobClass = getQuartzJobClass(job); + // 构建job信息 + Long jobId = job.getJobId(); + String jobGroup = job.getJobGroup(); + JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(getJobKey(jobId, jobGroup)).build(); + + // 表达式调度构建器 + CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(job.getCronExpression()); + cronScheduleBuilder = handleCronScheduleMisfirePolicy(job, cronScheduleBuilder); + + // 按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(jobId, jobGroup)) + .withSchedule(cronScheduleBuilder).build(); + + // 放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(ScheduleConstants.TASK_PROPERTIES, job); + + // 判断是否存在 + if (scheduler.checkExists(getJobKey(jobId, jobGroup))) + { + // 防止创建时存在数据问题 先移除,然后在执行创建操作 + scheduler.deleteJob(getJobKey(jobId, jobGroup)); + } + + // 判断任务是否过期 + if (StringUtils.isNotNull(CronUtils.getNextExecution(job.getCronExpression()))) + { + // 执行调度任务 + scheduler.scheduleJob(jobDetail, trigger); + } + + // 暂停任务 + if (job.getStatus().equals(ScheduleConstants.Status.PAUSE.getValue())) + { + scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup)); + } + } + + /** + * 设置定时任务策略 + */ + public static CronScheduleBuilder handleCronScheduleMisfirePolicy(SysJob job, CronScheduleBuilder cb) + throws TaskException + { + switch (job.getMisfirePolicy()) + { + case ScheduleConstants.MISFIRE_DEFAULT: + return cb; + case ScheduleConstants.MISFIRE_IGNORE_MISFIRES: + return cb.withMisfireHandlingInstructionIgnoreMisfires(); + case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED: + return cb.withMisfireHandlingInstructionFireAndProceed(); + case ScheduleConstants.MISFIRE_DO_NOTHING: + return cb.withMisfireHandlingInstructionDoNothing(); + default: + throw new TaskException("The task misfire policy '" + job.getMisfirePolicy() + + "' cannot be used in cron schedule tasks", Code.CONFIG_ERROR); + } + } + + /** + * 检查包名是否为白名单配置 + * + * @param invokeTarget 目标字符串 + * @return 结果 + */ + public static boolean whiteList(String invokeTarget) + { + String packageName = StringUtils.substringBefore(invokeTarget, "("); + int count = StringUtils.countMatches(packageName, "."); + if (count > 1) + { + return StringUtils.containsAnyIgnoreCase(invokeTarget, Constants.JOB_WHITELIST_STR); + } + Object obj = SpringUtils.getBean(StringUtils.split(invokeTarget, ".")[0]); + String beanPackageName = obj.getClass().getPackage().getName(); + return StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_WHITELIST_STR) + && !StringUtils.containsAnyIgnoreCase(beanPackageName, Constants.JOB_ERROR_STR); + } +} diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml new file mode 100644 index 0000000..3bb64e6 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobLogMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + select job_log_id, job_name, job_group, invoke_target, job_message, status, exception_info, create_time + from sys_job_log + + + + + + + + + + delete from sys_job_log where job_log_id = #{jobLogId} + + + + delete from sys_job_log where job_log_id in + + #{jobLogId} + + + + + truncate table sys_job_log + + + + insert into sys_job_log( + job_log_id, + job_name, + job_group, + invoke_target, + job_message, + status, + exception_info, + create_time + )values( + #{jobLogId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{jobMessage}, + #{status}, + #{exceptionInfo}, + GETDATE() + ) + + + diff --git a/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml new file mode 100644 index 0000000..e227991 --- /dev/null +++ b/ruoyi-quartz/src/main/resources/mapper/quartz/SysJobMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + select job_id, job_name, job_group, invoke_target, cron_expression, misfire_policy, concurrent, status, create_by, create_time, remark + from sys_job + + + + + + + + + + delete from sys_job where job_id = #{jobId} + + + + delete from sys_job where job_id in + + #{jobId} + + + + + update sys_job + + job_name = #{jobName}, + job_group = #{jobGroup}, + invoke_target = #{invokeTarget}, + cron_expression = #{cronExpression}, + misfire_policy = #{misfirePolicy}, + concurrent = #{concurrent}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where job_id = #{jobId} + + + + insert into sys_job( + job_id, + job_name, + job_group, + invoke_target, + cron_expression, + misfire_policy, + concurrent, + status, + remark, + create_by, + create_time + )values( + #{jobId}, + #{jobName}, + #{jobGroup}, + #{invokeTarget}, + #{cronExpression}, + #{misfirePolicy}, + #{concurrent}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + diff --git a/ruoyi-system/pom.xml b/ruoyi-system/pom.xml new file mode 100644 index 0000000..6248a2c --- /dev/null +++ b/ruoyi-system/pom.xml @@ -0,0 +1,28 @@ + + + + ruoyi + com.ruoyi + 3.8.9 + + 4.0.0 + + ruoyi-system + + + system系统模块 + + + + + + + com.ruoyi + ruoyi-common + + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java new file mode 100644 index 0000000..83f0703 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysCache.java @@ -0,0 +1,81 @@ +package com.ruoyi.system.domain; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 缓存信息 + * + * @author ruoyi + */ +public class SysCache +{ + /** 缓存名称 */ + private String cacheName = ""; + + /** 缓存键名 */ + private String cacheKey = ""; + + /** 缓存内容 */ + private String cacheValue = ""; + + /** 备注 */ + private String remark = ""; + + public SysCache() + { + + } + + public SysCache(String cacheName, String remark) + { + this.cacheName = cacheName; + this.remark = remark; + } + + public SysCache(String cacheName, String cacheKey, String cacheValue) + { + this.cacheName = StringUtils.replace(cacheName, ":", ""); + this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); + this.cacheValue = cacheValue; + } + + public String getCacheName() + { + return cacheName; + } + + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } + + public String getCacheKey() + { + return cacheKey; + } + + public void setCacheKey(String cacheKey) + { + this.cacheKey = cacheKey; + } + + public String getCacheValue() + { + return cacheValue; + } + + public void setCacheValue(String cacheValue) + { + this.cacheValue = cacheValue; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java new file mode 100644 index 0000000..3b20c61 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysConfig.java @@ -0,0 +1,115 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 参数配置表 sys_config + * + * @author ruoyi + */ +public class SysConfig extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 参数主键 */ + @Excel(name = "参数主键", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long configId; + + /** 参数名称 */ + @Excel(name = "参数名称") + private String configName; + + /** 参数键名 */ + @Excel(name = "参数键名") + private String configKey; + + /** 参数键值 */ + @Excel(name = "参数键值") + private String configValue; + + /** 系统内置(Y是 N否) */ + @Excel(name = "系统内置", readConverterExp = "Y=是,N=否") + private String configType; + + public Long getConfigId() + { + return configId; + } + + public void setConfigId(Long configId) + { + this.configId = configId; + } + + @NotBlank(message = "参数名称不能为空") + @Size(min = 0, max = 100, message = "参数名称不能超过100个字符") + public String getConfigName() + { + return configName; + } + + public void setConfigName(String configName) + { + this.configName = configName; + } + + @NotBlank(message = "参数键名长度不能为空") + @Size(min = 0, max = 100, message = "参数键名长度不能超过100个字符") + public String getConfigKey() + { + return configKey; + } + + public void setConfigKey(String configKey) + { + this.configKey = configKey; + } + + @NotBlank(message = "参数键值不能为空") + @Size(min = 0, max = 500, message = "参数键值长度不能超过500个字符") + public String getConfigValue() + { + return configValue; + } + + public void setConfigValue(String configValue) + { + this.configValue = configValue; + } + + public String getConfigType() + { + return configType; + } + + public void setConfigType(String configType) + { + this.configType = configType; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("configId", getConfigId()) + .append("configName", getConfigName()) + .append("configKey", getConfigKey()) + .append("configValue", getConfigValue()) + .append("configType", getConfigType()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java new file mode 100644 index 0000000..0f5c456 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysLogininfor.java @@ -0,0 +1,147 @@ +package com.ruoyi.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 系统访问记录表 sys_logininfor + * + * @author ruoyi + */ +public class SysLogininfor extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** ID */ + @Excel(name = "序号", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long infoId; + + /** 用户账号 */ + @Excel(name = "用户账号") + private String userName; + + /** 登录状态 0成功 1失败 */ + @Excel(name = "登录状态", readConverterExp = "0=成功,1=失败") + private String status; + + /** 登录IP地址 */ + @Excel(name = "登录地址") + private String ipaddr; + + /** 登录地点 */ + @Excel(name = "登录地点") + private String loginLocation; + + /** 浏览器类型 */ + @Excel(name = "浏览器") + private String browser; + + /** 操作系统 */ + @Excel(name = "操作系统") + private String os; + + /** 提示消息 */ + @Excel(name = "提示消息") + private String msg; + + /** 访问时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "访问时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date loginTime; + + public Long getInfoId() + { + return infoId; + } + + public void setInfoId(Long infoId) + { + this.infoId = infoId; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public String getMsg() + { + return msg; + } + + public void setMsg(String msg) + { + this.msg = msg; + } + + public Date getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Date loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java new file mode 100644 index 0000000..7887309 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysNotice.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.xss.Xss; + +/** + * 通知公告表 sys_notice + * + * @author ruoyi + */ +public class SysNotice extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 公告ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long noticeId; + + /** 公告标题 */ + private String noticeTitle; + + /** 公告类型(1通知 2公告) */ + private String noticeType; + + /** 公告内容 */ + private String noticeContent; + + /** 公告状态(0正常 1关闭) */ + private String status; + + public Long getNoticeId() + { + return noticeId; + } + + public void setNoticeId(Long noticeId) + { + this.noticeId = noticeId; + } + + public void setNoticeTitle(String noticeTitle) + { + this.noticeTitle = noticeTitle; + } + + @Xss(message = "公告标题不能包含脚本字符") + @NotBlank(message = "公告标题不能为空") + @Size(min = 0, max = 50, message = "公告标题不能超过50个字符") + public String getNoticeTitle() + { + return noticeTitle; + } + + public void setNoticeType(String noticeType) + { + this.noticeType = noticeType; + } + + public String getNoticeType() + { + return noticeType; + } + + public void setNoticeContent(String noticeContent) + { + this.noticeContent = noticeContent; + } + + public String getNoticeContent() + { + return noticeContent; + } + + public void setStatus(String status) + { + this.status = status; + } + + public String getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("noticeId", getNoticeId()) + .append("noticeTitle", getNoticeTitle()) + .append("noticeType", getNoticeType()) + .append("noticeContent", getNoticeContent()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java new file mode 100644 index 0000000..5ebafdd --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysOperLog.java @@ -0,0 +1,272 @@ +package com.ruoyi.system.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 操作日志记录表 oper_log + * + * @author ruoyi + */ +public class SysOperLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 日志主键 */ + @Excel(name = "操作序号", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long operId; + + /** 操作模块 */ + @Excel(name = "操作模块") + private String title; + + /** 业务类型(0其它 1新增 2修改 3删除) */ + @Excel(name = "业务类型", readConverterExp = "0=其它,1=新增,2=修改,3=删除,4=授权,5=导出,6=导入,7=强退,8=生成代码,9=清空数据") + private Integer businessType; + + /** 业务类型数组 */ + private Integer[] businessTypes; + + /** 请求方法 */ + @Excel(name = "请求方法") + private String method; + + /** 请求方式 */ + @Excel(name = "请求方式") + private String requestMethod; + + /** 操作类别(0其它 1后台用户 2手机端用户) */ + @Excel(name = "操作类别", readConverterExp = "0=其它,1=后台用户,2=手机端用户") + private Integer operatorType; + + /** 操作人员 */ + @Excel(name = "操作人员") + private String operName; + + /** 部门名称 */ + @Excel(name = "部门名称") + private String deptName; + + /** 请求url */ + @Excel(name = "请求地址") + private String operUrl; + + /** 操作地址 */ + @Excel(name = "操作地址") + private String operIp; + + /** 操作地点 */ + @Excel(name = "操作地点") + private String operLocation; + + /** 请求参数 */ + @Excel(name = "请求参数") + private String operParam; + + /** 返回参数 */ + @Excel(name = "返回参数") + private String jsonResult; + + /** 操作状态(0正常 1异常) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=异常") + private Integer status; + + /** 错误消息 */ + @Excel(name = "错误消息") + private String errorMsg; + + /** 操作时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + @Excel(name = "操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") + private Date operTime; + + /** 消耗时间 */ + @Excel(name = "消耗时间", suffix = "毫秒") + private Long costTime; + + public Long getOperId() + { + return operId; + } + + public void setOperId(Long operId) + { + this.operId = operId; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public Integer getBusinessType() + { + return businessType; + } + + public void setBusinessType(Integer businessType) + { + this.businessType = businessType; + } + + public Integer[] getBusinessTypes() + { + return businessTypes; + } + + public void setBusinessTypes(Integer[] businessTypes) + { + this.businessTypes = businessTypes; + } + + public String getMethod() + { + return method; + } + + public void setMethod(String method) + { + this.method = method; + } + + public String getRequestMethod() + { + return requestMethod; + } + + public void setRequestMethod(String requestMethod) + { + this.requestMethod = requestMethod; + } + + public Integer getOperatorType() + { + return operatorType; + } + + public void setOperatorType(Integer operatorType) + { + this.operatorType = operatorType; + } + + public String getOperName() + { + return operName; + } + + public void setOperName(String operName) + { + this.operName = operName; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getOperUrl() + { + return operUrl; + } + + public void setOperUrl(String operUrl) + { + this.operUrl = operUrl; + } + + public String getOperIp() + { + return operIp; + } + + public void setOperIp(String operIp) + { + this.operIp = operIp; + } + + public String getOperLocation() + { + return operLocation; + } + + public void setOperLocation(String operLocation) + { + this.operLocation = operLocation; + } + + public String getOperParam() + { + return operParam; + } + + public void setOperParam(String operParam) + { + this.operParam = operParam; + } + + public String getJsonResult() + { + return jsonResult; + } + + public void setJsonResult(String jsonResult) + { + this.jsonResult = jsonResult; + } + + public Integer getStatus() + { + return status; + } + + public void setStatus(Integer status) + { + this.status = status; + } + + public String getErrorMsg() + { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) + { + this.errorMsg = errorMsg; + } + + public Date getOperTime() + { + return operTime; + } + + public void setOperTime(Date operTime) + { + this.operTime = operTime; + } + + public Long getCostTime() + { + return costTime; + } + + public void setCostTime(Long costTime) + { + this.costTime = costTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java new file mode 100644 index 0000000..d2f28bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysPost.java @@ -0,0 +1,128 @@ +package com.ruoyi.system.domain; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.domain.BaseEntity; + +/** + * 岗位表 sys_post + * + * @author ruoyi + */ +public class SysPost extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 岗位序号 */ + @Excel(name = "岗位序号", cellType = ColumnType.NUMERIC) + @JsonSerialize(using = ToStringSerializer.class) + private Long postId; + + /** 岗位编码 */ + @Excel(name = "岗位编码") + private String postCode; + + /** 岗位名称 */ + @Excel(name = "岗位名称") + private String postName; + + /** 岗位排序 */ + @Excel(name = "岗位排序") + private Integer postSort; + + /** 状态(0正常 1停用) */ + @Excel(name = "状态", readConverterExp = "0=正常,1=停用") + private String status; + + /** 用户是否存在此岗位标识 默认不存在 */ + private boolean flag = false; + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @NotBlank(message = "岗位编码不能为空") + @Size(min = 0, max = 64, message = "岗位编码长度不能超过64个字符") + public String getPostCode() + { + return postCode; + } + + public void setPostCode(String postCode) + { + this.postCode = postCode; + } + + @NotBlank(message = "岗位名称不能为空") + @Size(min = 0, max = 50, message = "岗位名称长度不能超过50个字符") + public String getPostName() + { + return postName; + } + + public void setPostName(String postName) + { + this.postName = postName; + } + + @NotNull(message = "显示顺序不能为空") + public Integer getPostSort() + { + return postSort; + } + + public void setPostSort(Integer postSort) + { + this.postSort = postSort; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public boolean isFlag() + { + return flag; + } + + public void setFlag(boolean flag) + { + this.flag = flag; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("postId", getPostId()) + .append("postCode", getPostCode()) + .append("postName", getPostName()) + .append("postSort", getPostSort()) + .append("status", getStatus()) + .append("createBy", getCreateBy()) + .append("createTime", getCreateTime()) + .append("updateBy", getUpdateBy()) + .append("updateTime", getUpdateTime()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java new file mode 100644 index 0000000..8ad0b24 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleDept.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.domain; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和部门关联 sys_role_dept + * + * @author ruoyi + */ +public class SysRoleDept +{ + /** 角色ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long roleId; + + /** 部门ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getDeptId() + { + return deptId; + } + + public void setDeptId(Long deptId) + { + this.deptId = deptId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("deptId", getDeptId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java new file mode 100644 index 0000000..84a215f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysRoleMenu.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.domain; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 角色和菜单关联 sys_role_menu + * + * @author ruoyi + */ +public class SysRoleMenu +{ + /** 角色ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long roleId; + + /** 菜单ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long menuId; + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + public Long getMenuId() + { + return menuId; + } + + public void setMenuId(Long menuId) + { + this.menuId = menuId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("roleId", getRoleId()) + .append("menuId", getMenuId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java new file mode 100644 index 0000000..2bbd318 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserOnline.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.domain; + +/** + * 当前在线会话 + * + * @author ruoyi + */ +public class SysUserOnline +{ + /** 会话编号 */ + private String tokenId; + + /** 部门名称 */ + private String deptName; + + /** 用户名称 */ + private String userName; + + /** 登录IP地址 */ + private String ipaddr; + + /** 登录地址 */ + private String loginLocation; + + /** 浏览器类型 */ + private String browser; + + /** 操作系统 */ + private String os; + + /** 登录时间 */ + private Long loginTime; + + public String getTokenId() + { + return tokenId; + } + + public void setTokenId(String tokenId) + { + this.tokenId = tokenId; + } + + public String getDeptName() + { + return deptName; + } + + public void setDeptName(String deptName) + { + this.deptName = deptName; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getIpaddr() + { + return ipaddr; + } + + public void setIpaddr(String ipaddr) + { + this.ipaddr = ipaddr; + } + + public String getLoginLocation() + { + return loginLocation; + } + + public void setLoginLocation(String loginLocation) + { + this.loginLocation = loginLocation; + } + + public String getBrowser() + { + return browser; + } + + public void setBrowser(String browser) + { + this.browser = browser; + } + + public String getOs() + { + return os; + } + + public void setOs(String os) + { + this.os = os; + } + + public Long getLoginTime() + { + return loginTime; + } + + public void setLoginTime(Long loginTime) + { + this.loginTime = loginTime; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java new file mode 100644 index 0000000..75ab155 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserPost.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.domain; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和岗位关联 sys_user_post + * + * @author ruoyi + */ +public class SysUserPost +{ + /** 用户ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + /** 岗位ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long postId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getPostId() + { + return postId; + } + + public void setPostId(Long postId) + { + this.postId = postId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("postId", getPostId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java new file mode 100644 index 0000000..890f96d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/SysUserRole.java @@ -0,0 +1,50 @@ +package com.ruoyi.system.domain; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 用户和角色关联 sys_user_role + * + * @author ruoyi + */ +public class SysUserRole +{ + /** 用户ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long userId; + + /** 角色ID */ + @JsonSerialize(using = ToStringSerializer.class) + private Long roleId; + + public Long getUserId() + { + return userId; + } + + public void setUserId(Long userId) + { + this.userId = userId; + } + + public Long getRoleId() + { + return roleId; + } + + public void setRoleId(Long roleId) + { + this.roleId = roleId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("userId", getUserId()) + .append("roleId", getRoleId()) + .toString(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java new file mode 100644 index 0000000..a5d5fdc --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/MetaVo.java @@ -0,0 +1,106 @@ +package com.ruoyi.system.domain.vo; + +import com.ruoyi.common.utils.StringUtils; + +/** + * 路由显示信息 + * + * @author ruoyi + */ +public class MetaVo +{ + /** + * 设置该路由在侧边栏和面包屑中展示的名字 + */ + private String title; + + /** + * 设置该路由的图标,对应路径src/assets/icons/svg + */ + private String icon; + + /** + * 设置为true,则不会被 缓存 + */ + private boolean noCache; + + /** + * 内链地址(http(s)://开头) + */ + private String link; + + public MetaVo() + { + } + + public MetaVo(String title, String icon) + { + this.title = title; + this.icon = icon; + } + + public MetaVo(String title, String icon, boolean noCache) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + } + + public MetaVo(String title, String icon, String link) + { + this.title = title; + this.icon = icon; + this.link = link; + } + + public MetaVo(String title, String icon, boolean noCache, String link) + { + this.title = title; + this.icon = icon; + this.noCache = noCache; + if (StringUtils.ishttp(link)) + { + this.link = link; + } + } + + public boolean isNoCache() + { + return noCache; + } + + public void setNoCache(boolean noCache) + { + this.noCache = noCache; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getIcon() + { + return icon; + } + + public void setIcon(String icon) + { + this.icon = icon; + } + + public String getLink() + { + return link; + } + + public void setLink(String link) + { + this.link = link; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java new file mode 100644 index 0000000..afff8c9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/domain/vo/RouterVo.java @@ -0,0 +1,148 @@ +package com.ruoyi.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import java.util.List; + +/** + * 路由配置信息 + * + * @author ruoyi + */ +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class RouterVo +{ + /** + * 路由名字 + */ + private String name; + + /** + * 路由地址 + */ + private String path; + + /** + * 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现 + */ + private boolean hidden; + + /** + * 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + */ + private String redirect; + + /** + * 组件地址 + */ + private String component; + + /** + * 路由参数:如 {"id": 1, "name": "ry"} + */ + private String query; + + /** + * 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + */ + private Boolean alwaysShow; + + /** + * 其他元素 + */ + private MetaVo meta; + + /** + * 子路由 + */ + private List children; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getPath() + { + return path; + } + + public void setPath(String path) + { + this.path = path; + } + + public boolean getHidden() + { + return hidden; + } + + public void setHidden(boolean hidden) + { + this.hidden = hidden; + } + + public String getRedirect() + { + return redirect; + } + + public void setRedirect(String redirect) + { + this.redirect = redirect; + } + + public String getComponent() + { + return component; + } + + public void setComponent(String component) + { + this.component = component; + } + + public String getQuery() + { + return query; + } + + public void setQuery(String query) + { + this.query = query; + } + + public Boolean getAlwaysShow() + { + return alwaysShow; + } + + public void setAlwaysShow(Boolean alwaysShow) + { + this.alwaysShow = alwaysShow; + } + + public MetaVo getMeta() + { + return meta; + } + + public void setMeta(MetaVo meta) + { + this.meta = meta; + } + + public List getChildren() + { + return children; + } + + public void setChildren(List children) + { + this.children = children; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java new file mode 100644 index 0000000..13d49d6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysConfigMapper.java @@ -0,0 +1,76 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 数据层 + * + * @author ruoyi + */ +public interface SysConfigMapper +{ + /** + * 查询参数配置信息 + * + * @param config 参数配置信息 + * @return 参数配置信息 + */ + public SysConfig selectConfig(SysConfig config); + + /** + * 通过ID查询配置 + * + * @param configId 参数ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数配置信息 + */ + public SysConfig checkConfigKeyUnique(String configKey); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 删除参数配置 + * + * @param configId 参数ID + * @return 结果 + */ + public int deleteConfigById(Long configId); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + * @return 结果 + */ + public int deleteConfigByIds(Long[] configIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java new file mode 100644 index 0000000..384a9b6 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDeptMapper.java @@ -0,0 +1,118 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 数据层 + * + * @author ruoyi + */ +public interface SysDeptMapper +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @param deptCheckStrictly 部门树选择项是否关联显示 + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(@Param("roleId") Long roleId, @Param("deptCheckStrictly") boolean deptCheckStrictly); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门 + * + * @param deptId 部门ID + * @return 部门列表 + */ + public List selectChildrenDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public int hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 + */ + public int checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param deptName 部门名称 + * @param parentId 父部门ID + * @return 结果 + */ + public SysDept checkDeptNameUnique(@Param("deptName") String deptName, @Param("parentId") Long parentId); + + /** + * 新增部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 修改所在部门正常状态 + * + * @param deptIds 部门ID组 + */ + public void updateDeptStatusNormal(Long[] deptIds); + + /** + * 修改子元素关系 + * + * @param depts 子元素 + * @return 结果 + */ + public int updateDeptChildren(@Param("depts") List depts); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..a341f1e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictDataMapper.java @@ -0,0 +1,95 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictDataMapper +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(@Param("dictType") String dictType, @Param("dictValue") String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据 + */ + public int countDictDataByType(String dictType); + + /** + * 通过字典ID删除字典数据信息 + * + * @param dictCode 字典数据ID + * @return 结果 + */ + public int deleteDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + * @return 结果 + */ + public int deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); + + /** + * 同步修改字典类型 + * + * @param oldDictType 旧字典类型 + * @param newDictType 新旧字典类型 + * @return 结果 + */ + public int updateDictDataType(@Param("oldDictType") String oldDictType, @Param("newDictType") String newDictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..5fb48fb --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysDictTypeMapper.java @@ -0,0 +1,83 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典表 数据层 + * + * @author ruoyi + */ +public interface SysDictTypeMapper +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 通过字典ID删除字典信息 + * + * @param dictId 字典ID + * @return 结果 + */ + public int deleteDictTypeById(Long dictId); + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + * @return 结果 + */ + public int deleteDictTypeByIds(Long[] dictIds); + + /** + * 新增字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public SysDictType checkDictTypeUnique(String dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java new file mode 100644 index 0000000..629866f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysLogininforMapper.java @@ -0,0 +1,42 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 数据层 + * + * @author ruoyi + */ +public interface SysLogininforMapper +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + * + * @return 结果 + */ + public int cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java new file mode 100644 index 0000000..99c0c50 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysMenuMapper.java @@ -0,0 +1,125 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysMenu; + +/** + * 菜单表 数据层 + * + * @author ruoyi + */ +public interface SysMenuMapper +{ + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu); + + /** + * 根据用户所有权限 + * + * @return 权限列表 + */ + public List selectMenuPerms(); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + public List selectMenuListByUserId(SysMenu menu); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public List selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public List selectMenuPermsByUserId(Long userId); + + /** + * 根据用户ID查询菜单 + * + * @return 菜单列表 + */ + public List selectMenuTreeAll(); + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @param menuCheckStrictly 菜单树选择项是否关联显示 + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(@Param("roleId") Long roleId, @Param("menuCheckStrictly") boolean menuCheckStrictly); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int hasChildByMenuId(Long menuId); + + /** + * 新增菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menuName 菜单名称 + * @param parentId 父菜单ID + * @return 结果 + */ + public SysMenu checkMenuNameUnique(@Param("menuName") String menuName, @Param("parentId") Long parentId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..c34f0a2 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysNoticeMapper.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 通知公告表 数据层 + * + * @author ruoyi + */ +public interface SysNoticeMapper +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 批量删除公告 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java new file mode 100644 index 0000000..2ae6457 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysOperLogMapper.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 数据层 + * + * @author ruoyi + */ +public interface SysOperLogMapper +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java new file mode 100644 index 0000000..19be227 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysPostMapper.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 数据层 + * + * @author ruoyi + */ +public interface SysPostMapper +{ + /** + * 查询岗位数据集合 + * + * @param post 岗位信息 + * @return 岗位数据集合 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public List selectPostsByUserName(String userName); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 修改岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); + + /** + * 新增岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 校验岗位名称 + * + * @param postName 岗位名称 + * @return 结果 + */ + public SysPost checkPostNameUnique(String postName); + + /** + * 校验岗位编码 + * + * @param postCode 岗位编码 + * @return 结果 + */ + public SysPost checkPostCodeUnique(String postCode); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java new file mode 100644 index 0000000..f9d3a2f --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleDeptMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleDept; + +/** + * 角色与部门关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleDeptMapper +{ + /** + * 通过角色ID删除角色和部门关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleDeptByRoleId(Long roleId); + + /** + * 批量删除角色部门关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleDept(Long[] ids); + + /** + * 查询部门使用数量 + * + * @param deptId 部门ID + * @return 结果 + */ + public int selectCountRoleDeptByDeptId(Long deptId); + + /** + * 批量新增角色部门信息 + * + * @param roleDeptList 角色部门列表 + * @return 结果 + */ + public int batchRoleDept(List roleDeptList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java new file mode 100644 index 0000000..cf2bd8c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMapper.java @@ -0,0 +1,107 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysRole; + +/** + * 角色表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMapper +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 根据用户ID查询角色 + * + * @param userName 用户名 + * @return 角色列表 + */ + public List selectRolesByUserName(String userName); + + /** + * 校验角色名称是否唯一 + * + * @param roleName 角色名称 + * @return 角色信息 + */ + public SysRole checkRoleNameUnique(String roleName); + + /** + * 校验角色权限是否唯一 + * + * @param roleKey 角色权限 + * @return 角色信息 + */ + public SysRole checkRoleKeyUnique(String roleKey); + + /** + * 修改角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 新增角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..6602bee --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysRoleMenuMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysRoleMenu; + +/** + * 角色与菜单关联表 数据层 + * + * @author ruoyi + */ +public interface SysRoleMenuMapper +{ + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int checkMenuExistRole(Long menuId); + + /** + * 通过角色ID删除角色和菜单关联 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleMenuByRoleId(Long roleId); + + /** + * 批量删除角色菜单关联信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteRoleMenu(Long[] ids); + + /** + * 批量新增角色菜单信息 + * + * @param roleMenuList 角色菜单列表 + * @return 结果 + */ + public int batchRoleMenu(List roleMenuList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java new file mode 100644 index 0000000..76e1c79 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java @@ -0,0 +1,127 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.common.core.domain.entity.SysUser; + +/** + * 用户表 数据层 + * + * @author ruoyi + */ +public interface SysUserMapper +{ + /** + * 根据条件分页查询用户列表 + * + * @param sysUser 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser sysUser); + + /** + * 根据条件分页查询已配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public int updateUserAvatar(@Param("userName") String userName, @Param("avatar") String avatar); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(@Param("userName") String userName, @Param("password") String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 校验用户名称是否唯一 + * + * @param userName 用户名称 + * @return 结果 + */ + public SysUser checkUserNameUnique(String userName); + + /** + * 校验手机号码是否唯一 + * + * @param phonenumber 手机号码 + * @return 结果 + */ + public SysUser checkPhoneUnique(String phonenumber); + + /** + * 校验email是否唯一 + * + * @param email 用户邮箱 + * @return 结果 + */ + public SysUser checkEmailUnique(String email); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..2a6a720 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserPostMapper.java @@ -0,0 +1,44 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import com.ruoyi.system.domain.SysUserPost; + +/** + * 用户与岗位关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserPostMapper +{ + /** + * 通过用户ID删除用户和岗位关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserPostByUserId(Long userId); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 批量删除用户和岗位关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserPost(Long[] ids); + + /** + * 批量新增用户岗位信息 + * + * @param userPostList 用户岗位列表 + * @return 结果 + */ + public int batchUserPost(List userPostList); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java new file mode 100644 index 0000000..3143ec8 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserRoleMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.system.mapper; + +import java.util.List; +import org.apache.ibatis.annotations.Param; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 用户与角色关联表 数据层 + * + * @author ruoyi + */ +public interface SysUserRoleMapper +{ + /** + * 通过用户ID删除用户和角色关联 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserRoleByUserId(Long userId); + + /** + * 批量删除用户和角色关联 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteUserRole(Long[] ids); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 批量新增用户角色信息 + * + * @param userRoleList 用户角色列表 + * @return 结果 + */ + public int batchUserRole(List userRoleList); + + /** + * 删除用户和角色关联信息 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteUserRoleInfo(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int deleteUserRoleInfos(@Param("roleId") Long roleId, @Param("userIds") Long[] userIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java new file mode 100644 index 0000000..b307776 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysConfigService.java @@ -0,0 +1,89 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysConfig; + +/** + * 参数配置 服务层 + * + * @author ruoyi + */ +public interface ISysConfigService +{ + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + public SysConfig selectConfigById(Long configId); + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数键名 + * @return 参数键值 + */ + public String selectConfigByKey(String configKey); + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + public boolean selectCaptchaEnabled(); + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + public List selectConfigList(SysConfig config); + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int insertConfig(SysConfig config); + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + public int updateConfig(SysConfig config); + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + public void deleteConfigByIds(Long[] configIds); + + /** + * 加载参数缓存数据 + */ + public void loadingConfigCache(); + + /** + * 清空参数缓存数据 + */ + public void clearConfigCache(); + + /** + * 重置参数缓存数据 + */ + public void resetConfigCache(); + + /** + * 校验参数键名是否唯一 + * + * @param config 参数信息 + * @return 结果 + */ + public boolean checkConfigKeyUnique(SysConfig config); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java new file mode 100644 index 0000000..f228208 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDeptService.java @@ -0,0 +1,124 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; + +/** + * 部门管理 服务层 + * + * @author ruoyi + */ +public interface ISysDeptService +{ + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + public List selectDeptList(SysDept dept); + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + public List selectDeptTreeList(SysDept dept); + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + public List buildDeptTree(List depts); + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + public List buildDeptTreeSelect(List depts); + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + public List selectDeptListByRoleId(Long roleId); + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + public SysDept selectDeptById(Long deptId); + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + public int selectNormalChildrenDeptById(Long deptId); + + /** + * 是否存在部门子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + public boolean hasChildByDeptId(Long deptId); + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkDeptExistUser(Long deptId); + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + public boolean checkDeptNameUnique(SysDept dept); + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + public void checkDeptDataScope(Long deptId); + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int insertDept(SysDept dept); + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + public int updateDept(SysDept dept); + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + public int deleteDeptById(Long deptId); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java new file mode 100644 index 0000000..9bc4f13 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictDataService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictDataService +{ + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + public List selectDictDataList(SysDictData dictData); + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + public String selectDictLabel(String dictType, String dictValue); + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + public SysDictData selectDictDataById(Long dictCode); + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + public void deleteDictDataByIds(Long[] dictCodes); + + /** + * 新增保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int insertDictData(SysDictData dictData); + + /** + * 修改保存字典数据信息 + * + * @param dictData 字典数据信息 + * @return 结果 + */ + public int updateDictData(SysDictData dictData); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java new file mode 100644 index 0000000..01c1c1d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysDictTypeService.java @@ -0,0 +1,98 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; + +/** + * 字典 业务层 + * + * @author ruoyi + */ +public interface ISysDictTypeService +{ + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + public List selectDictTypeList(SysDictType dictType); + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + public List selectDictTypeAll(); + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + public List selectDictDataByType(String dictType); + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + public SysDictType selectDictTypeById(Long dictId); + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + public SysDictType selectDictTypeByType(String dictType); + + /** + * 批量删除字典信息 + * + * @param dictIds 需要删除的字典ID + */ + public void deleteDictTypeByIds(Long[] dictIds); + + /** + * 加载字典缓存数据 + */ + public void loadingDictCache(); + + /** + * 清空字典缓存数据 + */ + public void clearDictCache(); + + /** + * 重置字典缓存数据 + */ + public void resetDictCache(); + + /** + * 新增保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int insertDictType(SysDictType dictType); + + /** + * 修改保存字典类型信息 + * + * @param dictType 字典类型信息 + * @return 结果 + */ + public int updateDictType(SysDictType dictType); + + /** + * 校验字典类型称是否唯一 + * + * @param dictType 字典类型 + * @return 结果 + */ + public boolean checkDictTypeUnique(SysDictType dictType); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java new file mode 100644 index 0000000..ce3151d --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysLogininforService.java @@ -0,0 +1,40 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysLogininfor; + +/** + * 系统访问日志情况信息 服务层 + * + * @author ruoyi + */ +public interface ISysLogininforService +{ + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + public void insertLogininfor(SysLogininfor logininfor); + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + public List selectLogininforList(SysLogininfor logininfor); + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + public int deleteLogininforByIds(Long[] infoIds); + + /** + * 清空系统登录日志 + */ + public void cleanLogininfor(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java new file mode 100644 index 0000000..7d60696 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysMenuService.java @@ -0,0 +1,144 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.system.domain.vo.RouterVo; + +/** + * 菜单 业务层 + * + * @author ruoyi + */ +public interface ISysMenuService +{ + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(Long userId); + + /** + * 根据用户查询系统菜单列表 + * + * @param menu 菜单信息 + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuList(SysMenu menu, Long userId); + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectMenuPermsByUserId(Long userId); + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + public Set selectMenuPermsByRoleId(Long roleId); + + /** + * 根据用户ID查询菜单树信息 + * + * @param userId 用户ID + * @return 菜单列表 + */ + public List selectMenuTreeByUserId(Long userId); + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + public List selectMenuListByRoleId(Long roleId); + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + public List buildMenus(List menus); + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + public List buildMenuTree(List menus); + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + public List buildMenuTreeSelect(List menus); + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + public SysMenu selectMenuById(Long menuId); + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean hasChildByMenuId(Long menuId); + + /** + * 查询菜单是否存在角色 + * + * @param menuId 菜单ID + * @return 结果 true 存在 false 不存在 + */ + public boolean checkMenuExistRole(Long menuId); + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int insertMenu(SysMenu menu); + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + public int updateMenu(SysMenu menu); + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + public int deleteMenuById(Long menuId); + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean checkMenuNameUnique(SysMenu menu); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java new file mode 100644 index 0000000..47ce1b7 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysNoticeService.java @@ -0,0 +1,60 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysNotice; + +/** + * 公告 服务层 + * + * @author ruoyi + */ +public interface ISysNoticeService +{ + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + public SysNotice selectNoticeById(Long noticeId); + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + public List selectNoticeList(SysNotice notice); + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int insertNotice(SysNotice notice); + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + public int updateNotice(SysNotice notice); + + /** + * 删除公告信息 + * + * @param noticeId 公告ID + * @return 结果 + */ + public int deleteNoticeById(Long noticeId); + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + public int deleteNoticeByIds(Long[] noticeIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java new file mode 100644 index 0000000..4fd8e5a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysOperLogService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysOperLog; + +/** + * 操作日志 服务层 + * + * @author ruoyi + */ +public interface ISysOperLogService +{ + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + public void insertOperlog(SysOperLog operLog); + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + public List selectOperLogList(SysOperLog operLog); + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + public int deleteOperLogByIds(Long[] operIds); + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + public SysOperLog selectOperLogById(Long operId); + + /** + * 清空操作日志 + */ + public void cleanOperLog(); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java new file mode 100644 index 0000000..84779bf --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysPostService.java @@ -0,0 +1,99 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.system.domain.SysPost; + +/** + * 岗位信息 服务层 + * + * @author ruoyi + */ +public interface ISysPostService +{ + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位列表 + */ + public List selectPostList(SysPost post); + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + public List selectPostAll(); + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + public SysPost selectPostById(Long postId); + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + public List selectPostListByUserId(Long userId); + + /** + * 校验岗位名称 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostNameUnique(SysPost post); + + /** + * 校验岗位编码 + * + * @param post 岗位信息 + * @return 结果 + */ + public boolean checkPostCodeUnique(SysPost post); + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + public int countUserPostById(Long postId); + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + public int deletePostById(Long postId); + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + public int deletePostByIds(Long[] postIds); + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int insertPost(SysPost post); + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + public int updatePost(SysPost post); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java new file mode 100644 index 0000000..9185cce --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysRoleService.java @@ -0,0 +1,173 @@ +package com.ruoyi.system.service; + +import java.util.List; +import java.util.Set; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.system.domain.SysUserRole; + +/** + * 角色业务层 + * + * @author ruoyi + */ +public interface ISysRoleService +{ + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + public List selectRoleList(SysRole role); + + /** + * 根据用户ID查询角色列表 + * + * @param userId 用户ID + * @return 角色列表 + */ + public List selectRolesByUserId(Long userId); + + /** + * 根据用户ID查询角色权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + public Set selectRolePermissionByUserId(Long userId); + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + public List selectRoleAll(); + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + public List selectRoleListByUserId(Long userId); + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + public SysRole selectRoleById(Long roleId); + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleNameUnique(SysRole role); + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + public boolean checkRoleKeyUnique(SysRole role); + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + public void checkRoleAllowed(SysRole role); + + /** + * 校验角色是否有数据权限 + * + * @param roleIds 角色id + */ + public void checkRoleDataScope(Long... roleIds); + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + public int countUserRoleByRoleId(Long roleId); + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int insertRole(SysRole role); + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRole(SysRole role); + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + public int updateRoleStatus(SysRole role); + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + public int authDataScope(SysRole role); + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteRoleById(Long roleId); + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + public int deleteRoleByIds(Long[] roleIds); + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + public int deleteAuthUser(SysUserRole userRole); + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + public int deleteAuthUsers(Long roleId, Long[] userIds); + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要删除的用户数据ID + * @return 结果 + */ + public int insertAuthUsers(Long roleId, Long[] userIds); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java new file mode 100644 index 0000000..8eb5448 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserOnlineService.java @@ -0,0 +1,48 @@ +package com.ruoyi.system.service; + +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.system.domain.SysUserOnline; + +/** + * 在线用户 服务层 + * + * @author ruoyi + */ +public interface ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user); + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user); + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user); + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + public SysUserOnline loginUserToUserOnline(LoginUser user); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java new file mode 100644 index 0000000..10bc2ab --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java @@ -0,0 +1,206 @@ +package com.ruoyi.system.service; + +import java.util.List; +import com.ruoyi.common.core.domain.entity.SysUser; + +/** + * 用户 业务层 + * + * @author ruoyi + */ +public interface ISysUserService +{ + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUserList(SysUser user); + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectAllocatedList(SysUser user); + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + public List selectUnallocatedList(SysUser user); + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + public SysUser selectUserByUserName(String userName); + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + public SysUser selectUserById(Long userId); + + /** + * 根据用户ID查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserRoleGroup(String userName); + + /** + * 根据用户ID查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + public String selectUserPostGroup(String userName); + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkUserNameUnique(SysUser user); + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkPhoneUnique(SysUser user); + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean checkEmailUnique(SysUser user); + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + public void checkUserAllowed(SysUser user); + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + public void checkUserDataScope(Long userId); + + /** + * 新增用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int insertUser(SysUser user); + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public boolean registerUser(SysUser user); + + /** + * 修改用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUser(SysUser user); + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserAuth(Long userId, Long[] roleIds); + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserStatus(SysUser user); + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + public int updateUserProfile(SysUser user); + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + public boolean updateUserAvatar(String userName, String avatar); + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + public int resetPwd(SysUser user); + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + public int resetUserPwd(String userName, String password); + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + public int deleteUserById(Long userId); + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + public int deleteUserByIds(Long[] userIds); + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport, String operName); +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java new file mode 100644 index 0000000..aca85e9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysConfigServiceImpl.java @@ -0,0 +1,234 @@ +package com.ruoyi.system.service.impl; + +import java.util.Collection; +import java.util.List; +import javax.annotation.PostConstruct; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.constant.CacheConstants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.redis.RedisCache; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysConfig; +import com.ruoyi.system.mapper.SysConfigMapper; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 参数配置 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysConfigServiceImpl implements ISysConfigService +{ + @Autowired + private SysConfigMapper configMapper; + + @Autowired + private RedisCache redisCache; + + /** + * 项目启动时,初始化参数到缓存 + */ + @PostConstruct + public void init() + { + loadingConfigCache(); + } + + /** + * 查询参数配置信息 + * + * @param configId 参数配置ID + * @return 参数配置信息 + */ + @Override + @DataSource(DataSourceType.MASTER) + public SysConfig selectConfigById(Long configId) + { + SysConfig config = new SysConfig(); + config.setConfigId(configId); + return configMapper.selectConfig(config); + } + + /** + * 根据键名查询参数配置信息 + * + * @param configKey 参数key + * @return 参数键值 + */ + @Override + public String selectConfigByKey(String configKey) + { + String configValue = Convert.toStr(redisCache.getCacheObject(getCacheKey(configKey))); + if (StringUtils.isNotEmpty(configValue)) + { + return configValue; + } + SysConfig config = new SysConfig(); + config.setConfigKey(configKey); + SysConfig retConfig = configMapper.selectConfig(config); + if (StringUtils.isNotNull(retConfig)) + { + redisCache.setCacheObject(getCacheKey(configKey), retConfig.getConfigValue()); + return retConfig.getConfigValue(); + } + return StringUtils.EMPTY; + } + + /** + * 获取验证码开关 + * + * @return true开启,false关闭 + */ + @Override + public boolean selectCaptchaEnabled() + { + String captchaEnabled = selectConfigByKey("sys.account.captchaEnabled"); + if (StringUtils.isEmpty(captchaEnabled)) + { + return true; + } + return Convert.toBool(captchaEnabled); + } + + /** + * 查询参数配置列表 + * + * @param config 参数配置信息 + * @return 参数配置集合 + */ + @Override + public List selectConfigList(SysConfig config) + { + return configMapper.selectConfigList(config); + } + + /** + * 新增参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int insertConfig(SysConfig config) + { + int row = configMapper.insertConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 修改参数配置 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public int updateConfig(SysConfig config) + { + SysConfig temp = configMapper.selectConfigById(config.getConfigId()); + if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) + { + redisCache.deleteObject(getCacheKey(temp.getConfigKey())); + } + + int row = configMapper.updateConfig(config); + if (row > 0) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + return row; + } + + /** + * 批量删除参数信息 + * + * @param configIds 需要删除的参数ID + */ + @Override + public void deleteConfigByIds(Long[] configIds) + { + for (Long configId : configIds) + { + SysConfig config = selectConfigById(configId); + if (StringUtils.equals(UserConstants.YES, config.getConfigType())) + { + throw new ServiceException(String.format("内置参数【%1$s】不能删除 ", config.getConfigKey())); + } + configMapper.deleteConfigById(configId); + redisCache.deleteObject(getCacheKey(config.getConfigKey())); + } + } + + /** + * 加载参数缓存数据 + */ + @Override + public void loadingConfigCache() + { + List configsList = configMapper.selectConfigList(new SysConfig()); + for (SysConfig config : configsList) + { + redisCache.setCacheObject(getCacheKey(config.getConfigKey()), config.getConfigValue()); + } + } + + /** + * 清空参数缓存数据 + */ + @Override + public void clearConfigCache() + { + Collection keys = redisCache.keys(CacheConstants.SYS_CONFIG_KEY + "*"); + redisCache.deleteObject(keys); + } + + /** + * 重置参数缓存数据 + */ + @Override + public void resetConfigCache() + { + clearConfigCache(); + loadingConfigCache(); + } + + /** + * 校验参数键名是否唯一 + * + * @param config 参数配置信息 + * @return 结果 + */ + @Override + public boolean checkConfigKeyUnique(SysConfig config) + { + Long configId = StringUtils.isNull(config.getConfigId()) ? -1L : config.getConfigId(); + SysConfig info = configMapper.checkConfigKeyUnique(config.getConfigKey()); + if (StringUtils.isNotNull(info) && info.getConfigId().longValue() != configId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 设置cache key + * + * @param configKey 参数键 + * @return 缓存键key + */ + private String getCacheKey(String configKey) + { + return CacheConstants.SYS_CONFIG_KEY + configKey; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..66bd14c --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,340 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.stream.Collectors; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.mapper.SysDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门管理 服务实现 + * + * @author ruoyi + */ +@Service +public class SysDeptServiceImpl implements ISysDeptService +{ + @Autowired + private SysDeptMapper deptMapper; + + @Autowired + private SysRoleMapper roleMapper; + + /** + * 查询部门管理数据 + * + * @param dept 部门信息 + * @return 部门信息集合 + */ + @Override + @DataScope(deptAlias = "d") + public List selectDeptList(SysDept dept) + { + return deptMapper.selectDeptList(dept); + } + + /** + * 查询部门树结构信息 + * + * @param dept 部门信息 + * @return 部门树信息集合 + */ + @Override + public List selectDeptTreeList(SysDept dept) + { + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + return buildDeptTreeSelect(depts); + } + + /** + * 构建前端所需要树结构 + * + * @param depts 部门列表 + * @return 树结构列表 + */ + @Override + public List buildDeptTree(List depts) + { + List returnList = new ArrayList(); + List tempList = depts.stream().map(SysDept::getDeptId).collect(Collectors.toList()); + for (SysDept dept : depts) + { + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(dept.getParentId())) + { + recursionFn(depts, dept); + returnList.add(dept); + } + } + if (returnList.isEmpty()) + { + returnList = depts; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param depts 部门列表 + * @return 下拉树结构列表 + */ + @Override + public List buildDeptTreeSelect(List depts) + { + List deptTrees = buildDeptTree(depts); + return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据角色ID查询部门树信息 + * + * @param roleId 角色ID + * @return 选中部门列表 + */ + @Override + public List selectDeptListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return deptMapper.selectDeptListByRoleId(roleId, role.isDeptCheckStrictly()); + } + + /** + * 根据部门ID查询信息 + * + * @param deptId 部门ID + * @return 部门信息 + */ + @Override + public SysDept selectDeptById(Long deptId) + { + return deptMapper.selectDeptById(deptId); + } + + /** + * 根据ID查询所有子部门(正常状态) + * + * @param deptId 部门ID + * @return 子部门数 + */ + @Override + public int selectNormalChildrenDeptById(Long deptId) + { + return deptMapper.selectNormalChildrenDeptById(deptId); + } + + /** + * 是否存在子节点 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public boolean hasChildByDeptId(Long deptId) + { + int result = deptMapper.hasChildByDeptId(deptId); + return result > 0; + } + + /** + * 查询部门是否存在用户 + * + * @param deptId 部门ID + * @return 结果 true 存在 false 不存在 + */ + @Override + public boolean checkDeptExistUser(Long deptId) + { + int result = deptMapper.checkDeptExistUser(deptId); + return result > 0; + } + + /** + * 校验部门名称是否唯一 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public boolean checkDeptNameUnique(SysDept dept) + { + Long deptId = StringUtils.isNull(dept.getDeptId()) ? -1L : dept.getDeptId(); + SysDept info = deptMapper.checkDeptNameUnique(dept.getDeptName(), dept.getParentId()); + if (StringUtils.isNotNull(info) && info.getDeptId().longValue() != deptId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验部门是否有数据权限 + * + * @param deptId 部门id + */ + @Override + public void checkDeptDataScope(Long deptId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId()) && StringUtils.isNotNull(deptId)) + { + SysDept dept = new SysDept(); + dept.setDeptId(deptId); + List depts = SpringUtils.getAopProxy(this).selectDeptList(dept); + if (StringUtils.isEmpty(depts)) + { + throw new ServiceException("没有权限访问部门数据!"); + } + } + } + + /** + * 新增保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int insertDept(SysDept dept) + { + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + // 如果父节点不为正常状态,则不允许新增子节点 + if (!UserConstants.DEPT_NORMAL.equals(info.getStatus())) + { + throw new ServiceException("部门停用,不允许新增"); + } + dept.setAncestors(info.getAncestors() + "," + dept.getParentId()); + return deptMapper.insertDept(dept); + } + + /** + * 修改保存部门信息 + * + * @param dept 部门信息 + * @return 结果 + */ + @Override + public int updateDept(SysDept dept) + { + SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId()); + SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId()); + if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept)) + { + String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId(); + String oldAncestors = oldDept.getAncestors(); + dept.setAncestors(newAncestors); + updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors); + } + int result = deptMapper.updateDept(dept); + if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) + && !StringUtils.equals("0", dept.getAncestors())) + { + // 如果该部门是启用状态,则启用该部门的所有上级部门 + updateParentDeptStatusNormal(dept); + } + return result; + } + + /** + * 修改该部门的父级部门状态 + * + * @param dept 当前部门 + */ + private void updateParentDeptStatusNormal(SysDept dept) + { + String ancestors = dept.getAncestors(); + Long[] deptIds = Convert.toLongArray(ancestors); + deptMapper.updateDeptStatusNormal(deptIds); + } + + /** + * 修改子元素关系 + * + * @param deptId 被修改的部门ID + * @param newAncestors 新的父ID集合 + * @param oldAncestors 旧的父ID集合 + */ + public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) + { + List children = deptMapper.selectChildrenDeptById(deptId); + for (SysDept child : children) + { + child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors)); + } + if (children.size() > 0) + { + deptMapper.updateDeptChildren(children); + } + } + + /** + * 删除部门管理信息 + * + * @param deptId 部门ID + * @return 结果 + */ + @Override + public int deleteDeptById(Long deptId) + { + return deptMapper.deleteDeptById(deptId); + } + + /** + * 递归列表 + */ + private void recursionFn(List list, SysDept t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysDept tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysDept t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysDept n = (SysDept) it.next(); + if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysDept t) + { + return getChildList(list, t).size() > 0; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..9436e45 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,113 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.service.ISysDictDataService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictDataServiceImpl implements ISysDictDataService +{ + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 根据条件分页查询字典数据 + * + * @param dictData 字典数据信息 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataList(SysDictData dictData) + { + return dictDataMapper.selectDictDataList(dictData); + } + + /** + * 根据字典类型和字典键值查询字典数据信息 + * + * @param dictType 字典类型 + * @param dictValue 字典键值 + * @return 字典标签 + */ + @Override + public String selectDictLabel(String dictType, String dictValue) + { + return dictDataMapper.selectDictLabel(dictType, dictValue); + } + + /** + * 根据字典数据ID查询信息 + * + * @param dictCode 字典数据ID + * @return 字典数据 + */ + @Override + public SysDictData selectDictDataById(Long dictCode) + { + return dictDataMapper.selectDictDataById(dictCode); + } + + /** + * 批量删除字典数据信息 + * + * @param dictCodes 需要删除的字典数据ID + */ + @Override + public void deleteDictDataByIds(Long[] dictCodes) + { + for (Long dictCode : dictCodes) + { + SysDictData data = selectDictDataById(dictCode); + dictDataMapper.deleteDictDataById(dictCode); + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + } + + /** + * 新增保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int insertDictData(SysDictData data) + { + int row = dictDataMapper.insertDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } + + /** + * 修改保存字典数据信息 + * + * @param data 字典数据信息 + * @return 结果 + */ + @Override + public int updateDictData(SysDictData data) + { + int row = dictDataMapper.updateDictData(data); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(data.getDictType()); + DictUtils.setDictCache(data.getDictType(), dictDatas); + } + return row; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..d700819 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,225 @@ +package com.ruoyi.system.service.impl; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import javax.annotation.PostConstruct; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.core.domain.entity.SysDictType; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.DictUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.mapper.SysDictDataMapper; +import com.ruoyi.system.mapper.SysDictTypeMapper; +import com.ruoyi.system.service.ISysDictTypeService; + +/** + * 字典 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysDictTypeServiceImpl implements ISysDictTypeService +{ + @Autowired + private SysDictTypeMapper dictTypeMapper; + + @Autowired + private SysDictDataMapper dictDataMapper; + + /** + * 项目启动时,初始化字典到缓存 + */ + @PostConstruct + public void init() + { + loadingDictCache(); + } + + /** + * 根据条件分页查询字典类型 + * + * @param dictType 字典类型信息 + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeList(SysDictType dictType) + { + return dictTypeMapper.selectDictTypeList(dictType); + } + + /** + * 根据所有字典类型 + * + * @return 字典类型集合信息 + */ + @Override + public List selectDictTypeAll() + { + return dictTypeMapper.selectDictTypeAll(); + } + + /** + * 根据字典类型查询字典数据 + * + * @param dictType 字典类型 + * @return 字典数据集合信息 + */ + @Override + public List selectDictDataByType(String dictType) + { + List dictDatas = DictUtils.getDictCache(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + return dictDatas; + } + dictDatas = dictDataMapper.selectDictDataByType(dictType); + if (StringUtils.isNotEmpty(dictDatas)) + { + DictUtils.setDictCache(dictType, dictDatas); + return dictDatas; + } + return null; + } + + /** + * 根据字典类型ID查询信息 + * + * @param dictId 字典类型ID + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeById(Long dictId) + { + return dictTypeMapper.selectDictTypeById(dictId); + } + + /** + * 根据字典类型查询信息 + * + * @param dictType 字典类型 + * @return 字典类型 + */ + @Override + public SysDictType selectDictTypeByType(String dictType) + { + return dictTypeMapper.selectDictTypeByType(dictType); + } + + /** + * 批量删除字典类型信息 + * + * @param dictIds 需要删除的字典ID + */ + @Override + public void deleteDictTypeByIds(Long[] dictIds) + { + for (Long dictId : dictIds) + { + SysDictType dictType = selectDictTypeById(dictId); + if (dictDataMapper.countDictDataByType(dictType.getDictType()) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName())); + } + dictTypeMapper.deleteDictTypeById(dictId); + DictUtils.removeDictCache(dictType.getDictType()); + } + } + + /** + * 加载字典缓存数据 + */ + @Override + public void loadingDictCache() + { + SysDictData dictData = new SysDictData(); + dictData.setStatus("0"); + Map> dictDataMap = dictDataMapper.selectDictDataList(dictData).stream().collect(Collectors.groupingBy(SysDictData::getDictType)); + for (Map.Entry> entry : dictDataMap.entrySet()) + { + DictUtils.setDictCache(entry.getKey(), entry.getValue().stream().sorted(Comparator.comparing(SysDictData::getDictSort)).collect(Collectors.toList())); + } + } + + /** + * 清空字典缓存数据 + */ + @Override + public void clearDictCache() + { + DictUtils.clearDictCache(); + } + + /** + * 重置字典缓存数据 + */ + @Override + public void resetDictCache() + { + clearDictCache(); + loadingDictCache(); + } + + /** + * 新增保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + public int insertDictType(SysDictType dict) + { + int row = dictTypeMapper.insertDictType(dict); + if (row > 0) + { + DictUtils.setDictCache(dict.getDictType(), null); + } + return row; + } + + /** + * 修改保存字典类型信息 + * + * @param dict 字典类型信息 + * @return 结果 + */ + @Override + @Transactional + public int updateDictType(SysDictType dict) + { + SysDictType oldDict = dictTypeMapper.selectDictTypeById(dict.getDictId()); + dictDataMapper.updateDictDataType(oldDict.getDictType(), dict.getDictType()); + int row = dictTypeMapper.updateDictType(dict); + if (row > 0) + { + List dictDatas = dictDataMapper.selectDictDataByType(dict.getDictType()); + DictUtils.setDictCache(dict.getDictType(), dictDatas); + } + return row; + } + + /** + * 校验字典类型称是否唯一 + * + * @param dict 字典类型 + * @return 结果 + */ + @Override + public boolean checkDictTypeUnique(SysDictType dict) + { + Long dictId = StringUtils.isNull(dict.getDictId()) ? -1L : dict.getDictId(); + SysDictType dictType = dictTypeMapper.checkDictTypeUnique(dict.getDictType()); + if (StringUtils.isNotNull(dictType) && dictType.getDictId().longValue() != dictId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java new file mode 100644 index 0000000..7db8758 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysLogininforServiceImpl.java @@ -0,0 +1,67 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysLogininfor; +import com.ruoyi.system.mapper.SysLogininforMapper; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问日志情况信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysLogininforServiceImpl implements ISysLogininforService +{ + + @Autowired + private SysLogininforMapper logininforMapper; + + /** + * 新增系统登录日志 + * + * @param logininfor 访问日志对象 + */ + @Override + public void insertLogininfor(SysLogininfor logininfor) + { + logininforMapper.insertLogininfor(logininfor); + } + + /** + * 查询系统登录日志集合 + * + * @param logininfor 访问日志对象 + * @return 登录记录集合 + */ + @Override + public List selectLogininforList(SysLogininfor logininfor) + { + return logininforMapper.selectLogininforList(logininfor); + } + + /** + * 批量删除系统登录日志 + * + * @param infoIds 需要删除的登录日志ID + * @return 结果 + */ + @Override + public int deleteLogininforByIds(Long[] infoIds) + { + return logininforMapper.deleteLogininforByIds(infoIds); + } + + /** + * 清空系统登录日志 + */ + @Override + public void cleanLogininfor() + { + logininforMapper.cleanLogininfor(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..fe0d7d3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,545 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.Constants; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.TreeSelect; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.vo.MetaVo; +import com.ruoyi.system.domain.vo.RouterVo; +import com.ruoyi.system.mapper.SysMenuMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysMenuServiceImpl implements ISysMenuService +{ + public static final String PREMISSION_STRING = "perms[\"{0}\"]"; + + @Autowired + private SysMenuMapper menuMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + /** + * 根据用户查询系统菜单列表 + * + * @param userId 用户ID + * @return 菜单列表 + */ + @Override + public List selectMenuList(Long userId) + { + return selectMenuList(new SysMenu(), userId); + } + + /** + * 查询系统菜单列表 + * + * @param menu 菜单信息 + * @return 菜单列表 + */ + @Override + public List selectMenuList(SysMenu menu, Long userId) + { + List menuList = null; + // 管理员显示所有菜单信息 + if (SysUser.isAdmin(userId)) + { + menuList = menuMapper.selectMenuList(menu); + } + else + { + menu.getParams().put("userId", userId); + menuList = menuMapper.selectMenuListByUserId(menu); + } + return menuList; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByUserId(Long userId) + { + List perms = menuMapper.selectMenuPermsByUserId(userId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据角色ID查询权限 + * + * @param roleId 角色ID + * @return 权限列表 + */ + @Override + public Set selectMenuPermsByRoleId(Long roleId) + { + List perms = menuMapper.selectMenuPermsByRoleId(roleId); + Set permsSet = new HashSet<>(); + for (String perm : perms) + { + if (StringUtils.isNotEmpty(perm)) + { + permsSet.addAll(Arrays.asList(perm.trim().split(","))); + } + } + return permsSet; + } + + /** + * 根据用户ID查询菜单 + * + * @param userId 用户名称 + * @return 菜单列表 + */ + @Override + public List selectMenuTreeByUserId(Long userId) + { + List menus = null; + if (SecurityUtils.isAdmin(userId)) + { + menus = menuMapper.selectMenuTreeAll(); + } + else + { + menus = menuMapper.selectMenuTreeByUserId(userId); + } + return getChildPerms(menus, 0); + } + + /** + * 根据角色ID查询菜单树信息 + * + * @param roleId 角色ID + * @return 选中菜单列表 + */ + @Override + public List selectMenuListByRoleId(Long roleId) + { + SysRole role = roleMapper.selectRoleById(roleId); + return menuMapper.selectMenuListByRoleId(roleId, role.isMenuCheckStrictly()); + } + + /** + * 构建前端路由所需要的菜单 + * + * @param menus 菜单列表 + * @return 路由列表 + */ + @Override + public List buildMenus(List menus) + { + List routers = new LinkedList(); + for (SysMenu menu : menus) + { + RouterVo router = new RouterVo(); + router.setHidden("1".equals(menu.getVisible())); + router.setName(getRouteName(menu)); + router.setPath(getRouterPath(menu)); + router.setComponent(getComponent(menu)); + router.setQuery(menu.getQuery()); + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + List cMenus = menu.getChildren(); + if (StringUtils.isNotEmpty(cMenus) && UserConstants.TYPE_DIR.equals(menu.getMenuType())) + { + router.setAlwaysShow(true); + router.setRedirect("noRedirect"); + router.setChildren(buildMenus(cMenus)); + } + else if (isMenuFrame(menu)) + { + router.setMeta(null); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + children.setPath(menu.getPath()); + children.setComponent(menu.getComponent()); + children.setName(getRouteName(menu.getRouteName(), menu.getPath())); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), StringUtils.equals("1", menu.getIsCache()), menu.getPath())); + children.setQuery(menu.getQuery()); + childrenList.add(children); + router.setChildren(childrenList); + } + else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) + { + router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon())); + router.setPath("/"); + List childrenList = new ArrayList(); + RouterVo children = new RouterVo(); + String routerPath = innerLinkReplaceEach(menu.getPath()); + children.setPath(routerPath); + children.setComponent(UserConstants.INNER_LINK); + children.setName(getRouteName(menu.getRouteName(), routerPath)); + children.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getPath())); + childrenList.add(children); + router.setChildren(childrenList); + } + routers.add(router); + } + return routers; + } + + /** + * 构建前端所需要树结构 + * + * @param menus 菜单列表 + * @return 树结构列表 + */ + @Override + public List buildMenuTree(List menus) + { + List returnList = new ArrayList(); + List tempList = menus.stream().map(SysMenu::getMenuId).collect(Collectors.toList()); + for (Iterator iterator = menus.iterator(); iterator.hasNext();) + { + SysMenu menu = (SysMenu) iterator.next(); + // 如果是顶级节点, 遍历该父节点的所有子节点 + if (!tempList.contains(menu.getParentId())) + { + recursionFn(menus, menu); + returnList.add(menu); + } + } + if (returnList.isEmpty()) + { + returnList = menus; + } + return returnList; + } + + /** + * 构建前端所需要下拉树结构 + * + * @param menus 菜单列表 + * @return 下拉树结构列表 + */ + @Override + public List buildMenuTreeSelect(List menus) + { + List menuTrees = buildMenuTree(menus); + return menuTrees.stream().map(TreeSelect::new).collect(Collectors.toList()); + } + + /** + * 根据菜单ID查询信息 + * + * @param menuId 菜单ID + * @return 菜单信息 + */ + @Override + public SysMenu selectMenuById(Long menuId) + { + return menuMapper.selectMenuById(menuId); + } + + /** + * 是否存在菜单子节点 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean hasChildByMenuId(Long menuId) + { + int result = menuMapper.hasChildByMenuId(menuId); + return result > 0; + } + + /** + * 查询菜单使用数量 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public boolean checkMenuExistRole(Long menuId) + { + int result = roleMenuMapper.checkMenuExistRole(menuId); + return result > 0; + } + + /** + * 新增保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int insertMenu(SysMenu menu) + { + return menuMapper.insertMenu(menu); + } + + /** + * 修改保存菜单信息 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public int updateMenu(SysMenu menu) + { + return menuMapper.updateMenu(menu); + } + + /** + * 删除菜单管理信息 + * + * @param menuId 菜单ID + * @return 结果 + */ + @Override + public int deleteMenuById(Long menuId) + { + return menuMapper.deleteMenuById(menuId); + } + + /** + * 校验菜单名称是否唯一 + * + * @param menu 菜单信息 + * @return 结果 + */ + @Override + public boolean checkMenuNameUnique(SysMenu menu) + { + Long menuId = StringUtils.isNull(menu.getMenuId()) ? -1L : menu.getMenuId(); + SysMenu info = menuMapper.checkMenuNameUnique(menu.getMenuName(), menu.getParentId()); + if (StringUtils.isNotNull(info) && info.getMenuId().longValue() != menuId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 获取路由名称 + * + * @param menu 菜单信息 + * @return 路由名称 + */ + public String getRouteName(SysMenu menu) + { + // 非外链并且是一级目录(类型为目录) + if (isMenuFrame(menu)) + { + return StringUtils.EMPTY; + } + return getRouteName(menu.getRouteName(), menu.getPath()); + } + + /** + * 获取路由名称,如没有配置路由名称则取路由地址 + * + * @param name 路由名称 + * @param path 路由地址 + * @return 路由名称(驼峰格式) + */ + public String getRouteName(String name, String path) + { + String routerName = StringUtils.isNotEmpty(name) ? name : path; + return StringUtils.capitalize(routerName); + } + + /** + * 获取路由地址 + * + * @param menu 菜单信息 + * @return 路由地址 + */ + public String getRouterPath(SysMenu menu) + { + String routerPath = menu.getPath(); + // 内链打开外网方式 + if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + routerPath = innerLinkReplaceEach(routerPath); + } + // 非外链并且是一级目录(类型为目录) + if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType()) + && UserConstants.NO_FRAME.equals(menu.getIsFrame())) + { + routerPath = "/" + menu.getPath(); + } + // 非外链并且是一级目录(类型为菜单) + else if (isMenuFrame(menu)) + { + routerPath = "/"; + } + return routerPath; + } + + /** + * 获取组件信息 + * + * @param menu 菜单信息 + * @return 组件信息 + */ + public String getComponent(SysMenu menu) + { + String component = UserConstants.LAYOUT; + if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) + { + component = menu.getComponent(); + } + else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0 && isInnerLink(menu)) + { + component = UserConstants.INNER_LINK; + } + else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) + { + component = UserConstants.PARENT_VIEW; + } + return component; + } + + /** + * 是否为菜单内部跳转 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isMenuFrame(SysMenu menu) + { + return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType()) + && menu.getIsFrame().equals(UserConstants.NO_FRAME); + } + + /** + * 是否为内链组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isInnerLink(SysMenu menu) + { + return menu.getIsFrame().equals(UserConstants.NO_FRAME) && StringUtils.ishttp(menu.getPath()); + } + + /** + * 是否为parent_view组件 + * + * @param menu 菜单信息 + * @return 结果 + */ + public boolean isParentView(SysMenu menu) + { + return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType()); + } + + /** + * 根据父节点的ID获取所有子节点 + * + * @param list 分类表 + * @param parentId 传入的父节点ID + * @return String + */ + public List getChildPerms(List list, int parentId) + { + List returnList = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) + { + SysMenu t = (SysMenu) iterator.next(); + // 一、根据传入的某个父节点ID,遍历该父节点的所有子节点 + if (t.getParentId() == parentId) + { + recursionFn(list, t); + returnList.add(t); + } + } + return returnList; + } + + /** + * 递归列表 + * + * @param list 分类表 + * @param t 子节点 + */ + private void recursionFn(List list, SysMenu t) + { + // 得到子节点列表 + List childList = getChildList(list, t); + t.setChildren(childList); + for (SysMenu tChild : childList) + { + if (hasChild(list, tChild)) + { + recursionFn(list, tChild); + } + } + } + + /** + * 得到子节点列表 + */ + private List getChildList(List list, SysMenu t) + { + List tlist = new ArrayList(); + Iterator it = list.iterator(); + while (it.hasNext()) + { + SysMenu n = (SysMenu) it.next(); + if (n.getParentId().longValue() == t.getMenuId().longValue()) + { + tlist.add(n); + } + } + return tlist; + } + + /** + * 判断是否有子节点 + */ + private boolean hasChild(List list, SysMenu t) + { + return getChildList(list, t).size() > 0; + } + + /** + * 内链域名特殊字符替换 + * + * @return 替换后的内链域名 + */ + public String innerLinkReplaceEach(String path) + { + return StringUtils.replaceEach(path, new String[] { Constants.HTTP, Constants.HTTPS, Constants.WWW, ".", ":" }, + new String[] { "", "", "", "/", "/" }); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..a61e4b9 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,94 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysNotice; +import com.ruoyi.system.mapper.SysNoticeMapper; +import com.ruoyi.system.service.ISysNoticeService; + +/** + * 公告 服务层实现 + * + * @author ruoyi + */ +@Service +public class SysNoticeServiceImpl implements ISysNoticeService +{ + @Autowired + private SysNoticeMapper noticeMapper; + + /** + * 查询公告信息 + * + * @param noticeId 公告ID + * @return 公告信息 + */ + @Override + public SysNotice selectNoticeById(Long noticeId) + { + return noticeMapper.selectNoticeById(noticeId); + } + + /** + * 查询公告列表 + * + * @param notice 公告信息 + * @return 公告集合 + */ + @Override + public List selectNoticeList(SysNotice notice) + { + return noticeMapper.selectNoticeList(notice); + } + + /** + * 新增公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int insertNotice(SysNotice notice) + { + return noticeMapper.insertNotice(notice); + } + + /** + * 修改公告 + * + * @param notice 公告信息 + * @return 结果 + */ + @Override + public int updateNotice(SysNotice notice) + { + return noticeMapper.updateNotice(notice); + } + + /** + * 删除公告对象 + * + * @param noticeId 公告ID + * @return 结果 + */ + @Override + public int deleteNoticeById(Long noticeId) + { + return noticeMapper.deleteNoticeById(noticeId); + } + + /** + * 批量删除公告信息 + * + * @param noticeIds 需要删除的公告ID + * @return 结果 + */ + @Override + public int deleteNoticeByIds(Long[] noticeIds) + { + return noticeMapper.deleteNoticeByIds(noticeIds); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java new file mode 100644 index 0000000..aaf043a --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysOperLogServiceImpl.java @@ -0,0 +1,78 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.system.domain.SysOperLog; +import com.ruoyi.system.mapper.SysOperLogMapper; +import com.ruoyi.system.service.ISysOperLogService; + +/** + * 操作日志 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysOperLogServiceImpl implements ISysOperLogService +{ + @Autowired + private SysOperLogMapper operLogMapper; + + /** + * 新增操作日志 + * + * @param operLog 操作日志对象 + */ + @Override + public void insertOperlog(SysOperLog operLog) + { + operLogMapper.insertOperlog(operLog); + } + + /** + * 查询系统操作日志集合 + * + * @param operLog 操作日志对象 + * @return 操作日志集合 + */ + @Override + public List selectOperLogList(SysOperLog operLog) + { + return operLogMapper.selectOperLogList(operLog); + } + + /** + * 批量删除系统操作日志 + * + * @param operIds 需要删除的操作日志ID + * @return 结果 + */ + @Override + public int deleteOperLogByIds(Long[] operIds) + { + return operLogMapper.deleteOperLogByIds(operIds); + } + + /** + * 查询操作日志详细 + * + * @param operId 操作ID + * @return 操作日志对象 + */ + @Override + public SysOperLog selectOperLogById(Long operId) + { + return operLogMapper.selectOperLogById(operId); + } + + /** + * 清空操作日志 + */ + @Override + public void cleanOperLog() + { + operLogMapper.cleanOperLog(); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..e2baa33 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysPostServiceImpl.java @@ -0,0 +1,180 @@ +package com.ruoyi.system.service.impl; + +import java.util.List; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.service.ISysPostService; + +/** + * 岗位信息 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysPostServiceImpl implements ISysPostService +{ + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + /** + * 查询岗位信息集合 + * + * @param post 岗位信息 + * @return 岗位信息集合 + */ + @Override + public List selectPostList(SysPost post) + { + return postMapper.selectPostList(post); + } + + /** + * 查询所有岗位 + * + * @return 岗位列表 + */ + @Override + public List selectPostAll() + { + return postMapper.selectPostAll(); + } + + /** + * 通过岗位ID查询岗位信息 + * + * @param postId 岗位ID + * @return 角色对象信息 + */ + @Override + public SysPost selectPostById(Long postId) + { + return postMapper.selectPostById(postId); + } + + /** + * 根据用户ID获取岗位选择框列表 + * + * @param userId 用户ID + * @return 选中岗位ID列表 + */ + @Override + public List selectPostListByUserId(Long userId) + { + return postMapper.selectPostListByUserId(userId); + } + + /** + * 校验岗位名称是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostNameUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostNameUnique(post.getPostName()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验岗位编码是否唯一 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public boolean checkPostCodeUnique(SysPost post) + { + Long postId = StringUtils.isNull(post.getPostId()) ? -1L : post.getPostId(); + SysPost info = postMapper.checkPostCodeUnique(post.getPostCode()); + if (StringUtils.isNotNull(info) && info.getPostId().longValue() != postId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 通过岗位ID查询岗位使用数量 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int countUserPostById(Long postId) + { + return userPostMapper.countUserPostById(postId); + } + + /** + * 删除岗位信息 + * + * @param postId 岗位ID + * @return 结果 + */ + @Override + public int deletePostById(Long postId) + { + return postMapper.deletePostById(postId); + } + + /** + * 批量删除岗位信息 + * + * @param postIds 需要删除的岗位ID + * @return 结果 + */ + @Override + public int deletePostByIds(Long[] postIds) + { + for (Long postId : postIds) + { + SysPost post = selectPostById(postId); + if (countUserPostById(postId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", post.getPostName())); + } + } + return postMapper.deletePostByIds(postIds); + } + + /** + * 新增保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int insertPost(SysPost post) + { + return postMapper.insertPost(post); + } + + /** + * 修改保存岗位信息 + * + * @param post 岗位信息 + * @return 结果 + */ + @Override + public int updatePost(SysPost post) + { + return postMapper.updatePost(post); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..6abde6e --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,429 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import cn.hutool.core.util.IdUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysRoleDept; +import com.ruoyi.system.domain.SysRoleMenu; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.SysRoleDeptMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysRoleMenuMapper; +import com.ruoyi.system.mapper.SysUserRoleMapper; +import com.ruoyi.system.service.ISysRoleService; + +/** + * 角色 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysRoleServiceImpl implements ISysRoleService +{ + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysRoleMenuMapper roleMenuMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysRoleDeptMapper roleDeptMapper; + + /** + * 根据条件分页查询角色数据 + * + * @param role 角色信息 + * @return 角色数据集合信息 + */ + @Override + @DataScope(deptAlias = "d") + public List selectRoleList(SysRole role) + { + return roleMapper.selectRoleList(role); + } + + /** + * 根据用户ID查询角色 + * + * @param userId 用户ID + * @return 角色列表 + */ + @Override + public List selectRolesByUserId(Long userId) + { + List userRoles = roleMapper.selectRolePermissionByUserId(userId); + List roles = selectRoleAll(); + for (SysRole role : roles) + { + for (SysRole userRole : userRoles) + { + if (role.getRoleId().longValue() == userRole.getRoleId().longValue()) + { + role.setFlag(true); + break; + } + } + } + return roles; + } + + /** + * 根据用户ID查询权限 + * + * @param userId 用户ID + * @return 权限列表 + */ + @Override + public Set selectRolePermissionByUserId(Long userId) + { + List perms = roleMapper.selectRolePermissionByUserId(userId); + Set permsSet = new HashSet<>(); + for (SysRole perm : perms) + { + if (StringUtils.isNotNull(perm)) + { + permsSet.addAll(Arrays.asList(perm.getRoleKey().trim().split(","))); + } + } + return permsSet; + } + + /** + * 查询所有角色 + * + * @return 角色列表 + */ + @Override + public List selectRoleAll() + { + return SpringUtils.getAopProxy(this).selectRoleList(new SysRole()); + } + + /** + * 根据用户ID获取角色选择框列表 + * + * @param userId 用户ID + * @return 选中角色ID列表 + */ + @Override + public List selectRoleListByUserId(Long userId) + { + return roleMapper.selectRoleListByUserId(userId); + } + + /** + * 通过角色ID查询角色 + * + * @param roleId 角色ID + * @return 角色对象信息 + */ + @Override + public SysRole selectRoleById(Long roleId) + { + return roleMapper.selectRoleById(roleId); + } + + /** + * 校验角色名称是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleNameUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleNameUnique(role.getRoleName()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色权限是否唯一 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public boolean checkRoleKeyUnique(SysRole role) + { + Long roleId = StringUtils.isNull(role.getRoleId()) ? -1L : role.getRoleId(); + SysRole info = roleMapper.checkRoleKeyUnique(role.getRoleKey()); + if (StringUtils.isNotNull(info) && info.getRoleId().longValue() != roleId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验角色是否允许操作 + * + * @param role 角色信息 + */ + @Override + public void checkRoleAllowed(SysRole role) + { + if (StringUtils.isNotNull(role.getRoleId()) && role.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员角色"); + } + } + + /** + * 校验角色是否有数据权限 + * + * @param roleIds 角色id + */ + @Override + public void checkRoleDataScope(Long... roleIds) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + for (Long roleId : roleIds) + { + SysRole role = new SysRole(); + role.setRoleId(roleId); + List roles = SpringUtils.getAopProxy(this).selectRoleList(role); + if (StringUtils.isEmpty(roles)) + { + throw new ServiceException("没有权限访问角色数据!"); + } + } + } + } + + /** + * 通过角色ID查询角色使用数量 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + public int countUserRoleByRoleId(Long roleId) + { + return userRoleMapper.countUserRoleByRoleId(roleId); + } + + /** + * 新增保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int insertRole(SysRole role) + { + // 新增角色信息 + roleMapper.insertRole(role); + return insertRoleMenu(role); + } + + /** + * 修改保存角色信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int updateRole(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(role.getRoleId()); + return insertRoleMenu(role); + } + + /** + * 修改角色状态 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + public int updateRoleStatus(SysRole role) + { + return roleMapper.updateRole(role); + } + + /** + * 修改数据权限信息 + * + * @param role 角色信息 + * @return 结果 + */ + @Override + @Transactional + public int authDataScope(SysRole role) + { + // 修改角色信息 + roleMapper.updateRole(role); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(role.getRoleId()); + // 新增角色和部门信息(数据权限) + return insertRoleDept(role); + } + + /** + * 新增角色菜单信息 + * + * @param role 角色对象 + */ + public int insertRoleMenu(SysRole role) + { + int rows = 1; + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long menuId : role.getMenuIds()) + { + SysRoleMenu rm = new SysRoleMenu(); + rm.setRoleId(role.getRoleId()); + rm.setMenuId(menuId); + list.add(rm); + } + if (list.size() > 0) + { + rows = roleMenuMapper.batchRoleMenu(list); + } + return rows; + } + + /** + * 新增角色部门信息(数据权限) + * + * @param role 角色对象 + */ + public int insertRoleDept(SysRole role) + { + int rows = 1; + // 新增角色与部门(数据权限)管理 + List list = new ArrayList(); + for (Long deptId : role.getDeptIds()) + { + SysRoleDept rd = new SysRoleDept(); + rd.setRoleId(role.getRoleId()); + rd.setDeptId(deptId); + list.add(rd); + } + if (list.size() > 0) + { + rows = roleDeptMapper.batchRoleDept(list); + } + return rows; + } + + /** + * 通过角色ID删除角色 + * + * @param roleId 角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleById(Long roleId) + { + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenuByRoleId(roleId); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDeptByRoleId(roleId); + return roleMapper.deleteRoleById(roleId); + } + + /** + * 批量删除角色信息 + * + * @param roleIds 需要删除的角色ID + * @return 结果 + */ + @Override + @Transactional + public int deleteRoleByIds(Long[] roleIds) + { + for (Long roleId : roleIds) + { + checkRoleAllowed(new SysRole(roleId)); + checkRoleDataScope(roleId); + SysRole role = selectRoleById(roleId); + if (countUserRoleByRoleId(roleId) > 0) + { + throw new ServiceException(String.format("%1$s已分配,不能删除", role.getRoleName())); + } + } + // 删除角色与菜单关联 + roleMenuMapper.deleteRoleMenu(roleIds); + // 删除角色与部门关联 + roleDeptMapper.deleteRoleDept(roleIds); + return roleMapper.deleteRoleByIds(roleIds); + } + + /** + * 取消授权用户角色 + * + * @param userRole 用户和角色关联信息 + * @return 结果 + */ + @Override + public int deleteAuthUser(SysUserRole userRole) + { + return userRoleMapper.deleteUserRoleInfo(userRole); + } + + /** + * 批量取消授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要取消授权的用户数据ID + * @return 结果 + */ + @Override + public int deleteAuthUsers(Long roleId, Long[] userIds) + { + return userRoleMapper.deleteUserRoleInfos(roleId, userIds); + } + + /** + * 批量选择授权用户角色 + * + * @param roleId 角色ID + * @param userIds 需要授权的用户数据ID + * @return 结果 + */ + @Override + public int insertAuthUsers(Long roleId, Long[] userIds) + { + // 新增用户与角色管理 + List list = new ArrayList(); + for (Long userId : userIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + return userRoleMapper.batchUserRole(list); + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java new file mode 100644 index 0000000..f80a877 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserOnlineServiceImpl.java @@ -0,0 +1,96 @@ +package com.ruoyi.system.service.impl; + +import org.springframework.stereotype.Service; +import com.ruoyi.common.core.domain.model.LoginUser; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户 服务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserOnlineServiceImpl implements ISysUserOnlineService +{ + /** + * 通过登录地址查询信息 + * + * @param ipaddr 登录地址 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByIpaddr(String ipaddr, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过用户名称查询信息 + * + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByUserName(String userName, LoginUser user) + { + if (StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 通过登录地址/用户名称查询信息 + * + * @param ipaddr 登录地址 + * @param userName 用户名称 + * @param user 用户信息 + * @return 在线用户信息 + */ + @Override + public SysUserOnline selectOnlineByInfo(String ipaddr, String userName, LoginUser user) + { + if (StringUtils.equals(ipaddr, user.getIpaddr()) && StringUtils.equals(userName, user.getUsername())) + { + return loginUserToUserOnline(user); + } + return null; + } + + /** + * 设置在线用户信息 + * + * @param user 用户信息 + * @return 在线用户 + */ + @Override + public SysUserOnline loginUserToUserOnline(LoginUser user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUser())) + { + return null; + } + SysUserOnline sysUserOnline = new SysUserOnline(); + sysUserOnline.setTokenId(user.getToken()); + sysUserOnline.setUserName(user.getUsername()); + sysUserOnline.setIpaddr(user.getIpaddr()); + sysUserOnline.setLoginLocation(user.getLoginLocation()); + sysUserOnline.setBrowser(user.getBrowser()); + sysUserOnline.setOs(user.getOs()); + sysUserOnline.setLoginTime(user.getLoginTime()); + if (StringUtils.isNotNull(user.getUser().getDept())) + { + sysUserOnline.setDeptName(user.getUser().getDept().getDeptName()); + } + return sysUserOnline; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..87e7ed3 --- /dev/null +++ b/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java @@ -0,0 +1,552 @@ +package com.ruoyi.system.service.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.validation.Validator; + +import cn.hutool.core.util.IdUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import com.ruoyi.common.annotation.DataScope; +import com.ruoyi.common.constant.UserConstants; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.bean.BeanValidators; +import com.ruoyi.common.utils.spring.SpringUtils; +import com.ruoyi.system.domain.SysPost; +import com.ruoyi.system.domain.SysUserPost; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.mapper.SysPostMapper; +import com.ruoyi.system.mapper.SysRoleMapper; +import com.ruoyi.system.mapper.SysUserMapper; +import com.ruoyi.system.mapper.SysUserPostMapper; +import com.ruoyi.system.mapper.SysUserRoleMapper; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户 业务层处理 + * + * @author ruoyi + */ +@Service +public class SysUserServiceImpl implements ISysUserService +{ + private static final Logger log = LoggerFactory.getLogger(SysUserServiceImpl.class); + + @Autowired + private SysUserMapper userMapper; + + @Autowired + private SysRoleMapper roleMapper; + + @Autowired + private SysPostMapper postMapper; + + @Autowired + private SysUserRoleMapper userRoleMapper; + + @Autowired + private SysUserPostMapper userPostMapper; + + @Autowired + private ISysConfigService configService; + + @Autowired + private ISysDeptService deptService; + + @Autowired + protected Validator validator; + + /** + * 根据条件分页查询用户列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUserList(SysUser user) + { + return userMapper.selectUserList(user); + } + + /** + * 根据条件分页查询已分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectAllocatedList(SysUser user) + { + return userMapper.selectAllocatedList(user); + } + + /** + * 根据条件分页查询未分配用户角色列表 + * + * @param user 用户信息 + * @return 用户信息集合信息 + */ + @Override + @DataScope(deptAlias = "d", userAlias = "u") + public List selectUnallocatedList(SysUser user) + { + return userMapper.selectUnallocatedList(user); + } + + /** + * 通过用户名查询用户 + * + * @param userName 用户名 + * @return 用户对象信息 + */ + @Override + public SysUser selectUserByUserName(String userName) + { + return userMapper.selectUserByUserName(userName); + } + + /** + * 通过用户ID查询用户 + * + * @param userId 用户ID + * @return 用户对象信息 + */ + @Override + public SysUser selectUserById(Long userId) + { + return userMapper.selectUserById(userId); + } + + /** + * 查询用户所属角色组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserRoleGroup(String userName) + { + List list = roleMapper.selectRolesByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysRole::getRoleName).collect(Collectors.joining(",")); + } + + /** + * 查询用户所属岗位组 + * + * @param userName 用户名 + * @return 结果 + */ + @Override + public String selectUserPostGroup(String userName) + { + List list = postMapper.selectPostsByUserName(userName); + if (CollectionUtils.isEmpty(list)) + { + return StringUtils.EMPTY; + } + return list.stream().map(SysPost::getPostName).collect(Collectors.joining(",")); + } + + /** + * 校验用户名称是否唯一 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean checkUserNameUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkUserNameUnique(user.getUserName()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验手机号码是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkPhoneUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkPhoneUnique(user.getPhonenumber()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验email是否唯一 + * + * @param user 用户信息 + * @return + */ + @Override + public boolean checkEmailUnique(SysUser user) + { + Long userId = StringUtils.isNull(user.getUserId()) ? -1L : user.getUserId(); + SysUser info = userMapper.checkEmailUnique(user.getEmail()); + if (StringUtils.isNotNull(info) && info.getUserId().longValue() != userId.longValue()) + { + return UserConstants.NOT_UNIQUE; + } + return UserConstants.UNIQUE; + } + + /** + * 校验用户是否允许操作 + * + * @param user 用户信息 + */ + @Override + public void checkUserAllowed(SysUser user) + { + if (StringUtils.isNotNull(user.getUserId()) && user.isAdmin()) + { + throw new ServiceException("不允许操作超级管理员用户"); + } + } + + /** + * 校验用户是否有数据权限 + * + * @param userId 用户id + */ + @Override + public void checkUserDataScope(Long userId) + { + if (!SysUser.isAdmin(SecurityUtils.getUserId())) + { + SysUser user = new SysUser(); + user.setUserId(userId); + List users = SpringUtils.getAopProxy(this).selectUserList(user); + if (StringUtils.isEmpty(users)) + { + throw new ServiceException("没有权限访问用户数据!"); + } + } + } + + /** + * 新增保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int insertUser(SysUser user) + { + // 新增用户信息 + int rows = userMapper.insertUser(user); + // 新增用户岗位关联 + insertUserPost(user); + // 新增用户与角色管理 + insertUserRole(user); + return rows; + } + + /** + * 注册用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public boolean registerUser(SysUser user) + { + return userMapper.insertUser(user) > 0; + } + + /** + * 修改保存用户信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + @Transactional + public int updateUser(SysUser user) + { + Long userId = user.getUserId(); + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 新增用户与角色管理 + insertUserRole(user); + // 删除用户与岗位关联 + userPostMapper.deleteUserPostByUserId(userId); + // 新增用户与岗位管理 + insertUserPost(user); + return userMapper.updateUser(user); + } + + /** + * 用户授权角色 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + @Override + @Transactional + public void insertUserAuth(Long userId, Long[] roleIds) + { + userRoleMapper.deleteUserRoleByUserId(userId); + insertUserRole(userId, roleIds); + } + + /** + * 修改用户状态 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserStatus(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户基本信息 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int updateUserProfile(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 修改用户头像 + * + * @param userName 用户名 + * @param avatar 头像地址 + * @return 结果 + */ + @Override + public boolean updateUserAvatar(String userName, String avatar) + { + return userMapper.updateUserAvatar(userName, avatar) > 0; + } + + /** + * 重置用户密码 + * + * @param user 用户信息 + * @return 结果 + */ + @Override + public int resetPwd(SysUser user) + { + return userMapper.updateUser(user); + } + + /** + * 重置用户密码 + * + * @param userName 用户名 + * @param password 密码 + * @return 结果 + */ + @Override + public int resetUserPwd(String userName, String password) + { + return userMapper.resetUserPwd(userName, password); + } + + /** + * 新增用户角色信息 + * + * @param user 用户对象 + */ + public void insertUserRole(SysUser user) + { + this.insertUserRole(user.getUserId(), user.getRoleIds()); + } + + /** + * 新增用户岗位信息 + * + * @param user 用户对象 + */ + public void insertUserPost(SysUser user) + { + Long[] posts = user.getPostIds(); + if (StringUtils.isNotEmpty(posts)) + { + // 新增用户与岗位管理 + List list = new ArrayList(posts.length); + for (Long postId : posts) + { + SysUserPost up = new SysUserPost(); + up.setUserId(user.getUserId()); + up.setPostId(postId); + list.add(up); + } + userPostMapper.batchUserPost(list); + } + } + + /** + * 新增用户角色信息 + * + * @param userId 用户ID + * @param roleIds 角色组 + */ + public void insertUserRole(Long userId, Long[] roleIds) + { + if (StringUtils.isNotEmpty(roleIds)) + { + // 新增用户与角色管理 + List list = new ArrayList(roleIds.length); + for (Long roleId : roleIds) + { + SysUserRole ur = new SysUserRole(); + ur.setUserId(userId); + ur.setRoleId(roleId); + list.add(ur); + } + userRoleMapper.batchUserRole(list); + } + } + + /** + * 通过用户ID删除用户 + * + * @param userId 用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserById(Long userId) + { + // 删除用户与角色关联 + userRoleMapper.deleteUserRoleByUserId(userId); + // 删除用户与岗位表 + userPostMapper.deleteUserPostByUserId(userId); + return userMapper.deleteUserById(userId); + } + + /** + * 批量删除用户信息 + * + * @param userIds 需要删除的用户ID + * @return 结果 + */ + @Override + @Transactional + public int deleteUserByIds(Long[] userIds) + { + for (Long userId : userIds) + { + checkUserAllowed(new SysUser(userId)); + checkUserDataScope(userId); + } + // 删除用户与角色关联 + userRoleMapper.deleteUserRole(userIds); + // 删除用户与岗位关联 + userPostMapper.deleteUserPost(userIds); + return userMapper.deleteUserByIds(userIds); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @param operName 操作用户 + * @return 结果 + */ + @Override + public String importUser(List userList, Boolean isUpdateSupport, String operName) + { + if (StringUtils.isNull(userList) || userList.size() == 0) + { + throw new ServiceException("导入用户数据不能为空!"); + } + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + for (SysUser user : userList) + { + try + { + // 验证是否存在这个用户 + SysUser u = userMapper.selectUserByUserName(user.getUserName()); + if (StringUtils.isNull(u)) + { + BeanValidators.validateWithException(validator, user); + deptService.checkDeptDataScope(user.getDeptId()); + String password = configService.selectConfigByKey("sys.user.initPassword"); + user.setPassword(SecurityUtils.encryptPassword(password)); + user.setCreateBy(operName); + userMapper.insertUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + BeanValidators.validateWithException(validator, user); + checkUserAllowed(u); + checkUserDataScope(u.getUserId()); + deptService.checkDeptDataScope(user.getDeptId()); + user.setUserId(u.getUserId()); + user.setUpdateBy(operName); + userMapper.updateUser(user); + successNum++; + successMsg.append("
" + successNum + "、账号 " + user.getUserName() + " 更新成功"); + } + else + { + failureNum++; + failureMsg.append("
" + failureNum + "、账号 " + user.getUserName() + " 已存在"); + } + } + catch (Exception e) + { + failureNum++; + String msg = "
" + failureNum + "、账号 " + user.getUserName() + " 导入失败:"; + failureMsg.append(msg + e.getMessage()); + log.error(msg, e); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml new file mode 100644 index 0000000..6891002 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysConfigMapper.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + select config_id, config_name, config_key, config_value, config_type, create_by, create_time, update_by, update_time, remark + from sys_config + + + + + + + and config_id = #{configId} + + + and config_key = #{configKey} + + + + + + + + + + + + + + insert into sys_config ( + config_id, + config_name, + config_key, + config_value, + config_type, + create_by, + remark, + create_time + )values( + #{configId}, + #{configName}, + #{configKey}, + #{configValue}, + #{configType}, + #{createBy}, + #{remark}, + GETDATE() + ) + + + + update sys_config + + config_name = #{configName}, + config_key = #{configKey}, + config_value = #{configValue}, + config_type = #{configType}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = GETDATE() + + where config_id = #{configId} + + + + delete from sys_config where config_id = #{configId} + + + + delete from sys_config where config_id in + + #{configId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml new file mode 100644 index 0000000..d07252a --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDeptMapper.xml @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + select d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.phone, d.email, d.status, d.del_flag, d.create_by, d.create_time + from sys_dept d + + + + + + + + + + + + + + + + + + + + insert into sys_dept( + dept_id, + parent_id, + dept_name, + ancestors, + order_num, + leader, + phone, + email, + status, + create_by, + create_time + )values( + #{deptId}, + #{parentId}, + #{deptName}, + #{ancestors}, + #{orderNum}, + #{leader}, + #{phone}, + #{email}, + #{status}, + #{createBy}, + GETDATE() + ) + + + + update sys_dept + + parent_id = #{parentId}, + dept_name = #{deptName}, + ancestors = #{ancestors}, + order_num = #{orderNum}, + leader = #{leader}, + phone = #{phone}, + email = #{email}, + status = #{status}, + update_by = #{updateBy}, + update_time = GETDATE() + + where dept_id = #{deptId} + + + + update sys_dept set ancestors = + + when #{item.deptId} then #{item.ancestors} + + where dept_id in + + #{item.deptId} + + + + + update sys_dept set status = '0' where dept_id in + + #{deptId} + + + + + update sys_dept set del_flag = '2' where dept_id = #{deptId} + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml new file mode 100644 index 0000000..cfc61a3 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictDataMapper.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + select dict_code, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark + from sys_dict_data + + + + + + + + + + + + + + delete from sys_dict_data where dict_code = #{dictCode} + + + + delete from sys_dict_data where dict_code in + + #{dictCode} + + + + + update sys_dict_data + + dict_sort = #{dictSort}, + dict_label = #{dictLabel}, + dict_value = #{dictValue}, + dict_type = #{dictType}, + css_class = #{cssClass}, + list_class = #{listClass}, + is_default = #{isDefault}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where dict_code = #{dictCode} + + + + update sys_dict_data set dict_type = #{newDictType} where dict_type = #{oldDictType} + + + + insert into sys_dict_data( + dict_code, + dict_sort, + dict_label, + dict_value, + dict_type, + css_class, + list_class, + is_default, + status, + remark, + create_by, + create_time + )values( + #{dictCode}, + #{dictSort}, + #{dictLabel}, + #{dictValue}, + #{dictType}, + #{cssClass}, + #{listClass}, + #{isDefault}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml new file mode 100644 index 0000000..2b25f0d --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysDictTypeMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + select dict_id, dict_name, dict_type, status, create_by, create_time, remark + from sys_dict_type + + + + + + + + + + + + + + delete from sys_dict_type where dict_id = #{dictId} + + + + delete from sys_dict_type where dict_id in + + #{dictId} + + + + + update sys_dict_type + + dict_name = #{dictName}, + dict_type = #{dictType}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where dict_id = #{dictId} + + + + insert into sys_dict_type( + dict_id, + dict_name, + dict_type, + status, + remark, + create_by, + create_time + )values( + #{dictId}, + #{dictName}, + #{dictType}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml new file mode 100644 index 0000000..20032fd --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysLogininforMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + insert into sys_logininfor (user_name, status, ipaddr, login_location, browser, os, msg, login_time) + values (#{userName}, #{status}, #{ipaddr}, #{loginLocation}, #{browser}, #{os}, #{msg}, GETDATE()) + + + + + + delete from sys_logininfor where info_id in + + #{infoId} + + + + + truncate table sys_logininfor + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml new file mode 100644 index 0000000..755010b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysMenuMapper.xml @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select menu_id, menu_name, parent_id, order_num, path, component, query, route_name, is_frame, is_cache, menu_type, visible, status, ISNULL(perms,'') as perms, icon, create_time + from sys_menu + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_menu + + menu_name = #{menuName}, + parent_id = #{parentId}, + order_num = #{orderNum}, + path = #{path}, + component = #{component}, + query = #{query}, + route_name = #{routeName}, + is_frame = #{isFrame}, + is_cache = #{isCache}, + menu_type = #{menuType}, + visible = #{visible}, + status = #{status}, + perms = #{perms}, + icon = #{icon}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where menu_id = #{menuId} + + + + insert into sys_menu( + menu_id, + parent_id, + menu_name, + order_num, + path, + component, + query, + route_name, + is_frame, + is_cache, + menu_type, + visible, + status, + perms, + icon, + remark, + create_by, + create_time + )values( + #{menuId}, + #{parentId}, + #{menuName}, + #{orderNum}, + #{path}, + #{component}, + #{query}, + #{routeName}, + #{isFrame}, + #{isCache}, + #{menuType}, + #{visible}, + #{status}, + #{perms}, + #{icon}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + + delete from sys_menu where menu_id = #{menuId} + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml new file mode 100644 index 0000000..a93655f --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysNoticeMapper.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + select notice_id, notice_title, notice_type, cast(notice_content as char) as notice_content, status, create_by, create_time, update_by, update_time, remark + from sys_notice + + + + + + + + insert into sys_notice ( + notice_id, + notice_title, + notice_type, + notice_content, + status, + remark, + create_by, + create_time + )values( + #{noticeId}, + #{noticeTitle}, + #{noticeType}, + #{noticeContent}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + + update sys_notice + + notice_title = #{noticeTitle}, + notice_type = #{noticeType}, + notice_content = #{noticeContent}, + status = #{status}, + update_by = #{updateBy}, + update_time = GETDATE() + + where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id = #{noticeId} + + + + delete from sys_notice where notice_id in + + #{noticeId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml new file mode 100644 index 0000000..aa66d0d --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysOperLogMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select oper_id, title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, oper_time, cost_time + from sys_oper_log + + + + insert into sys_oper_log(title, business_type, method, request_method, operator_type, oper_name, dept_name, oper_url, oper_ip, oper_location, oper_param, json_result, status, error_msg, cost_time, oper_time) + values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operName}, #{deptName}, #{operUrl}, #{operIp}, #{operLocation}, #{operParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, GETDATE()) + + + + + + delete from sys_oper_log where oper_id in + + #{operId} + + + + + + + truncate table sys_oper_log + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml new file mode 100644 index 0000000..8190419 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysPostMapper.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + select post_id, post_code, post_name, post_sort, status, create_by, create_time, remark + from sys_post + + + + + + + + + + + + + + + + + + update sys_post + + post_code = #{postCode}, + post_name = #{postName}, + post_sort = #{postSort}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where post_id = #{postId} + + + + insert into sys_post( + post_id, + post_code, + post_name, + post_sort, + status, + remark, + create_by, + create_time + )values( + #{postId}, + #{postCode}, + #{postName}, + #{postSort}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + + delete from sys_post where post_id = #{postId} + + + + delete from sys_post where post_id in + + #{postId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml new file mode 100644 index 0000000..7c4139b --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleDeptMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_role_dept where role_id=#{roleId} + + + + + + delete from sys_role_dept where role_id in + + #{roleId} + + + + + insert into sys_role_dept(role_id, dept_id) values + + (#{item.roleId},#{item.deptId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml new file mode 100644 index 0000000..0314a3f --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMapper.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + select distinct r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.menu_check_strictly, r.dept_check_strictly, + r.status, r.del_flag, r.create_time, r.remark + from sys_role r + left join sys_user_role ur on ur.role_id = r.role_id + left join sys_user u on u.user_id = ur.user_id + left join sys_dept d on u.dept_id = d.dept_id + + + + + + + + + + + + + + + + + + + + insert into sys_role( + role_id, + role_name, + role_key, + role_sort, + data_scope, + menu_check_strictly, + dept_check_strictly, + status, + remark, + create_by, + create_time + )values( + #{roleId}, + #{roleName}, + #{roleKey}, + #{roleSort}, + #{dataScope}, + #{menuCheckStrictly}, + #{deptCheckStrictly}, + #{status}, + #{remark}, + #{createBy}, + GETDATE() + ) + + + + update sys_role + + role_name = #{roleName}, + role_key = #{roleKey}, + role_sort = #{roleSort}, + data_scope = #{dataScope}, + menu_check_strictly = #{menuCheckStrictly}, + dept_check_strictly = #{deptCheckStrictly}, + status = #{status}, + remark = #{remark}, + update_by = #{updateBy}, + update_time = GETDATE() + + where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id = #{roleId} + + + + update sys_role set del_flag = '2' where role_id in + + #{roleId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml new file mode 100644 index 0000000..cb60a85 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysRoleMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + delete from sys_role_menu where role_id=#{roleId} + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + insert into sys_role_menu(role_id, menu_id) values + + (#{item.roleId},#{item.menuId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml new file mode 100644 index 0000000..cabcbe9 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select u.user_id, u.dept_id, u.user_name, u.nick_name, u.email, u.avatar, u.phonenumber, u.password, u.sex, u.status, u.del_flag, u.login_ip, u.login_date, u.create_by, u.create_time, u.remark, + d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status, + r.role_id, r.role_name, r.role_key, r.role_sort, r.data_scope, r.status as role_status + from sys_user u + left join sys_dept d on u.dept_id = d.dept_id + left join sys_user_role ur on u.user_id = ur.user_id + left join sys_role r on r.role_id = ur.role_id + + + + + + + + + + + + + + + + + + + + insert into sys_user( + user_id, + dept_id, + user_name, + nick_name, + email, + avatar, + phonenumber, + sex, + password, + status, + create_by, + remark, + create_time + )values( + #{userId}, + #{deptId}, + #{userName}, + #{nickName}, + #{email}, + #{avatar}, + #{phonenumber}, + #{sex}, + #{password}, + #{status}, + #{createBy}, + #{remark}, + GETDATE() + ) + + + + update sys_user + + dept_id = #{deptId}, + nick_name = #{nickName}, + email = #{email}, + phonenumber = #{phonenumber}, + sex = #{sex}, + avatar = #{avatar}, + password = #{password}, + status = #{status}, + login_ip = #{loginIp}, + login_date = #{loginDate}, + update_by = #{updateBy}, + remark = #{remark}, + update_time = GETDATE() + + where user_id = #{userId} + + + + update sys_user set status = #{status} where user_id = #{userId} + + + + update sys_user set avatar = #{avatar} where user_name = #{userName} + + + + update sys_user set password = #{password} where user_name = #{userName} + + + + update sys_user set del_flag = '2' where user_id = #{userId} + + + + update sys_user set del_flag = '2' where user_id in + + #{userId} + + + + diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml new file mode 100644 index 0000000..2b90bc4 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserPostMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + delete from sys_user_post where user_id=#{userId} + + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + insert into sys_user_post(user_id, post_id) values + + (#{item.userId},#{item.postId}) + + + + \ No newline at end of file diff --git a/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml new file mode 100644 index 0000000..dd72689 --- /dev/null +++ b/ruoyi-system/src/main/resources/mapper/system/SysUserRoleMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + delete from sys_user_role where user_id=#{userId} + + + + + + delete from sys_user_role where user_id in + + #{userId} + + + + + insert into sys_user_role(user_id, role_id) values + + (#{item.userId},#{item.roleId}) + + + + + delete from sys_user_role where user_id=#{userId} and role_id=#{roleId} + + + + delete from sys_user_role where role_id=#{roleId} and user_id in + + #{userId} + + + \ No newline at end of file diff --git a/ruoyi-ui/.editorconfig b/ruoyi-ui/.editorconfig new file mode 100644 index 0000000..7034f9b --- /dev/null +++ b/ruoyi-ui/.editorconfig @@ -0,0 +1,22 @@ +# 告诉EditorConfig插件,这是根文件,不用继续往上查找 +root = true + +# 匹配全部文件 +[*] +# 设置字符集 +charset = utf-8 +# 缩进风格,可选space、tab +indent_style = space +# 缩进的空格数 +indent_size = 2 +# 结尾换行符,可选lf、cr、crlf +end_of_line = lf +# 在文件结尾插入新行 +insert_final_newline = true +# 删除一行中的前后空格 +trim_trailing_whitespace = true + +# 匹配md结尾的文件 +[*.md] +insert_final_newline = false +trim_trailing_whitespace = false diff --git a/ruoyi-ui/.env.development b/ruoyi-ui/.env.development new file mode 100644 index 0000000..4a29a35 --- /dev/null +++ b/ruoyi-ui/.env.development @@ -0,0 +1,11 @@ +# 页面标题 +VUE_APP_TITLE = FORTUCKY WCS SYSTEM + +# 开发环境配置 +ENV = 'development' + +# 若依管理系统/开发环境 +VUE_APP_BASE_API = '/dev-api' + +# 路由懒加载 +VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/ruoyi-ui/.env.production b/ruoyi-ui/.env.production new file mode 100644 index 0000000..242f36b --- /dev/null +++ b/ruoyi-ui/.env.production @@ -0,0 +1,8 @@ +# 页面标题 +VUE_APP_TITLE = FORTUCKY WCS SYSTEM + +# 生产环境配置 +ENV = 'production' + +# 若依管理系统/生产环境 +VUE_APP_BASE_API = '/prod-api' diff --git a/ruoyi-ui/.env.staging b/ruoyi-ui/.env.staging new file mode 100644 index 0000000..fb658c0 --- /dev/null +++ b/ruoyi-ui/.env.staging @@ -0,0 +1,12 @@ +# 页面标题 +VUE_APP_TITLE = FORTUCKY WCS SYSTEM + +BABEL_ENV = production + +NODE_ENV = production + +# 测试环境配置 +ENV = 'staging' + +# 若依管理系统/测试环境 +VUE_APP_BASE_API = '/stage-api' diff --git a/ruoyi-ui/.eslintignore b/ruoyi-ui/.eslintignore new file mode 100644 index 0000000..89be6f6 --- /dev/null +++ b/ruoyi-ui/.eslintignore @@ -0,0 +1,10 @@ +# 忽略build目录下类型为js的文件的语法检查 +build/*.js +# 忽略src/assets目录下文件的语法检查 +src/assets +# 忽略public目录下文件的语法检查 +public +# 忽略当前目录下为js的文件的语法检查 +*.js +# 忽略当前目录下为vue的文件的语法检查 +*.vue \ No newline at end of file diff --git a/ruoyi-ui/.eslintrc.js b/ruoyi-ui/.eslintrc.js new file mode 100644 index 0000000..82bbdee --- /dev/null +++ b/ruoyi-ui/.eslintrc.js @@ -0,0 +1,199 @@ +// ESlint 检查配置 +module.exports = { + root: true, + parserOptions: { + parser: 'babel-eslint', + sourceType: 'module' + }, + env: { + browser: true, + node: true, + es6: true, + }, + extends: ['plugin:vue/recommended', 'eslint:recommended'], + + // add your custom rules here + //it is base on https://github.com/vuejs/eslint-config-vue + rules: { + "vue/max-attributes-per-line": [2, { + "singleline": 10, + "multiline": { + "max": 1, + "allowFirstLine": false + } + }], + "vue/singleline-html-element-content-newline": "off", + "vue/multiline-html-element-content-newline":"off", + "vue/name-property-casing": ["error", "PascalCase"], + "vue/no-v-html": "off", + 'accessor-pairs': 2, + 'arrow-spacing': [2, { + 'before': true, + 'after': true + }], + 'block-spacing': [2, 'always'], + 'brace-style': [2, '1tbs', { + 'allowSingleLine': true + }], + 'camelcase': [0, { + 'properties': 'always' + }], + 'comma-dangle': [2, 'never'], + 'comma-spacing': [2, { + 'before': false, + 'after': true + }], + 'comma-style': [2, 'last'], + 'constructor-super': 2, + 'curly': [2, 'multi-line'], + 'dot-location': [2, 'property'], + 'eol-last': 2, + 'eqeqeq': ["error", "always", {"null": "ignore"}], + 'generator-star-spacing': [2, { + 'before': true, + 'after': true + }], + 'handle-callback-err': [2, '^(err|error)$'], + 'indent': [2, 2, { + 'SwitchCase': 1 + }], + 'jsx-quotes': [2, 'prefer-single'], + 'key-spacing': [2, { + 'beforeColon': false, + 'afterColon': true + }], + 'keyword-spacing': [2, { + 'before': true, + 'after': true + }], + 'new-cap': [2, { + 'newIsCap': true, + 'capIsNew': false + }], + 'new-parens': 2, + 'no-array-constructor': 2, + 'no-caller': 2, + 'no-console': 'off', + 'no-class-assign': 2, + 'no-cond-assign': 2, + 'no-const-assign': 2, + 'no-control-regex': 0, + 'no-delete-var': 2, + 'no-dupe-args': 2, + 'no-dupe-class-members': 2, + 'no-dupe-keys': 2, + 'no-duplicate-case': 2, + 'no-empty-character-class': 2, + 'no-empty-pattern': 2, + 'no-eval': 2, + 'no-ex-assign': 2, + 'no-extend-native': 2, + 'no-extra-bind': 2, + 'no-extra-boolean-cast': 2, + 'no-extra-parens': [2, 'functions'], + 'no-fallthrough': 2, + 'no-floating-decimal': 2, + 'no-func-assign': 2, + 'no-implied-eval': 2, + 'no-inner-declarations': [2, 'functions'], + 'no-invalid-regexp': 2, + 'no-irregular-whitespace': 2, + 'no-iterator': 2, + 'no-label-var': 2, + 'no-labels': [2, { + 'allowLoop': false, + 'allowSwitch': false + }], + 'no-lone-blocks': 2, + 'no-mixed-spaces-and-tabs': 2, + 'no-multi-spaces': 2, + 'no-multi-str': 2, + 'no-multiple-empty-lines': [2, { + 'max': 1 + }], + 'no-native-reassign': 2, + 'no-negated-in-lhs': 2, + 'no-new-object': 2, + 'no-new-require': 2, + 'no-new-symbol': 2, + 'no-new-wrappers': 2, + 'no-obj-calls': 2, + 'no-octal': 2, + 'no-octal-escape': 2, + 'no-path-concat': 2, + 'no-proto': 2, + 'no-redeclare': 2, + 'no-regex-spaces': 2, + 'no-return-assign': [2, 'except-parens'], + 'no-self-assign': 2, + 'no-self-compare': 2, + 'no-sequences': 2, + 'no-shadow-restricted-names': 2, + 'no-spaced-func': 2, + 'no-sparse-arrays': 2, + 'no-this-before-super': 2, + 'no-throw-literal': 2, + 'no-trailing-spaces': 2, + 'no-undef': 2, + 'no-undef-init': 2, + 'no-unexpected-multiline': 2, + 'no-unmodified-loop-condition': 2, + 'no-unneeded-ternary': [2, { + 'defaultAssignment': false + }], + 'no-unreachable': 2, + 'no-unsafe-finally': 2, + 'no-unused-vars': [2, { + 'vars': 'all', + 'args': 'none' + }], + 'no-useless-call': 2, + 'no-useless-computed-key': 2, + 'no-useless-constructor': 2, + 'no-useless-escape': 0, + 'no-whitespace-before-property': 2, + 'no-with': 2, + 'one-var': [2, { + 'initialized': 'never' + }], + 'operator-linebreak': [2, 'after', { + 'overrides': { + '?': 'before', + ':': 'before' + } + }], + 'padded-blocks': [2, 'never'], + 'quotes': [2, 'single', { + 'avoidEscape': true, + 'allowTemplateLiterals': true + }], + 'semi': [2, 'never'], + 'semi-spacing': [2, { + 'before': false, + 'after': true + }], + 'space-before-blocks': [2, 'always'], + 'space-before-function-paren': [2, 'never'], + 'space-in-parens': [2, 'never'], + 'space-infix-ops': 2, + 'space-unary-ops': [2, { + 'words': true, + 'nonwords': false + }], + 'spaced-comment': [2, 'always', { + 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] + }], + 'template-curly-spacing': [2, 'never'], + 'use-isnan': 2, + 'valid-typeof': 2, + 'wrap-iife': [2, 'any'], + 'yield-star-spacing': [2, 'both'], + 'yoda': [2, 'never'], + 'prefer-const': 2, + 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, + 'object-curly-spacing': [2, 'always', { + objectsInObjects: false + }], + 'array-bracket-spacing': [2, 'never'] + } +} diff --git a/ruoyi-ui/.gitignore b/ruoyi-ui/.gitignore new file mode 100644 index 0000000..78a752d --- /dev/null +++ b/ruoyi-ui/.gitignore @@ -0,0 +1,23 @@ +.DS_Store +node_modules/ +dist/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +**/*.log + +tests/**/coverage/ +tests/e2e/reports +selenium-debug.log + +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.local + +package-lock.json +yarn.lock diff --git a/ruoyi-ui/README.md b/ruoyi-ui/README.md new file mode 100644 index 0000000..00c0ab8 --- /dev/null +++ b/ruoyi-ui/README.md @@ -0,0 +1,30 @@ +## 开发 + +```bash +# 克隆项目 +git clone https://gitee.com/y_project/RuoYi-Vue + +# 进入项目目录 +cd ruoyi-ui + +# 安装依赖 +npm install + +# 建议不要直接使用 cnpm 安装依赖,会有各种诡异的 bug。可以通过如下操作解决 npm 下载速度慢的问题 +npm install --registry=https://registry.npmmirror.com + +# 启动服务 +npm run dev +``` + +浏览器访问 http://localhost:80 + +## 发布 + +```bash +# 构建测试环境 +npm run build:stage + +# 构建生产环境 +npm run build:prod +``` \ No newline at end of file diff --git a/ruoyi-ui/babel.config.js b/ruoyi-ui/babel.config.js new file mode 100644 index 0000000..c8267b2 --- /dev/null +++ b/ruoyi-ui/babel.config.js @@ -0,0 +1,13 @@ +module.exports = { + presets: [ + // https://github.com/vuejs/vue-cli/tree/master/packages/@vue/babel-preset-app + '@vue/cli-plugin-babel/preset' + ], + 'env': { + 'development': { + // babel-plugin-dynamic-import-node plugin only does one thing by converting all import() to require(). + // This plugin can significantly increase the speed of hot updates, when you have a large number of pages. + 'plugins': ['dynamic-import-node'] + } + } +} \ No newline at end of file diff --git a/ruoyi-ui/bin/build.bat b/ruoyi-ui/bin/build.bat new file mode 100644 index 0000000..dda590d --- /dev/null +++ b/ruoyi-ui/bin/build.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ´ò°üWeb¹¤³̣¬Éú³ÉdistÎļþ¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run build:prod + +pause \ No newline at end of file diff --git a/ruoyi-ui/bin/package.bat b/ruoyi-ui/bin/package.bat new file mode 100644 index 0000000..0e5bc0f --- /dev/null +++ b/ruoyi-ui/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] °²װWeb¹¤³̣¬Éú³Énode_modulesÎļþ¡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm install --registry=https://registry.npmmirror.com + +pause \ No newline at end of file diff --git a/ruoyi-ui/bin/run-web.bat b/ruoyi-ui/bin/run-web.bat new file mode 100644 index 0000000..d30deae --- /dev/null +++ b/ruoyi-ui/bin/run-web.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [ÐÅϢ] ʹÓà Vue CLI ÃüÁîÔËÐÐ Web ¹¤³̡£ +echo. + +%~d0 +cd %~dp0 + +cd .. +npm run dev + +pause \ No newline at end of file diff --git a/ruoyi-ui/build/index.js b/ruoyi-ui/build/index.js new file mode 100644 index 0000000..0c57de2 --- /dev/null +++ b/ruoyi-ui/build/index.js @@ -0,0 +1,35 @@ +const { run } = require('runjs') +const chalk = require('chalk') +const config = require('../vue.config.js') +const rawArgv = process.argv.slice(2) +const args = rawArgv.join(' ') + +if (process.env.npm_config_preview || rawArgv.includes('--preview')) { + const report = rawArgv.includes('--report') + + run(`vue-cli-service build ${args}`) + + const port = 9526 + const publicPath = config.publicPath + + var connect = require('connect') + var serveStatic = require('serve-static') + const app = connect() + + app.use( + publicPath, + serveStatic('./dist', { + index: ['index.html', '/'] + }) + ) + + app.listen(port, function () { + console.log(chalk.green(`> Preview at http://localhost:${port}${publicPath}`)) + if (report) { + console.log(chalk.green(`> Report at http://localhost:${port}${publicPath}report.html`)) + } + + }) +} else { + run(`vue-cli-service build ${args}`) +} diff --git a/ruoyi-ui/package.json b/ruoyi-ui/package.json new file mode 100644 index 0000000..55cbf34 --- /dev/null +++ b/ruoyi-ui/package.json @@ -0,0 +1,73 @@ +{ + "name": "FLRD", + "version": "3.8.9", + "description": "FORTUCKY WCS SYSTEM", + "author": "FLRD", + "license": "MIT", + "scripts": { + "dev": "vue-cli-service serve", + "build:prod": "vue-cli-service build", + "build:stage": "vue-cli-service build --mode staging", + "preview": "node build/index.js --preview" + }, + "keywords": [ + "vue", + "admin", + "dashboard", + "element-ui", + "boilerplate", + "admin-template", + "management-system" + ], + "repository": { + "type": "git", + "url": "https://gitee.com/y_project/RuoYi-Vue.git" + }, + "dependencies": { + "@riophae/vue-treeselect": "0.4.0", + "axios": "0.28.1", + "clipboard": "2.0.8", + "core-js": "3.37.1", + "echarts": "5.4.0", + "element-ui": "2.15.14", + "file-saver": "2.0.5", + "fuse.js": "6.4.3", + "highlight.js": "9.18.5", + "js-beautify": "1.13.0", + "js-cookie": "3.0.1", + "jsencrypt": "3.0.0-rc.1", + "nprogress": "0.2.0", + "quill": "2.0.2", + "screenfull": "5.0.2", + "sortablejs": "1.10.2", + "splitpanes": "2.4.1", + "vue": "2.6.12", + "vue-count-to": "1.0.13", + "vue-cropper": "0.5.5", + "vue-router": "3.4.9", + "vuedraggable": "2.24.3", + "vuex": "3.6.0", + "vue-i18n": "7.3.2" + }, + "devDependencies": { + "@vue/cli-plugin-babel": "4.4.6", + "@vue/cli-service": "4.4.6", + "babel-plugin-dynamic-import-node": "2.3.3", + "chalk": "4.1.0", + "compression-webpack-plugin": "6.1.2", + "connect": "3.6.6", + "sass": "1.32.13", + "sass-loader": "10.1.1", + "script-ext-html-webpack-plugin": "2.1.5", + "svg-sprite-loader": "5.1.1", + "vue-template-compiler": "2.6.12" + }, + "engines": { + "node": ">=8.9", + "npm": ">= 3.0.0" + }, + "browserslist": [ + "> 1%", + "last 2 versions" + ] +} diff --git a/ruoyi-ui/public/favicon.ico b/ruoyi-ui/public/favicon.ico new file mode 100644 index 0000000..e263760 Binary files /dev/null and b/ruoyi-ui/public/favicon.ico differ diff --git a/ruoyi-ui/public/html/ie.html b/ruoyi-ui/public/html/ie.html new file mode 100644 index 0000000..052ffcd --- /dev/null +++ b/ruoyi-ui/public/html/ie.html @@ -0,0 +1,46 @@ + + + + + + 请升级您的浏览器 + + + + + + +

请升级您的浏览器,以便我们更好的为您提供服务!

+

您正在使用 Internet Explorer 的早期版本(IE11以下版本或使用该内核的浏览器)。这意味着在升级浏览器前,您将无法访问此网站。

+
+

请注意:微软公司对Windows XP 及 Internet Explorer 早期版本的支持已经结束

+

自 2016 年 1 月 12 日起,Microsoft 不再为 IE 11 以下版本提供相应支持和更新。没有关键的浏览器安全更新,您的电脑可能易受有害病毒、间谍软件和其他恶意软件的攻击,它们可以窃取或损害您的业务数据和信息。请参阅 微软对 Internet Explorer 早期版本的支持将于 2016 年 1 月 12 日结束的说明 。

+
+

您可以选择更先进的浏览器

+

推荐使用以下浏览器的最新版本。如果您的电脑已有以下浏览器的最新版本则直接使用该浏览器访问即可。

+ +
+ + \ No newline at end of file diff --git a/ruoyi-ui/public/index.html b/ruoyi-ui/public/index.html new file mode 100644 index 0000000..925455c --- /dev/null +++ b/ruoyi-ui/public/index.html @@ -0,0 +1,208 @@ + + + + + + + + + <%= webpackConfig.name %> + + + + +
+
+
+
+
+
正在加载系统资源,请耐心等待
+
+
+ + diff --git a/ruoyi-ui/public/monitor/css/style.css b/ruoyi-ui/public/monitor/css/style.css new file mode 100644 index 0000000..9a208cf --- /dev/null +++ b/ruoyi-ui/public/monitor/css/style.css @@ -0,0 +1,344 @@ +/* 页面基本样式 */ +body { + background-color: #f3f3f4; /* 设置背景色 */ + margin: 0; + padding: 0; + overflow: hidden; /* 防止出现滚动条 */ +} + +/* 主容器样式 */ +#main { + position: absolute; + margin-top: 0; + margin-left: 0; + z-index: -99; /* 确保在最底层 */ + height: 870px; + width: 1700px; + transform: scale(1); + transform-origin: center center; + cursor: default; +} + +#main.dragging { + cursor: move; +} + +/* 小方块样式 */ +.small-box { + position: absolute; + background-color: #3c8dbc; + border: 2px solid #ffffff; + display: inline-block; + transition: all 0.3s ease; /* 添加过渡动画效果 */ +} + +/* 控制按钮容器样式 */ +.control-buttons { + position: fixed; + top: 20px; + right: 20px; + display: flex; + gap: 10px; + z-index: 1000; +} + +/* 控制按钮样式 */ +.control-buttons button { + padding: 10px 20px; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + transition: background-color 0.3s ease; +} + +#enableDragBtn { + background-color: #4CAF50; +} + +#enableDragBtn:hover:not(:disabled) { + background-color: #45a049; +} + +#disableDragBtn { + background-color: #f44336; +} + +#disableDragBtn:hover:not(:disabled) { + background-color: #da190b; +} + +.control-buttons button:disabled { + background-color: #cccccc; + cursor: not-allowed; + opacity: 0.7; +} + +/* 设备拖动控制按钮样式 */ +.drag-control-buttons { + display: flex; + gap: 10px; + margin-top: 5px; +} + +.drag-control-btn { + padding: 8px 16px; + border: none; + border-radius: 4px; + color: white; + cursor: pointer; + font-size: 14px; + transition: all 0.3s ease; +} + +.drag-control-btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +.enable-drag { + background-color: #4CAF50; +} + +.enable-drag:hover:not(:disabled) { + background-color: #45a049; +} + +.disable-drag { + background-color: #f44336; +} + +.disable-drag:hover:not(:disabled) { + background-color: #da190b; +} + +/* 链条输送机样式 */ +.conveyor { + position: absolute; + background-image: url('../img/Lssj.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + cursor: grab; + user-select: none; + transition: transform 0.1s ease; + z-index: 1; +} + +.conveyor:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); +} + +.conveyor:active { + cursor: grabbing; + transform: scale(1.02); +} + +.conveyor-center { + position: absolute; + width: 15px; + height: 15px; + background-color: #51bfff; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: none; +} + +/* 滚筒输送机样式 */ +.roller-conveyor { + position: absolute; + background-image: url('../img/Gssj.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + cursor: grab; + user-select: none; + transition: transform 0.1s ease; + z-index: 2; +} + +.roller-conveyor:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); +} + +.roller-conveyor:active { + cursor: grabbing; + transform: scale(1.02); +} + +.roller-conveyor-center { + position: absolute; + width: 15px; + height: 15px; + background-color: #51bfff; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: none; +} + +/* 堆垛机样式 */ +.stacker { + position: absolute; + background-image: url('../img/ddj.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + cursor: grab; + user-select: none; + transition: transform 0.1s ease, left 0.5s ease-in-out; +} + +.stacker:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.stacker:active { + cursor: grabbing; + transform: scale(1.02); +} + +.stacker-center { + position: absolute; + width: 15px; + height: 15px; + background-color: #51bfff; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: none; +} + +/* RGV样式 */ +.rgv { + position: absolute; + background-image: url('../img/rgv.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + cursor: grab; + user-select: none; + transition: transform 0.1s ease, left 0.5s ease-in-out; +} + +.rgv:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.rgv:active { + cursor: grabbing; + transform: scale(1.02); +} + +.rgv-center { + position: absolute; + width: 15px; + height: 15px; + background-color: #51bfff; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + display: none; +} + +/* 堆垛机轨道样式 */ +.track { + position: absolute; + background-image: url('../img/gd.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + cursor: grab; + user-select: none; + transition: transform 0.1s ease; +} + +.track:hover { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); +} + +.track:active { + cursor: grabbing; + transform: scale(1.02); +} + +/* 报警闪红光 */ +.breathing-border { + border: 1px solid #ff0000; + box-sizing: border-box; + animation: breathing 1.5s ease-in-out infinite; +} + +@keyframes breathing { + 0% { + border-color: #ff0000; + box-shadow: 0 0 0px 0px rgb(255, 0, 0); + } + + 50% { + border-color: #ff0000; + box-shadow: 0 0 15px 15px rgba(0, 123, 255, 0); + } + + 100% { + border-color: #ff0000; + box-shadow: 0 0 0px 0px rgba(255, 0, 0, 0); + } +} + +/* 报警信息滚动动画 */ +@keyframes scroll-left { + 0% { + left: 20%; + } + 100% { + left: -150%; + } +} + +/* 慢速滚动动画 */ +@keyframes scroll-left-slow { + 0% { + left: 100%; + } + 100% { + left: -100%; + } +} + +/* 列位置显示样式 */ +.column-positions { + margin-top: 10px; + max-height: 300px; + overflow-y: auto; + background-color: #f5f5f5; + border-radius: 4px; + padding: 10px; +} + +.column-positions-info p { + margin: 0 0 10px 0; + font-weight: bold; +} + +.positions-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + gap: 8px; +} + +.position-item { + background-color: #fff; + padding: 8px; + border-radius: 4px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + display: flex; + justify-content: space-between; + align-items: center; +} + +.position-item span:first-child { + color: #666; + font-size: 0.9em; +} + +.position-item span:last-child { + font-weight: bold; + color: #333; +} diff --git a/ruoyi-ui/public/monitor/img/Gssj.png b/ruoyi-ui/public/monitor/img/Gssj.png new file mode 100644 index 0000000..7a4c207 Binary files /dev/null and b/ruoyi-ui/public/monitor/img/Gssj.png differ diff --git a/ruoyi-ui/public/monitor/img/Lssj.png b/ruoyi-ui/public/monitor/img/Lssj.png new file mode 100644 index 0000000..60d1a7e Binary files /dev/null and b/ruoyi-ui/public/monitor/img/Lssj.png differ diff --git a/ruoyi-ui/public/monitor/img/ddj.png b/ruoyi-ui/public/monitor/img/ddj.png new file mode 100644 index 0000000..534cea4 Binary files /dev/null and b/ruoyi-ui/public/monitor/img/ddj.png differ diff --git a/ruoyi-ui/public/monitor/img/gd.png b/ruoyi-ui/public/monitor/img/gd.png new file mode 100644 index 0000000..5c9e901 Binary files /dev/null and b/ruoyi-ui/public/monitor/img/gd.png differ diff --git a/ruoyi-ui/public/monitor/img/rgv.png b/ruoyi-ui/public/monitor/img/rgv.png new file mode 100644 index 0000000..f51c912 Binary files /dev/null and b/ruoyi-ui/public/monitor/img/rgv.png differ diff --git a/ruoyi-ui/public/monitor/index.html b/ruoyi-ui/public/monitor/index.html new file mode 100644 index 0000000..048e407 --- /dev/null +++ b/ruoyi-ui/public/monitor/index.html @@ -0,0 +1,59 @@ + + + + + + 2D可视化监控系统 + + + + + +

2D可视化监控系统

+ + +
+
+
+ 🚨 0条报警 +
+
+
+
+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-ui/public/monitor/js/axios.min.js b/ruoyi-ui/public/monitor/js/axios.min.js new file mode 100644 index 0000000..b58abf2 --- /dev/null +++ b/ruoyi-ui/public/monitor/js/axios.min.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).axios=t()}(this,(function(){"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(t){for(var r=1;r=0;--o){var i=this.tryEntries[o],a=i.completion;if("root"===i.tryLoc)return r("end");if(i.tryLoc<=this.prev){var s=n.call(i,"catchLoc"),u=n.call(i,"finallyLoc");if(s&&u){if(this.prev=0;--r){var o=this.tryEntries[r];if(o.tryLoc<=this.prev&&n.call(o,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),S(r),l}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var o=n.arg;S(r)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:A(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),l}},e}function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}function o(e,t,r,n,o,i,a){try{var s=e[i](a),u=s.value}catch(e){return void r(e)}s.done?t(u):Promise.resolve(u).then(n,o)}function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function a(e,t){for(var r=0;re.length)&&(t=e.length);for(var r=0,n=new Array(t);r2&&void 0!==arguments[2]?arguments[2]:{},a=i.allOwnKeys,s=void 0!==a&&a;if(null!=e)if("object"!==n(e)&&(e=[e]),S(e))for(r=0,o=e.length;r0;)if(t===(r=n[o]).toLowerCase())return r;return null}var B="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:"undefined"!=typeof window?window:global,I=function(e){return!R(e)&&e!==B};var q,z=(q="undefined"!=typeof Uint8Array&&b(Uint8Array),function(e){return q&&e instanceof q}),M=E("HTMLFormElement"),H=function(e){var t=Object.prototype.hasOwnProperty;return function(e,r){return t.call(e,r)}}(),J=E("RegExp"),G=function(e,t){var r=Object.getOwnPropertyDescriptors(e),n={};U(r,(function(r,o){var i;!1!==(i=t(r,o,e))&&(n[o]=i||r)})),Object.defineProperties(e,n)},W="abcdefghijklmnopqrstuvwxyz",K="0123456789",V={DIGIT:K,ALPHA:W,ALPHA_DIGIT:W+W.toUpperCase()+K};var X=E("AsyncFunction"),$={isArray:S,isArrayBuffer:A,isBuffer:function(e){return null!==e&&!R(e)&&null!==e.constructor&&!R(e.constructor)&&x(e.constructor.isBuffer)&&e.constructor.isBuffer(e)},isFormData:function(e){var t;return e&&("function"==typeof FormData&&e instanceof FormData||x(e.append)&&("formdata"===(t=w(e))||"object"===t&&x(e.toString)&&"[object FormData]"===e.toString()))},isArrayBufferView:function(e){return"undefined"!=typeof ArrayBuffer&&ArrayBuffer.isView?ArrayBuffer.isView(e):e&&e.buffer&&A(e.buffer)},isString:j,isNumber:T,isBoolean:function(e){return!0===e||!1===e},isObject:P,isPlainObject:N,isUndefined:R,isDate:k,isFile:_,isBlob:L,isRegExp:J,isFunction:x,isStream:function(e){return P(e)&&x(e.pipe)},isURLSearchParams:F,isTypedArray:z,isFileList:C,forEach:U,merge:function e(){for(var t=I(this)&&this||{},r=t.caseless,n={},o=function(t,o){var i=r&&D(n,o)||o;N(n[i])&&N(t)?n[i]=e(n[i],t):N(t)?n[i]=e({},t):S(t)?n[i]=t.slice():n[i]=t},i=0,a=arguments.length;i3&&void 0!==arguments[3]?arguments[3]:{},o=n.allOwnKeys;return U(t,(function(t,n){r&&x(t)?e[n]=y(t,r):e[n]=t}),{allOwnKeys:o}),e},trim:function(e){return e.trim?e.trim():e.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")},stripBOM:function(e){return 65279===e.charCodeAt(0)&&(e=e.slice(1)),e},inherits:function(e,t,r,n){e.prototype=Object.create(t.prototype,n),e.prototype.constructor=e,Object.defineProperty(e,"super",{value:t.prototype}),r&&Object.assign(e.prototype,r)},toFlatObject:function(e,t,r,n){var o,i,a,s={};if(t=t||{},null==e)return t;do{for(i=(o=Object.getOwnPropertyNames(e)).length;i-- >0;)a=o[i],n&&!n(a,e,t)||s[a]||(t[a]=e[a],s[a]=!0);e=!1!==r&&b(e)}while(e&&(!r||r(e,t))&&e!==Object.prototype);return t},kindOf:w,kindOfTest:E,endsWith:function(e,t,r){e=String(e),(void 0===r||r>e.length)&&(r=e.length),r-=t.length;var n=e.indexOf(t,r);return-1!==n&&n===r},toArray:function(e){if(!e)return null;if(S(e))return e;var t=e.length;if(!T(t))return null;for(var r=new Array(t);t-- >0;)r[t]=e[t];return r},forEachEntry:function(e,t){for(var r,n=(e&&e[Symbol.iterator]).call(e);(r=n.next())&&!r.done;){var o=r.value;t.call(e,o[0],o[1])}},matchAll:function(e,t){for(var r,n=[];null!==(r=e.exec(t));)n.push(r);return n},isHTMLForm:M,hasOwnProperty:H,hasOwnProp:H,reduceDescriptors:G,freezeMethods:function(e){G(e,(function(t,r){if(x(e)&&-1!==["arguments","caller","callee"].indexOf(r))return!1;var n=e[r];x(n)&&(t.enumerable=!1,"writable"in t?t.writable=!1:t.set||(t.set=function(){throw Error("Can not rewrite read-only method '"+r+"'")}))}))},toObjectSet:function(e,t){var r={},n=function(e){e.forEach((function(e){r[e]=!0}))};return S(e)?n(e):n(String(e).split(t)),r},toCamelCase:function(e){return e.toLowerCase().replace(/[-_\s]([a-z\d])(\w*)/g,(function(e,t,r){return t.toUpperCase()+r}))},noop:function(){},toFiniteNumber:function(e,t){return e=+e,Number.isFinite(e)?e:t},findKey:D,global:B,isContextDefined:I,ALPHABET:V,generateString:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:16,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:V.ALPHA_DIGIT,r="",n=t.length;e--;)r+=t[Math.random()*n|0];return r},isSpecCompliantForm:function(e){return!!(e&&x(e.append)&&"FormData"===e[Symbol.toStringTag]&&e[Symbol.iterator])},toJSONObject:function(e){var t=new Array(10);return function e(r,n){if(P(r)){if(t.indexOf(r)>=0)return;if(!("toJSON"in r)){t[n]=r;var o=S(r)?[]:{};return U(r,(function(t,r){var i=e(t,n+1);!R(i)&&(o[r]=i)})),t[n]=void 0,o}}return r}(e,0)},isAsyncFn:X,isThenable:function(e){return e&&(P(e)||x(e))&&x(e.then)&&x(e.catch)}};function Q(e,t,r,n,o){Error.call(this),Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.message=e,this.name="AxiosError",t&&(this.code=t),r&&(this.config=r),n&&(this.request=n),o&&(this.response=o)}$.inherits(Q,Error,{toJSON:function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:$.toJSONObject(this.config),code:this.code,status:this.response&&this.response.status?this.response.status:null}}});var Y=Q.prototype,Z={};["ERR_BAD_OPTION_VALUE","ERR_BAD_OPTION","ECONNABORTED","ETIMEDOUT","ERR_NETWORK","ERR_FR_TOO_MANY_REDIRECTS","ERR_DEPRECATED","ERR_BAD_RESPONSE","ERR_BAD_REQUEST","ERR_CANCELED","ERR_NOT_SUPPORT","ERR_INVALID_URL"].forEach((function(e){Z[e]={value:e}})),Object.defineProperties(Q,Z),Object.defineProperty(Y,"isAxiosError",{value:!0}),Q.from=function(e,t,r,n,o,i){var a=Object.create(Y);return $.toFlatObject(e,a,(function(e){return e!==Error.prototype}),(function(e){return"isAxiosError"!==e})),Q.call(a,e.message,t,r,n,o),a.cause=e,a.name=e.name,i&&Object.assign(a,i),a};function ee(e){return $.isPlainObject(e)||$.isArray(e)}function te(e){return $.endsWith(e,"[]")?e.slice(0,-2):e}function re(e,t,r){return e?e.concat(t).map((function(e,t){return e=te(e),!r&&t?"["+e+"]":e})).join(r?".":""):t}var ne=$.toFlatObject($,{},null,(function(e){return/^is[A-Z]/.test(e)}));function oe(e,t,r){if(!$.isObject(e))throw new TypeError("target must be an object");t=t||new FormData;var o=(r=$.toFlatObject(r,{metaTokens:!0,dots:!1,indexes:!1},!1,(function(e,t){return!$.isUndefined(t[e])}))).metaTokens,i=r.visitor||f,a=r.dots,s=r.indexes,u=(r.Blob||"undefined"!=typeof Blob&&Blob)&&$.isSpecCompliantForm(t);if(!$.isFunction(i))throw new TypeError("visitor must be a function");function c(e){if(null===e)return"";if($.isDate(e))return e.toISOString();if(!u&&$.isBlob(e))throw new Q("Blob is not supported. Use a Buffer instead.");return $.isArrayBuffer(e)||$.isTypedArray(e)?u&&"function"==typeof Blob?new Blob([e]):Buffer.from(e):e}function f(e,r,i){var u=e;if(e&&!i&&"object"===n(e))if($.endsWith(r,"{}"))r=o?r:r.slice(0,-2),e=JSON.stringify(e);else if($.isArray(e)&&function(e){return $.isArray(e)&&!e.some(ee)}(e)||($.isFileList(e)||$.endsWith(r,"[]"))&&(u=$.toArray(e)))return r=te(r),u.forEach((function(e,n){!$.isUndefined(e)&&null!==e&&t.append(!0===s?re([r],n,a):null===s?r:r+"[]",c(e))})),!1;return!!ee(e)||(t.append(re(i,r,a),c(e)),!1)}var l=[],h=Object.assign(ne,{defaultVisitor:f,convertValue:c,isVisitable:ee});if(!$.isObject(e))throw new TypeError("data must be an object");return function e(r,n){if(!$.isUndefined(r)){if(-1!==l.indexOf(r))throw Error("Circular reference detected in "+n.join("."));l.push(r),$.forEach(r,(function(r,o){!0===(!($.isUndefined(r)||null===r)&&i.call(t,r,$.isString(o)?o.trim():o,n,h))&&e(r,n?n.concat(o):[o])})),l.pop()}}(e),t}function ie(e){var t={"!":"%21","'":"%27","(":"%28",")":"%29","~":"%7E","%20":"+","%00":"\0"};return encodeURIComponent(e).replace(/[!'()~]|%20|%00/g,(function(e){return t[e]}))}function ae(e,t){this._pairs=[],e&&oe(e,this,t)}var se=ae.prototype;function ue(e){return encodeURIComponent(e).replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}function ce(e,t,r){if(!t)return e;var n,o=r&&r.encode||ue,i=r&&r.serialize;if(n=i?i(t,r):$.isURLSearchParams(t)?t.toString():new ae(t,r).toString(o)){var a=e.indexOf("#");-1!==a&&(e=e.slice(0,a)),e+=(-1===e.indexOf("?")?"?":"&")+n}return e}se.append=function(e,t){this._pairs.push([e,t])},se.toString=function(e){var t=e?function(t){return e.call(this,t,ie)}:ie;return this._pairs.map((function(e){return t(e[0])+"="+t(e[1])}),"").join("&")};var fe,le=function(){function e(){i(this,e),this.handlers=[]}return s(e,[{key:"use",value:function(e,t,r){return this.handlers.push({fulfilled:e,rejected:t,synchronous:!!r&&r.synchronous,runWhen:r?r.runWhen:null}),this.handlers.length-1}},{key:"eject",value:function(e){this.handlers[e]&&(this.handlers[e]=null)}},{key:"clear",value:function(){this.handlers&&(this.handlers=[])}},{key:"forEach",value:function(e){$.forEach(this.handlers,(function(t){null!==t&&e(t)}))}}]),e}(),he={silentJSONParsing:!0,forcedJSONParsing:!0,clarifyTimeoutError:!1},de={isBrowser:!0,classes:{URLSearchParams:"undefined"!=typeof URLSearchParams?URLSearchParams:ae,FormData:"undefined"!=typeof FormData?FormData:null,Blob:"undefined"!=typeof Blob?Blob:null},protocols:["http","https","file","blob","url","data"]},pe="undefined"!=typeof window&&"undefined"!=typeof document,ve=(fe="undefined"!=typeof navigator&&navigator.product,pe&&["ReactNative","NativeScript","NS"].indexOf(fe)<0),ye="undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope&&"function"==typeof self.importScripts,me=t(t({},Object.freeze({__proto__:null,hasBrowserEnv:pe,hasStandardBrowserWebWorkerEnv:ye,hasStandardBrowserEnv:ve})),de);function ge(e){function t(e,r,n,o){var i=e[o++];if("__proto__"===i)return!0;var a=Number.isFinite(+i),s=o>=e.length;return i=!i&&$.isArray(n)?n.length:i,s?($.hasOwnProp(n,i)?n[i]=[n[i],r]:n[i]=r,!a):(n[i]&&$.isObject(n[i])||(n[i]=[]),t(e,r,n[i],o)&&$.isArray(n[i])&&(n[i]=function(e){var t,r,n={},o=Object.keys(e),i=o.length;for(t=0;t-1,i=$.isObject(e);if(i&&$.isHTMLForm(e)&&(e=new FormData(e)),$.isFormData(e))return o?JSON.stringify(ge(e)):e;if($.isArrayBuffer(e)||$.isBuffer(e)||$.isStream(e)||$.isFile(e)||$.isBlob(e))return e;if($.isArrayBufferView(e))return e.buffer;if($.isURLSearchParams(e))return t.setContentType("application/x-www-form-urlencoded;charset=utf-8",!1),e.toString();if(i){if(n.indexOf("application/x-www-form-urlencoded")>-1)return function(e,t){return oe(e,new me.classes.URLSearchParams,Object.assign({visitor:function(e,t,r,n){return me.isNode&&$.isBuffer(e)?(this.append(t,e.toString("base64")),!1):n.defaultVisitor.apply(this,arguments)}},t))}(e,this.formSerializer).toString();if((r=$.isFileList(e))||n.indexOf("multipart/form-data")>-1){var a=this.env&&this.env.FormData;return oe(r?{"files[]":e}:e,a&&new a,this.formSerializer)}}return i||o?(t.setContentType("application/json",!1),function(e,t,r){if($.isString(e))try{return(t||JSON.parse)(e),$.trim(e)}catch(e){if("SyntaxError"!==e.name)throw e}return(r||JSON.stringify)(e)}(e)):e}],transformResponse:[function(e){var t=this.transitional||be.transitional,r=t&&t.forcedJSONParsing,n="json"===this.responseType;if(e&&$.isString(e)&&(r&&!this.responseType||n)){var o=!(t&&t.silentJSONParsing)&&n;try{return JSON.parse(e)}catch(e){if(o){if("SyntaxError"===e.name)throw Q.from(e,Q.ERR_BAD_RESPONSE,this,null,this.response);throw e}}}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,maxBodyLength:-1,env:{FormData:me.classes.FormData,Blob:me.classes.Blob},validateStatus:function(e){return e>=200&&e<300},headers:{common:{Accept:"application/json, text/plain, */*","Content-Type":void 0}}};$.forEach(["delete","get","head","post","put","patch"],(function(e){be.headers[e]={}}));var we=be,Ee=$.toObjectSet(["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"]),Oe=Symbol("internals");function Se(e){return e&&String(e).trim().toLowerCase()}function Re(e){return!1===e||null==e?e:$.isArray(e)?e.map(Re):String(e)}function Ae(e,t,r,n,o){return $.isFunction(n)?n.call(this,t,r):(o&&(t=r),$.isString(t)?$.isString(n)?-1!==t.indexOf(n):$.isRegExp(n)?n.test(t):void 0:void 0)}var je=function(e,t){function r(e){i(this,r),e&&this.set(e)}return s(r,[{key:"set",value:function(e,t,r){var n=this;function o(e,t,r){var o=Se(t);if(!o)throw new Error("header name must be a non-empty string");var i=$.findKey(n,o);(!i||void 0===n[i]||!0===r||void 0===r&&!1!==n[i])&&(n[i||t]=Re(e))}var i,a,s,u,c,f=function(e,t){return $.forEach(e,(function(e,r){return o(e,r,t)}))};return $.isPlainObject(e)||e instanceof this.constructor?f(e,t):$.isString(e)&&(e=e.trim())&&!/^[-_a-zA-Z0-9^`|~,!#$%&'*+.]+$/.test(e.trim())?f((c={},(i=e)&&i.split("\n").forEach((function(e){u=e.indexOf(":"),a=e.substring(0,u).trim().toLowerCase(),s=e.substring(u+1).trim(),!a||c[a]&&Ee[a]||("set-cookie"===a?c[a]?c[a].push(s):c[a]=[s]:c[a]=c[a]?c[a]+", "+s:s)})),c),t):null!=e&&o(t,e,r),this}},{key:"get",value:function(e,t){if(e=Se(e)){var r=$.findKey(this,e);if(r){var n=this[r];if(!t)return n;if(!0===t)return function(e){for(var t,r=Object.create(null),n=/([^\s,;=]+)\s*(?:=\s*([^,;]+))?/g;t=n.exec(e);)r[t[1]]=t[2];return r}(n);if($.isFunction(t))return t.call(this,n,r);if($.isRegExp(t))return t.exec(n);throw new TypeError("parser must be boolean|regexp|function")}}}},{key:"has",value:function(e,t){if(e=Se(e)){var r=$.findKey(this,e);return!(!r||void 0===this[r]||t&&!Ae(0,this[r],r,t))}return!1}},{key:"delete",value:function(e,t){var r=this,n=!1;function o(e){if(e=Se(e)){var o=$.findKey(r,e);!o||t&&!Ae(0,r[o],o,t)||(delete r[o],n=!0)}}return $.isArray(e)?e.forEach(o):o(e),n}},{key:"clear",value:function(e){for(var t=Object.keys(this),r=t.length,n=!1;r--;){var o=t[r];e&&!Ae(0,this[o],o,e,!0)||(delete this[o],n=!0)}return n}},{key:"normalize",value:function(e){var t=this,r={};return $.forEach(this,(function(n,o){var i=$.findKey(r,o);if(i)return t[i]=Re(n),void delete t[o];var a=e?function(e){return e.trim().toLowerCase().replace(/([a-z\d])(\w*)/g,(function(e,t,r){return t.toUpperCase()+r}))}(o):String(o).trim();a!==o&&delete t[o],t[a]=Re(n),r[a]=!0})),this}},{key:"concat",value:function(){for(var e,t=arguments.length,r=new Array(t),n=0;n1?r-1:0),o=1;o1?"since :\n"+s.map(Ue).join("\n"):" "+Ue(s[0]):"as no adapter specified"),"ERR_NOT_SUPPORT")}return r};function Ie(e){if(e.cancelToken&&e.cancelToken.throwIfRequested(),e.signal&&e.signal.aborted)throw new Ne(null,e)}function qe(e){return Ie(e),e.headers=xe.from(e.headers),e.data=Te.call(e,e.transformRequest),-1!==["post","put","patch"].indexOf(e.method)&&e.headers.setContentType("application/x-www-form-urlencoded",!1),Be(e.adapter||we.adapter)(e).then((function(t){return Ie(e),t.data=Te.call(e,e.transformResponse,t),t.headers=xe.from(t.headers),t}),(function(t){return Pe(t)||(Ie(e),t&&t.response&&(t.response.data=Te.call(e,e.transformResponse,t.response),t.response.headers=xe.from(t.response.headers))),Promise.reject(t)}))}var ze=function(e){return e instanceof xe?e.toJSON():e};function Me(e,t){t=t||{};var r={};function n(e,t,r){return $.isPlainObject(e)&&$.isPlainObject(t)?$.merge.call({caseless:r},e,t):$.isPlainObject(t)?$.merge({},t):$.isArray(t)?t.slice():t}function o(e,t,r){return $.isUndefined(t)?$.isUndefined(e)?void 0:n(void 0,e,r):n(e,t,r)}function i(e,t){if(!$.isUndefined(t))return n(void 0,t)}function a(e,t){return $.isUndefined(t)?$.isUndefined(e)?void 0:n(void 0,e):n(void 0,t)}function s(r,o,i){return i in t?n(r,o):i in e?n(void 0,r):void 0}var u={url:i,method:i,data:i,baseURL:a,transformRequest:a,transformResponse:a,paramsSerializer:a,timeout:a,timeoutMessage:a,withCredentials:a,withXSRFToken:a,adapter:a,responseType:a,xsrfCookieName:a,xsrfHeaderName:a,onUploadProgress:a,onDownloadProgress:a,decompress:a,maxContentLength:a,maxBodyLength:a,beforeRedirect:a,transport:a,httpAgent:a,httpsAgent:a,cancelToken:a,socketPath:a,responseEncoding:a,validateStatus:s,headers:function(e,t){return o(ze(e),ze(t),!0)}};return $.forEach(Object.keys(Object.assign({},e,t)),(function(n){var i=u[n]||o,a=i(e[n],t[n],n);$.isUndefined(a)&&i!==s||(r[n]=a)})),r}var He="1.6.7",Je={};["object","boolean","number","function","string","symbol"].forEach((function(e,t){Je[e]=function(r){return n(r)===e||"a"+(t<1?"n ":" ")+e}}));var Ge={};Je.transitional=function(e,t,r){function n(e,t){return"[Axios v1.6.7] Transitional option '"+e+"'"+t+(r?". "+r:"")}return function(r,o,i){if(!1===e)throw new Q(n(o," has been removed"+(t?" in "+t:"")),Q.ERR_DEPRECATED);return t&&!Ge[o]&&(Ge[o]=!0,console.warn(n(o," has been deprecated since v"+t+" and will be removed in the near future"))),!e||e(r,o,i)}};var We={assertOptions:function(e,t,r){if("object"!==n(e))throw new Q("options must be an object",Q.ERR_BAD_OPTION_VALUE);for(var o=Object.keys(e),i=o.length;i-- >0;){var a=o[i],s=t[a];if(s){var u=e[a],c=void 0===u||s(u,a,e);if(!0!==c)throw new Q("option "+a+" must be "+c,Q.ERR_BAD_OPTION_VALUE)}else if(!0!==r)throw new Q("Unknown option "+a,Q.ERR_BAD_OPTION)}},validators:Je},Ke=We.validators,Ve=function(){function e(t){i(this,e),this.defaults=t,this.interceptors={request:new le,response:new le}}var t,n;return s(e,[{key:"request",value:(t=r().mark((function e(t,n){var o,i;return r().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.prev=0,e.next=3,this._request(t,n);case 3:return e.abrupt("return",e.sent);case 6:throw e.prev=6,e.t0=e.catch(0),e.t0 instanceof Error&&(Error.captureStackTrace?Error.captureStackTrace(o={}):o=new Error,i=o.stack?o.stack.replace(/^.+\n/,""):"",e.t0.stack?i&&!String(e.t0.stack).endsWith(i.replace(/^.+\n.+\n/,""))&&(e.t0.stack+="\n"+i):e.t0.stack=i),e.t0;case 10:case"end":return e.stop()}}),e,this,[[0,6]])})),n=function(){var e=this,r=arguments;return new Promise((function(n,i){var a=t.apply(e,r);function s(e){o(a,n,i,s,u,"next",e)}function u(e){o(a,n,i,s,u,"throw",e)}s(void 0)}))},function(e,t){return n.apply(this,arguments)})},{key:"_request",value:function(e,t){"string"==typeof e?(t=t||{}).url=e:t=e||{};var r=t=Me(this.defaults,t),n=r.transitional,o=r.paramsSerializer,i=r.headers;void 0!==n&&We.assertOptions(n,{silentJSONParsing:Ke.transitional(Ke.boolean),forcedJSONParsing:Ke.transitional(Ke.boolean),clarifyTimeoutError:Ke.transitional(Ke.boolean)},!1),null!=o&&($.isFunction(o)?t.paramsSerializer={serialize:o}:We.assertOptions(o,{encode:Ke.function,serialize:Ke.function},!0)),t.method=(t.method||this.defaults.method||"get").toLowerCase();var a=i&&$.merge(i.common,i[t.method]);i&&$.forEach(["delete","get","head","post","put","patch","common"],(function(e){delete i[e]})),t.headers=xe.concat(a,i);var s=[],u=!0;this.interceptors.request.forEach((function(e){"function"==typeof e.runWhen&&!1===e.runWhen(t)||(u=u&&e.synchronous,s.unshift(e.fulfilled,e.rejected))}));var c,f=[];this.interceptors.response.forEach((function(e){f.push(e.fulfilled,e.rejected)}));var l,h=0;if(!u){var d=[qe.bind(this),void 0];for(d.unshift.apply(d,s),d.push.apply(d,f),l=d.length,c=Promise.resolve(t);h0;)n._listeners[t](e);n._listeners=null}})),this.promise.then=function(e){var t,r=new Promise((function(e){n.subscribe(e),t=e})).then(e);return r.cancel=function(){n.unsubscribe(t)},r},t((function(e,t,o){n.reason||(n.reason=new Ne(e,t,o),r(n.reason))}))}return s(e,[{key:"throwIfRequested",value:function(){if(this.reason)throw this.reason}},{key:"subscribe",value:function(e){this.reason?e(this.reason):this._listeners?this._listeners.push(e):this._listeners=[e]}},{key:"unsubscribe",value:function(e){if(this._listeners){var t=this._listeners.indexOf(e);-1!==t&&this._listeners.splice(t,1)}}}],[{key:"source",value:function(){var t;return{token:new e((function(e){t=e})),cancel:t}}}]),e}();var Qe={Continue:100,SwitchingProtocols:101,Processing:102,EarlyHints:103,Ok:200,Created:201,Accepted:202,NonAuthoritativeInformation:203,NoContent:204,ResetContent:205,PartialContent:206,MultiStatus:207,AlreadyReported:208,ImUsed:226,MultipleChoices:300,MovedPermanently:301,Found:302,SeeOther:303,NotModified:304,UseProxy:305,Unused:306,TemporaryRedirect:307,PermanentRedirect:308,BadRequest:400,Unauthorized:401,PaymentRequired:402,Forbidden:403,NotFound:404,MethodNotAllowed:405,NotAcceptable:406,ProxyAuthenticationRequired:407,RequestTimeout:408,Conflict:409,Gone:410,LengthRequired:411,PreconditionFailed:412,PayloadTooLarge:413,UriTooLong:414,UnsupportedMediaType:415,RangeNotSatisfiable:416,ExpectationFailed:417,ImATeapot:418,MisdirectedRequest:421,UnprocessableEntity:422,Locked:423,FailedDependency:424,TooEarly:425,UpgradeRequired:426,PreconditionRequired:428,TooManyRequests:429,RequestHeaderFieldsTooLarge:431,UnavailableForLegalReasons:451,InternalServerError:500,NotImplemented:501,BadGateway:502,ServiceUnavailable:503,GatewayTimeout:504,HttpVersionNotSupported:505,VariantAlsoNegotiates:506,InsufficientStorage:507,LoopDetected:508,NotExtended:510,NetworkAuthenticationRequired:511};Object.entries(Qe).forEach((function(e){var t=c(e,2),r=t[0],n=t[1];Qe[n]=r}));var Ye=Qe;var Ze=function e(t){var r=new Xe(t),n=y(Xe.prototype.request,r);return $.extend(n,Xe.prototype,r,{allOwnKeys:!0}),$.extend(n,r,null,{allOwnKeys:!0}),n.create=function(r){return e(Me(t,r))},n}(we);return Ze.Axios=Xe,Ze.CanceledError=Ne,Ze.CancelToken=$e,Ze.isCancel=Pe,Ze.VERSION=He,Ze.toFormData=oe,Ze.AxiosError=Q,Ze.Cancel=Ze.CanceledError,Ze.all=function(e){return Promise.all(e)},Ze.spread=function(e){return function(t){return e.apply(null,t)}},Ze.isAxiosError=function(e){return $.isObject(e)&&!0===e.isAxiosError},Ze.mergeConfig=Me,Ze.AxiosHeaders=xe,Ze.formToJSON=function(e){return ge($.isHTMLForm(e)?new FormData(e):e)},Ze.getAdapter=Be,Ze.HttpStatusCode=Ye,Ze.default=Ze,Ze})); +//# sourceMappingURL=axios.min.js.map diff --git a/ruoyi-ui/public/monitor/js/index.js b/ruoyi-ui/public/monitor/js/index.js new file mode 100644 index 0000000..edb7282 --- /dev/null +++ b/ruoyi-ui/public/monitor/js/index.js @@ -0,0 +1,1964 @@ + +// 查询堆垛机数据列表 +function listDdj() { + request({ + url: '/dev-api/dynamicMonitoring/list/ddjList', + method: 'get' + }).then(response => { + handleStackerDataSuccess(response) + }).catch(error => { + console.error('堆垛机数据获取失败:', error); + }); +} + + +// 查询输送机数据列表 +function listSsj() { + request({ + url: '/dev-api/dynamicMonitoring/list/ssjList', + method: 'get' + }).then(response => { + handleConveyorDataSuccess(response) + }).catch(error => { + console.error('输送机数据获取失败:', error) + }); +} +// 查询报警信息数据列表 +function listAlarm() { + request({ + url: '/dev-api/dynamicMonitoring/list/alarmList', + method: 'get' + }).then(response => { + console.log("报警信息:",response) + handleAlarmDataSuccess(response) + }).catch(error => { + handleAlarmDataError(response) + }); +} +/** + * 2D可视化监控系统 - 数据同步和状态管理 + * 负责配置同步、设备状态更新、实时数据获取等功能 + */ + +// ==================== 全局变量 ==================== + +/** + * 配置编辑模式标识 + * @type {number} 1-编辑模式, 2-演示模式 + */ +let configEditMode = 2; + +// ==================== 配置同步功能 ==================== + +/** + * 获取系统配置并同步状态 + * 从服务器获取最新配置,更新本地状态和UI + */ +function getConfig() { + $.ajax({ + url: API_URLS.config, + type: 'GET', + dataType: 'json', + cache: false, + success: handleConfigSuccess, + error: handleConfigError + }); +} + +/** + * 处理配置获取成功 + * @param {Object} data 配置数据 + */ +function handleConfigSuccess(data) { + // 更新编辑模式状态 + updateEditMode(data.editMode); + + // 同步main.js中的全局配置 + syncMainConfig(data); + + // 更新组件拖拽状态 + updateDraggableStatus(data.deviceDraggable); +} + +/** + * 处理配置获取失败 + * @param {Object} xhr XMLHttpRequest对象 + * @param {string} status 状态信息 + * @param {string} error 错误信息 + */ +function handleConfigError(xhr, status, error) { + console.error('配置获取失败:', error); +} + +/** + * 更新编辑模式状态 + * @param {boolean} editMode 是否为编辑模式 + */ +function updateEditMode(editMode) { + configEditMode = editMode ? 1 : 2; + const modeText = editMode ? '编辑模式' : '演示模式'; +} + +/** + * 同步main.js中的全局配置 + * @param {Object} data 配置数据 + */ +function syncMainConfig(data) { + if (!window.config) return; + + // 更新所有配置项 + window.config.deviceDraggable = data.deviceDraggable; + window.config.editMode = data.editMode; + window.config.allowBackendAccess = data.allowBackendAccess; + window.config.labels = data.labels || []; + window.config.conveyors = data.conveyors || []; + window.config.stackers = data.stackers || []; + window.config.tracks = data.tracks || []; + window.config.rollerConveyors = data.rollerConveyors || []; + window.config.titleSettings = data.titleSettings; + + // 调用main.js中的更新函数 + if (typeof window.updateDevicesDraggableState === 'function') { + window.updateDevicesDraggableState(); + } + + // 更新标签显示 + if (typeof window.updateLabels === 'function') { + window.updateLabels(); + } + + // 更新页面标题样式 + if (typeof window.updatePageTitleStyle === 'function' && data.titleSettings) { + window.updatePageTitleStyle(data.titleSettings); + } +} + +// ==================== UI状态管理 ==================== + +/** + * 更新所有组件的拖拽状态 + * 根据配置更新设备元素的鼠标样式和拖拽能力 + * @param {boolean} isDraggable 是否允许拖拽 + */ +function updateDraggableStatus(isDraggable) { + const draggableElements = document.querySelectorAll('.conveyor, .roller-conveyor, .stacker, .label'); + + draggableElements.forEach(element => { + // 根据元素类型设置不同的鼠标样式 + const cursorStyle = isDraggable ? + (element.classList.contains('stacker') ? 'grab' : 'move') : + 'default'; + element.style.cursor = cursorStyle; + }); + + // 同步更新全局配置 + if (window.config) { + window.config.deviceDraggable = isDraggable; + } +} + +// ==================== 页面初始化 ==================== + +/** + * 页面加载完成后的初始化 + */ +$(document).ready(function() { + // 立即执行一次数据获取 + initializeData(); + + // 设置实时配置监听 + setupConfigListeners(); + + // 启动定时器 + startTimers(); +}); + +/** + * 初始化数据获取 + */ +function initializeData() { + getConfig(); + updateConveyorStatus(); + updateStackerStatus(); +} + +/** + * 设置配置变化监听器 + */ +function setupConfigListeners() { + // 监听localStorage变化(about页面通知) + setupLocalStorageListener(); + + // 监听BroadcastChannel消息(跨标签页通信) + setupBroadcastChannelListener(); +} + +/** + * 设置localStorage监听器 + */ +function setupLocalStorageListener() { + let lastRefreshTime = localStorage.getItem('refreshIndex'); + + setInterval(() => { + const currentRefreshTime = localStorage.getItem('refreshIndex'); + if (currentRefreshTime && currentRefreshTime !== lastRefreshTime) { + lastRefreshTime = currentRefreshTime; + getConfig(); + } + }, 100); +} + +/** + * 设置BroadcastChannel监听器 + */ +function setupBroadcastChannelListener() { + if (!window.BroadcastChannel) return; + + const broadcastChannel = new BroadcastChannel('index_refresh_channel'); + broadcastChannel.onmessage = (event) => { + if (event.data.refresh) { + getConfig(); + } + }; +} + +// 报警更新定时器ID +let alarmUpdateTimer = null; + +/** + * 启动定时器 + */ +function startTimers() { + // 设备状态更新定时器 + setInterval(updateConveyorStatus, 5000); + setInterval(updateStackerStatus, 5000); + setInterval(updateRgvStatus, 5000); + + // 配置同步定时器 + // setInterval(getConfig, 1000); + + // 启动智能报警更新定时器 + startSmartAlarmTimer(); +} + +/** + * 启动报警系统 + */ +function startSmartAlarmTimer() { + // 立即执行一次 + updateAlarmStatus(); + console.log('报警系统已启动'); +} + +/** + * 安排下次报警更新 + * @param {number} delay 延迟时间(毫秒) + */ +function scheduleNextAlarmUpdate(delay) { + // 清除现有定时器 + if (alarmUpdateTimer) { + clearTimeout(alarmUpdateTimer); + } + + alarmUpdateTimer = setTimeout(() => { + updateAlarmStatus(); + // 递归安排下次更新 + scheduleNextAlarmUpdate(3000); + }, delay); +} + +// ==================== 设备状态更新 ==================== + +/** + * 更新输送机状态 + * 从服务器获取输送机实时数据并更新UI显示 + */ +function updateConveyorStatus() { + // 检查是否允许访问后台数据 + if (!canAccessBackendData()) { + return; + } + + listSsj() +} + +/** + * 处理输送机数据获取成功 + * @param {Object} response 服务器响应数据 + */ +function handleConveyorDataSuccess(response) { + if (configEditMode !== 2) { + return; + } + + const conveyors = document.querySelectorAll('.conveyor, .roller-conveyor'); + + conveyors.forEach(conveyor => { + const conveyorId = conveyor.getAttribute('id'); + const deviceData = response.data.find(item => item.id === conveyorId); + + if (deviceData) { + updateConveyorDisplay(conveyor, deviceData); + } + }); +} + +/** + * 更新单个输送机的显示状态 + * @param {HTMLElement} conveyor 输送机DOM元素 + * @param {Object} data 设备数据 + */ +function updateConveyorDisplay(conveyor, data) { + + // 更新中心方块显示状态 + const centerBlock = conveyor.querySelector('.conveyor-center, .roller-conveyor-center'); + if (centerBlock) { + + centerBlock.style.display = data.dw === '1' ? 'block' : 'none'; + } + + // 更新设备状态指示(故障闪烁) + const hasError = parseInt(data.sbzt) > 3; + conveyor.classList.toggle('breathing-border', hasError); +} + + // ==================== 辅助函数 ==================== + + /** + * 检查是否允许访问后台数据 + * @returns {boolean} 是否允许访问 + */ + function canAccessBackendData() { + if (window.config && window.config.allowBackendAccess === false) { + return false; + } + return true; + } + + /** + * 验证服务器响应数据是否有效 + * @param {Object} response 服务器响应 + * @returns {boolean} 数据是否有效 + */ + function isValidResponse(response) { + return response && response.code === 0 && response.data; + } + + /** + * 更新堆垛机状态 + * 从服务器获取堆垛机实时数据并更新UI显示 + */ + function updateStackerStatus() { + // 检查是否允许访问后台数据 + if (!canAccessBackendData()) { + return; + } + listDdj() + +} + +/** + * 处理堆垛机数据获取成功 + * @param {Object} response 服务器响应数据 + */ +function handleStackerDataSuccess(response) { + + if (response.data.length==0 || configEditMode != 2) { + return; + } + + const stackers = document.querySelectorAll('.stacker'); + + stackers.forEach(stacker => { + const stackerId = stacker.getAttribute('id'); + const deviceData = response.data.find(item => item.id === stackerId); + + if (deviceData) { + updateStackerDisplay(stacker, deviceData); + } + }); +} + +/** + * 更新单个堆垛机的显示状态 + * @param {HTMLElement} stacker 堆垛机DOM元素 + * @param {Object} data 设备数据 + */ +function updateStackerDisplay(stacker, data) { + const centerBlock = stacker.querySelector('.stacker-center'); + const executionPhase = data.zxjd; + + // 根据执行阶段更新堆垛机状态 + switch(executionPhase) { + case '1': // 移动到原位置,隐藏货物 + updateStackerPosition(stacker, data.ywz, false); + break; + case '2': // 移动到原位置,显示货物 + updateStackerPosition(stacker, data.ywz, true); + break; + case '3': // 移动到目标位置,显示货物 + updateStackerPosition(stacker, data.mbwz, true); + break; + case '4': + case '5': // 移动到目标位置,隐藏货物 + updateStackerPosition(stacker, data.mbwz, false); + break; + } + + // 处理堆垛机报警状态(sbzt > 1000 时显示红光) + updateStackerAlarmStatus(stacker, data.sbzt); +} + +/** + * 更新堆垛机报警状态 + * @param {HTMLElement} stacker 堆垛机元素 + * @param {string|number} sbzt 设备状态值 + */ +function updateStackerAlarmStatus(stacker, sbzt) { + const statusValue = parseInt(sbzt); + + if (statusValue > 1000) { + // 添加报警样式 + stacker.classList.add('breathing-border'); + console.log(`堆垛机 ${stacker.getAttribute('id')} 报警,状态值: ${statusValue}`); + } else { + // 移除报警样式 + stacker.classList.remove('breathing-border'); + } +} + +/** + * 更新堆垛机位置和货物显示 + * @param {HTMLElement} stacker 堆垛机元素 + * @param {string} position 目标位置 + * @param {boolean} showCargo 是否显示货物 + */ +function updateStackerPosition(stacker, position, showCargo) { + const centerBlock = stacker.querySelector('.stacker-center'); + + // 更新货物显示状态 + if (centerBlock) { + centerBlock.style.display = showCargo ? 'block' : 'none'; + } + + // 计算并移动到目标位置 + const targetPosition = parseInt(position); + if (!isNaN(targetPosition)) { + // 获取配置信息以确定小方块总数 + const boxesPerRow = window.config ? window.config.boxesPerRow : 60; + + if (targetPosition === 0) { + // 位置为0时,移动到第一个小方块左侧 + moveStackerToColumn(stacker, -1); + } else if (targetPosition === boxesPerRow + 1) { + // 位置为小方块数量+1时,移动到最后一个小方块右侧 + moveStackerToColumn(stacker, boxesPerRow); + } else if (targetPosition > 0) { + // 正常位置处理 + moveStackerToColumn(stacker, targetPosition - 1); + } + } +} + +/** + * 移动堆垛机到指定列 + * @param {HTMLElement} stacker 堆垛机元素 + * @param {number} columnIndex 目标列索引 + */ +function moveStackerToColumn(stacker, columnIndex) { + // 获取堆垛机的ID和对应的货架ID + const stackerId = stacker.getAttribute('id'); + const stackerConfig = window.config.stackers.find(s => s.id === stackerId); + if (!stackerConfig || !stackerConfig.shelfId) { + console.warn(`堆垛机 ${stackerId} 未找到对应的货架ID`); + return; + } + + // 获取对应行的配置 + const rowConfig = window.config.boxRows.find(row => row.id === stackerConfig.shelfId); + if (!rowConfig) { + console.warn(`未找到货架ID ${stackerConfig.shelfId} 对应的方块行配置`); + return; + } + + // 获取该行的方块数量 + const boxesPerRow = rowConfig.boxCount; + let finalX; + + if (columnIndex === -1) { + // 位置为0时,移动到第一个小方块左侧一个方块距离 + const firstBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="0"]`); + if (!firstBox) { + console.warn(`未找到货架 ${stackerConfig.shelfId} 的第一个方块`); + return; + } + + const firstBoxX = parseInt(firstBox.style.left); + const boxSize = parseInt(firstBox.style.width); + const stackerWidth = parseInt(stacker.style.width); + const centerOffset = (stackerWidth - boxSize) / 2; + finalX = firstBoxX - boxSize - centerOffset; // 向左偏移一个方块距离 + + } else if (columnIndex === boxesPerRow) { + // 位置为小方块数量+1时,移动到最后一个小方块右侧一个方块距离 + const lastBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="${boxesPerRow - 1}"]`); + if (!lastBox) { + console.warn(`未找到货架 ${stackerConfig.shelfId} 的最后一个方块`); + return; + } + + const lastBoxX = parseInt(lastBox.style.left); + const boxSize = parseInt(lastBox.style.width); + const stackerWidth = parseInt(stacker.style.width); + const centerOffset = (stackerWidth - boxSize) / 2; + finalX = lastBoxX + boxSize - centerOffset; // 向右偏移一个方块距离 + + } else { + // 正常位置处理 + const targetBox = document.querySelector(`.small-box[data-row="${stackerConfig.shelfId}"][data-column="${columnIndex}"]`); + if (!targetBox) { + console.warn(`未找到货架 ${stackerConfig.shelfId} 的第 ${columnIndex} 个方块`); + return; + } + + const targetX = parseInt(targetBox.style.left); + const stackerWidth = parseInt(stacker.style.width); + const boxSize = parseInt(targetBox.style.width); + const centerOffset = (stackerWidth - boxSize) / 2; + finalX = targetX - centerOffset; + } + + // 添加平滑移动动画 + stacker.style.transition = 'left 2s ease-in-out'; + stacker.style.left = finalX + 'px'; +} + +// ==================== RGV状态管理 ==================== + +/** + * 更新RGV状态 + */ +function updateRgvStatus() { + // 检查是否允许访问后台数据 + if (!canAccessBackendData()) { + return; + } + + $.ajax({ + url: API_URLS.rgv, + type: 'GET', + dataType: 'json', + success: handleRgvDataSuccess, + error: (xhr, status, error) => console.error('RGV数据获取失败:', error) + }); +} + +/** + * 处理RGV数据获取成功 + * @param {Object} response 服务器响应数据 + */ +function handleRgvDataSuccess(response) { + if (!isValidResponse(response) || configEditMode !== 2) { + return; + } + + const rgvs = document.querySelectorAll('.rgv'); + + rgvs.forEach(rgv => { + const rgvId = rgv.getAttribute('id'); + const deviceData = response.data.find(item => item.id === rgvId); + + if (deviceData) { + updateRgvDisplay(rgv, deviceData); + } + }); +} + +/** + * 更新RGV显示状态 + * @param {HTMLElement} rgv RGV元素 + * @param {Object} data RGV数据 + */ +function updateRgvDisplay(rgv, data) { + const centerBlock = rgv.querySelector('.rgv-center'); + const executionPhase = data.zxjd; + + // 根据执行阶段更新RGV状态 + switch(executionPhase) { + case '1': // 移动到原位置,隐藏货物 + updateRgvPosition(rgv, data.ywz, false); + break; + case '2': // 移动到原位置,显示货物 + updateRgvPosition(rgv, data.ywz, true); + break; + case '3': // 移动到目标位置,显示货物 + updateRgvPosition(rgv, data.mbwz, true); + break; + case '4': + case '5': // 移动到目标位置,隐藏货物 + updateRgvPosition(rgv, data.mbwz, false); + break; + } + + // 处理RGV报警状态(sbzt > 1000 时显示红光) + updateRgvAlarmStatus(rgv, data.sbzt); +} + +/** + * 更新RGV报警状态 + * @param {HTMLElement} rgv RGV元素 + * @param {string|number} sbzt 设备状态值 + */ +function updateRgvAlarmStatus(rgv, sbzt) { + const statusValue = parseInt(sbzt); + + if (statusValue > 1000) { + // 添加报警样式 + rgv.classList.add('breathing-border'); + console.log(`RGV ${rgv.getAttribute('id')} 报警,状态值: ${statusValue}`); + } else { + // 移除报警样式 + rgv.classList.remove('breathing-border'); + } +} + +/** + * 更新RGV位置和货物显示 + * @param {HTMLElement} rgv RGV元素 + * @param {string} conveyorId 目标输送机ID + * @param {boolean} showCargo 是否显示货物 + */ +function updateRgvPosition(rgv, conveyorId, showCargo) { + const centerBlock = rgv.querySelector('.rgv-center'); + + // 更新货物显示状态 + if (centerBlock) { + centerBlock.style.display = showCargo ? 'block' : 'none'; + } + + // 移动到目标输送机位置 + if (conveyorId) { + moveRgvToConveyor(rgv, conveyorId); + } +} + +/** + * 移动RGV到指定输送机位置 + * @param {HTMLElement} rgv RGV元素 + * @param {string} conveyorId 目标输送机ID + */ +function moveRgvToConveyor(rgv, conveyorId) { + const targetConveyor = document.getElementById(conveyorId); + if (!targetConveyor) { + console.warn(`未找到输送机: ${conveyorId}`); + return; + } + + // 获取输送机的位置和尺寸 + const conveyorX = parseInt(targetConveyor.style.left); + const conveyorY = parseInt(targetConveyor.style.top); + const conveyorWidth = parseInt(targetConveyor.style.width); + const conveyorHeight = parseInt(targetConveyor.style.height); + + // 获取RGV的尺寸 + const rgvWidth = parseInt(rgv.style.width); + const rgvHeight = parseInt(rgv.style.height); + + // 计算RGV的目标位置(只调整y轴与输送机对齐,x轴保持不变) + const currentX = parseInt(rgv.style.left); // 保持当前x坐标 + const targetY = conveyorY + (conveyorHeight - rgvHeight) / 2; + + // 添加平滑移动动画(只动y轴) + rgv.style.transition = 'top 2s ease-in-out'; + rgv.style.top = targetY + 'px'; + + console.log(`RGV ${rgv.getAttribute('id')} 移动到输送机 ${conveyorId} Y轴位置: ${targetY}px (X轴保持: ${currentX}px)`); +} + +// 注意:此处的重复代码已被上面的优化函数替代 +// 旧的堆垛机处理逻辑已移至 updateStackerDisplay 等函数中 + +// 定时器设置已移至 startTimers 函数中 + +// ==================== 设备信息弹框功能 ==================== + +/** + * 显示设备信息弹框 + * @param {string} deviceType 设备类型 + * @param {string} deviceId 设备ID + * @param {Object} deviceConfig 设备配置信息 + */ +function showDeviceInfo(deviceType, deviceId, deviceConfig) { + const box = document.getElementById('box'); + const detailsDiv = document.getElementById('device-details'); + + if (!box || !detailsDiv) { + console.error('设备信息弹框元素未找到'); + return; + } + + // 获取设备实时数据 + getDeviceRealTimeData(deviceType, deviceId).then(realTimeData => { + // 构建设备信息HTML + const deviceInfo = buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, realTimeData); + detailsDiv.innerHTML = deviceInfo; + + // 显示弹框 + box.style.display = 'block'; + }).catch(error => { + console.error('获取设备实时数据失败:', error); + // 即使获取实时数据失败,也显示基本配置信息 + const deviceInfo = buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, null); + detailsDiv.innerHTML = deviceInfo; + box.style.display = 'block'; + }); +} + +/** + * 获取设备实时数据 + * @param {string} deviceType 设备类型 + * @param {string} deviceId 设备ID + * @returns {Promise} 设备实时数据 + */ +function getDeviceRealTimeData(deviceType, deviceId) { + return new Promise((resolve, reject) => { + + if (deviceType === 'stacker') { + // 获取堆垛机实时数据 - 使用配置的URL + request({ + url: '/dev-api/dynamicMonitoring/list/ddjList', + method: 'get' + }).then(response => { + if (response && response.data) { + const deviceData = response.data.find(item => item.id === deviceId); + resolve(deviceData || null); + } else { + resolve(null); + } + }).catch(error => { + console.error('获取堆垛机数据失败:', error); + reject('获取堆垛机数据失败'); + }); + } else if (deviceType === 'conveyor' || deviceType === 'rollerConveyor') { + // 获取输送机实时数据 - 使用配置的URL + request({ + url: '/dev-api/dynamicMonitoring/list/ssjList', + method: 'get' + }).then(response => { + if (response && response.data) { + const deviceData = response.data.find(item => item.id === deviceId); + resolve(deviceData || null); + } else { + resolve(null); + } + }).catch(error => { + console.error('获取输送机数据失败:', error); + reject('获取输送机数据失败'); + }); + } else if (deviceType === 'rgv') { + // 获取RGV实时数据 - 使用配置的URL + $.ajax({ + url: API_URLS.rgv, + type: 'GET', + dataType: 'json', + success: function(response) { + if (response && response.data) { + const deviceData = response.data.find(item => item.id === deviceId); + resolve(deviceData || null); + } else { + resolve(null); + } + }, + error: function(xhr, status, error) { + console.error('获取RGV数据失败:', error); + reject('获取RGV数据失败'); + } + }); + } else { + // 其他设备类型暂无实时数据 + resolve(null); + } + }); +} + + + +function ssjSbzt(a){ + if (a==0){ + return"未知" + }else if(a==1){ + return "手动" + }else if(a==2){ + return "自动" + }else if (a==3){ + return "运行" + }else if (a==4){ + return "输送超时" + }else if(a==5){ + return "断路器跳闸" + }else if (a==6){ + return "变频器报警" + }else if (a==7){ + return "开关任务异常" + }else if (a==8){ + return "急停" + }else if (a==9){ + return "超界" + }else if(a==10){ + return "外型异常" + }else if(a==11){ + return "称重异常" + }else if(a==12){ + return "扫码异常" + }else { + return a; + } +} +function ddjSbzt(a){ + if (a<1000){ + return "正常" + }else if(a==1031){ +return "急停按下" + }else if (a==1032){ + return "货叉不在中位" + }else if (a==1033){ + return "运行变频器报警" + }else if (a==1034){ + return "升降变频器报警" + }else if (a==1035){ + return "货叉变频器报警" + }else if (a==1037){ + return "运行限位故障" + }else if(a==1038){ + return "升降限位故障" + }else if (a==1039){ + return "运行不能纠偏" + }else if (a==1040){ + return "升降不能纠偏" + }else if (a==1041){ + return "水平测距出错" + }else if (a==1042){ + return "垂直BPS出错" + }else if (a==1043){ + return "超速保护" + }else if (a==1044){ + return "前边超宽" + }else if (a==1045){ + return "后边超宽" + }else if (a==1046){ + return "左边歪斜" + }else if (a==1047){ + return "右边歪斜" + }else if (a==1048){ + return "超高1" + }else if (a==1049){ + return "超高2" + }else if (a==1050){ + return "放货完毕货叉有货" + }else if (a==1051){ + return "取货时货叉有货" + }else if (a==1052){ + return "放货目标有货" + }else if (a==1053){ + return "取货完毕无货" + }else if (a==1054){ + return "放货时货叉无货" + }else if (a==1055){ + return "货叉动作超时" + }else if (a==1056){ + return "货叉不能纠偏" + }else if (a==1057){ + return "微升降超时" + }else if (a==1058){ + return "松绳过载" + }else if (a==1059){ + return "设定超限" + }else if (a==1062){ + return "入库输送机任务号不符" + }else if (a==1063){ + return "安全门打开" + }else if (a==1064){ + return "与安全门通讯中断" + }else { + return a + } +} +/** + * 构建设备信息HTML + * @param {string} deviceType 设备类型 + * @param {string} deviceId 设备ID + * @param {Object} deviceConfig 设备配置信息 + * @param {Object} realTimeData 实时数据 + * @returns {string} HTML字符串 + */ +function buildDeviceInfoHTML(deviceType, deviceId, deviceConfig, realTimeData) { + const deviceTypeNames = { + 'conveyor': '链条输送机', + 'rollerConveyor': '滚筒输送机', + 'stacker': '堆垛机', + 'rgv': 'RGV', + 'track': '堆垛机轨道' + }; + + const typeName = deviceTypeNames[deviceType] || '未知设备'; + + // 使用后台数据中的ID,如果没有实时数据则使用配置ID + const displayId = realTimeData ? realTimeData.id : deviceId; + + // 设备类型图标 + const deviceIcons = { + 'conveyor': '', + 'rollerConveyor': '', + 'stacker': '', + 'rgv': '' }; + const deviceIcon = deviceIcons[deviceType] || '⚙️'; + + let html = ` +
+ +
+
${deviceIcon}
+

${typeName}

+
ID: ${displayId}
+
+ `; + + if (realTimeData) { + html += ` + +
+ `; + + if (deviceType === 'stacker') { + // 堆垛机字段:基于ddj.json + const statusValue = parseInt(realTimeData.sbzt); + const statusColor = statusValue > 1000 ? '#ff4444' : '#00aa00'; + const statusText = ddjSbzt(statusValue); + const statusBgColor = statusValue > 1000 ? 'rgba(255,68,68,0.1)' : 'rgba(0,170,0,0.1)'; + + // 执行阶段文字描述 + const executionPhases = { + '1': '取货行驶', + '2': '取货', + '3': '放货行驶', + '4': '放货', + '5': '完成' + }; + const phaseText = executionPhases[realTimeData.zxjd] || `未知阶段(${realTimeData.zxjd})`; + + html += ` +
+
设备信息
+
名称: ${realTimeData.name}
+
执行阶段: ${phaseText}
+
任务号: ${realTimeData.rwh}
+
+ +
+
运行状态
+
+ 状态: + ${statusText} +
+
状态值: ${realTimeData.sbzt}
+
+ +
+
位置信息
+
+
+
${realTimeData.yp}排${realTimeData.ywz}列${realTimeData.yc}层
+
原位置
+
+
→
+
+
${realTimeData.mbp}排${realTimeData.mbwz}列${realTimeData.mbc}层
+
目标位置
+
+
+
+ `; + } else if (deviceType === 'conveyor' || deviceType === 'rollerConveyor') { + // 输送机字段:基于Lssj.json + const statusValue = parseInt(realTimeData.sbzt); + const statusColor = statusValue > 3 ? '#ff4444' : '#00aa00'; + const statusText = ssjSbzt(statusValue) + const statusBgColor = statusValue > 3 ? 'rgba(255,68,68,0.1)' : 'rgba(0,170,0,0.1)'; + const hasCargoText = realTimeData.dw === '1' ? '有货物' : '无货物'; + const kxText = realTimeData.kx === '1' ? '空闲' : '不空闲'; + const cargoColor = realTimeData.dw === '1' ? '#FF9800' : '#9E9E9E'; + const switchColor = realTimeData.kx === '1' ? '#4CAF50' : '#F44336'; + + html += ` +
+
设备信息
+
名称: ${realTimeData.name}
+
任务号: ${realTimeData.rwh}
+
托盘编码: ${realTimeData.trayCode}
+
+ +
+
运行状态
+
+ 状态: + ${statusText} +
+
状态值: ${realTimeData.sbzt}
+
+ +
+
开关状态
+
+
${kxText}
+
+
+ +
+
货物状态
+
+
${hasCargoText}
+
+
+ `; + } else if (deviceType === 'rgv') { + // RGV字段:基于rgv.json + const statusValue = parseInt(realTimeData.sbzt); + const statusColor = statusValue > 1000 ? '#ff4444' : '#00aa00'; + const statusText = statusValue > 1000 ? '报警' : '正常'; + const statusBgColor = statusValue > 1000 ? 'rgba(255,68,68,0.1)' : 'rgba(0,170,0,0.1)'; + // 执行阶段文字描述 + const executionPhases = { + '1': '取货行驶', + '2': '取货', + '3': '放货行驶', + '4': '放货', + '5': '完成' + }; + const phaseTextRgv = executionPhases[realTimeData.zxjd] || `未知阶段(${realTimeData.zxjd})`; + + // 执行阶段描述 + const phaseDescriptions = { + '1': '移动到原位置(无货物)', + '2': '移动到原位置(有货物)', + '3': '移动到目标位置(有货物)', + '4': '移动到目标位置(无货物)', + '5': '完成(无货物)' + }; + const phaseText = phaseDescriptions[realTimeData.zxjd] || '未知阶段'; + const phaseColor = realTimeData.zxjd === '1' || realTimeData.zxjd === '4' || realTimeData.zxjd === '5' ? '#9E9E9E' : '#FF9800'; + + html += ` +
+
设备信息
+
名称: ${realTimeData.name}
+
任务号: ${realTimeData.rwh}
+
执行阶段: ${phaseTextRgv}
+
+ +
+
运行状态
+
+ 状态: + ${statusText} +
+
状态值: ${realTimeData.sbzt}
+
+ +
+
执行阶段
+
+
${phaseText}
+
+
+ +
+
位置信息
+
+
+
${realTimeData.ywz}
+
原位置(输送机ID)
+
+
+
${realTimeData.mbwz}
+
目标位置(输送机ID)
+
+
+
+ `; + } + + html += `
`; + } else { + html += ` +
+
📊
+
暂无设备数据
+
请检查设备连接状态
+
+ `; + } + + html += ` + +
+ +
+
+ `; + + return html; +} + +/** + * 关闭设备信息弹框 + */ +function closeBox() { + const box = document.getElementById('box'); + if (box) { + box.style.display = 'none'; + } +} + +/** + * 初始化弹框拖动功能 + */ +function initBoxDrag() { + const box = document.getElementById('box'); + const header = document.getElementById('box-header'); + + if (!box || !header) return; + + let isDragging = false; + let startX = 0; + let startY = 0; + let initialLeft = 0; + let initialTop = 0; + + // 鼠标按下事件 + header.addEventListener('mousedown', function(e) { + e.preventDefault(); + isDragging = true; + + // 记录初始位置 + startX = e.clientX; + startY = e.clientY; + + // 获取弹框当前位置 + const rect = box.getBoundingClientRect(); + initialLeft = rect.left; + initialTop = rect.top; + + // 改变光标样式 + document.body.style.cursor = 'move'; + header.style.cursor = 'move'; + + // 添加全局事件监听 + document.addEventListener('mousemove', handleMouseMove); + document.addEventListener('mouseup', handleMouseUp); + }); + + function handleMouseMove(e) { + if (!isDragging) return; + + e.preventDefault(); + + // 计算移动距离 + const deltaX = e.clientX - startX; + const deltaY = e.clientY - startY; + + // 计算新位置 + let newLeft = initialLeft + deltaX; + let newTop = initialTop + deltaY; + + // 边界检查 + const boxWidth = box.offsetWidth; + const boxHeight = box.offsetHeight; + const windowWidth = window.innerWidth; + const windowHeight = window.innerHeight; + + // 限制在窗口范围内 + newLeft = Math.max(0, Math.min(newLeft, windowWidth - boxWidth)); + newTop = Math.max(0, Math.min(newTop, windowHeight - boxHeight)); + + // 应用新位置 + box.style.left = newLeft + 'px'; + box.style.top = newTop + 'px'; + box.style.transform = 'none'; // 移除居中变换 + } + + function handleMouseUp(e) { + if (!isDragging) return; + + isDragging = false; + + // 恢复光标样式 + document.body.style.cursor = ''; + header.style.cursor = 'move'; + + // 移除全局事件监听 + document.removeEventListener('mousemove', handleMouseMove); + document.removeEventListener('mouseup', handleMouseUp); + } +} + +// 将函数暴露到全局作用域 +window.showDeviceInfo = showDeviceInfo; +window.closeBox = closeBox; +window.initBoxDrag = initBoxDrag; + +// 页面加载完成后初始化拖动功能 +$(document).ready(function() { + initBoxDrag(); + // 初始化报警系统 + initAlarmSystem(); +}); + +// ==================== 报警信息系统 ==================== + +/** + * 初始化报警系统 + * 系统启动时调用,开始报警监控 + */ +function initAlarmSystem() { + // 先加载报警设置,检查是否有效 + loadAlarmSettings().then(() => { + // 只有在报警设置有效时才开始监控 + const container = document.getElementById('alarm-container'); + if (container && container.style.display !== 'none') { + updateAlarmStatus(); + } + }); +} + +/** + * 更新报警状态 + * 从后台获取最新的报警数据并更新显示 + */ +function updateAlarmStatus() { + // 检查是否允许访问后台数据 + if (!canAccessBackendData()) { + return; + } + + listAlarm() +} + +/** + * 处理报警数据获取成功的回调 + * @param {Object} response 服务器响应数据 + */ +function handleAlarmDataSuccess(response) { + // 验证响应数据有效性 + if (!isValidAlarmResponse(response)) { + showNoAlarmState(); + return; + } + + const alarms = response.alarm; + + // 根据报警数量决定显示方式 + if (alarms.length === 0) { + showNoAlarmState(); + } else { + showAlarmContainer(alarms); + } +} + +/** + * 处理报警数据获取失败的回调 + * @param {Object} xhr XMLHttpRequest对象 + * @param {string} status 状态文本 + * @param {string} error 错误信息 + */ +function handleAlarmDataError(xhr, status, error) { + console.error('报警数据获取失败:', { + status: status, + error: error, + responseText: xhr.responseText + }); + + // 网络错误时显示无报警状态,避免界面异常 + showNoAlarmState(); +} + +// ==================== 报警系统模块 ==================== + +// 报警系统配置(可通过设置页面修改) +let ALARM_CONFIG = { + // 样式配置 + STYLES: { + NORMAL: { + background: 'linear-gradient(135deg, #999999, #777777)', + boxShadow: '0 4px 15px rgba(153, 153, 153, 0.3)' + }, + ALARM: { + background: 'linear-gradient(135deg, #ff6b6b, #ee5a24)', + boxShadow: '0 4px 15px rgba(255, 107, 107, 0.3)' + } + }, + // 时间配置 + TIMING: { + NO_ALARM_CHECK_INTERVAL: 3000, // 无报警时检查间隔(毫秒) + SHORT_CONTENT_CHECK_INTERVAL: 3000, // 短内容检查间隔(毫秒) + SCROLL_SPEED: 50 // 滚动速度(像素/秒) + }, + // 文本配置 + TEXT: { + NO_ALARM: '暂无报警', + SEPARATOR: '    ' + }, + // 报警框设置 + CONTAINER: { + width: 400, + height: 60, + x: 20, + y: 20, + position: 'bottom-center' + } +}; + +// API接口配置(从config.json中的apiSettings读取) +let API_URLS = { + stacker: 'json/ddj.json', + conveyor: 'json/Lssj.json', + rgv: 'json/rgv.json', + alarm: 'json/alarm.json', + config: 'json/config.json' +}; +// 报警系统全局变量 +let alarmScrollTimer = null; // 滚动动画定时器 +let currentAlarmData = []; // 当前报警数据缓存 +let isScrolling = false; // 滚动状态标记 + +/** + * 显示报警容器并更新内容 + * @param {Array} alarms 报警数据数组 + */ +function showAlarmContainer(alarms) { + const elements = getAlarmElements(); + if (!elements) return; + + const { container, countElement, textElement } = elements; + + if (alarms.length === 0) { + setNoAlarmState(container, countElement, textElement); + return; + } + + setAlarmState(container, countElement, textElement, alarms); +} + +/** + * 显示无报警状态(灰色状态) + */ +function showNoAlarmState() { + const elements = getAlarmElements(); + if (!elements) return; + + const { container, countElement, textElement } = elements; + setNoAlarmState(container, countElement, textElement); +} + +/** + * 隐藏报警容器(仅在配置无效时使用) + */ +function hideAlarmContainer() { + const container = document.getElementById('alarm-container'); + if (container) { + // 完全隐藏报警框 + container.style.display = 'none'; + + // 停止所有报警相关的定时器和动画 + stopAlarmScroll(); + + // 清除报警更新定时器 + if (alarmUpdateTimer) { + clearTimeout(alarmUpdateTimer); + alarmUpdateTimer = null; + } + + console.log('报警框已隐藏'); + } +} + +/** + * 获取报警相关DOM元素 + * @returns {Object|null} 包含DOM元素的对象或null + */ +function getAlarmElements() { + const container = document.getElementById('alarm-container'); + const countElement = document.getElementById('alarm-count'); + const textElement = document.getElementById('alarm-text'); + + if (!container || !countElement || !textElement) { + console.error('报警显示元素未找到'); + return null; + } + + return { container, countElement, textElement }; +} + +/** + * 设置无报警状态的样式和内容 + * @param {HTMLElement} container 容器元素 + * @param {HTMLElement} countElement 计数元素 + * @param {HTMLElement} textElement 文本元素 + */ +function setNoAlarmState(container, countElement, textElement) { + // 更新内容 + countElement.textContent = '0'; + textElement.textContent = ALARM_CONFIG.TEXT.NO_ALARM; + + // 设置样式 + applyContainerStyle(container, ALARM_CONFIG.STYLES.NORMAL); + + // 停止滚动 + stopAlarmScroll(); + + // 显示容器 + container.style.display = 'block'; + + // 定时重新检查 + setTimeout(() => { + updateAlarmStatus(); + }, ALARM_CONFIG.TIMING.NO_ALARM_CHECK_INTERVAL); +} + +/** + * 设置有报警状态的样式和内容 + * @param {HTMLElement} container 容器元素 + * @param {HTMLElement} countElement 计数元素 + * @param {HTMLElement} textElement 文本元素 + * @param {Array} alarms 报警数据数组 + */ +function setAlarmState(container, countElement, textElement, alarms) { + // 更新报警数量 + countElement.textContent = alarms.length; + + // 设置报警样式 + applyContainerStyle(container, ALARM_CONFIG.STYLES.ALARM); + + // 构建滚动文本 + const scrollText = buildAlarmText(alarms); + + // 检查内容是否变化 + const contentChanged = JSON.stringify(alarms) !== JSON.stringify(currentAlarmData); + + if (contentChanged || !isScrolling) { + currentAlarmData = [...alarms]; + textElement.textContent = scrollText; + startAlarmScroll(textElement); + } + + // 显示容器 + container.style.display = 'block'; +} + +/** + * 应用容器样式 + * @param {HTMLElement} container 容器元素 + * @param {Object} style 样式对象 + */ +function applyContainerStyle(container, style) { + container.style.background = style.background; + container.style.boxShadow = style.boxShadow; +} + +/** + * 构建报警文本 + * @param {Array} alarms 报警数据数组 + * @returns {string} 格式化的报警文本 + */ +function buildAlarmText(alarms) { + const alarmTexts = alarms.map(alarm => { + const errorCode = alarm.code || alarm.type || '未知'; + if(alarm.name=="堆垛机"){ + return `【${alarm.id}${alarm.name}】错误代码: ${errorCode},报警信息:${ddjSbzt(errorCode)}`; + }else if(alarm.name=="输送机"){ + return `【${alarm.id}${alarm.name}】错误代码: ${errorCode},报警信息:${ssjSbzt(errorCode)}`; + }else { + return `【${alarm.id}${alarm.name}】错误代码: ${errorCode},报警信息:未知`; + } + }); + return alarmTexts.join(ALARM_CONFIG.TEXT.SEPARATOR); +} + +/** + * 开始报警文本滚动 + * @param {HTMLElement} textElement 文本元素 + */ +function startAlarmScroll(textElement) { + // 停止当前滚动 + stopAlarmScroll(); + + // 获取滚动容器和测量尺寸 + const scrollContainer = document.getElementById('alarm-scroll'); + if (!scrollContainer) { + console.error('滚动容器未找到'); + return; + } + + const containerWidth = scrollContainer.offsetWidth; + const textWidth = textElement.scrollWidth; + + // 判断是否需要滚动 + if (textWidth <= containerWidth) { + // 短内容居中显示 + const centerPosition = (containerWidth - textWidth) / 2; + textElement.style.left = centerPosition + 'px'; + + isScrolling = false; + // 短内容定时重新检查 + setTimeout(() => { + updateAlarmStatus(); + }, ALARM_CONFIG.TIMING.SHORT_CONTENT_CHECK_INTERVAL); + return; + } + + // 计算居中起始位置,但不小于50px + const centerStartPosition = Math.max(50, (containerWidth - textWidth) / 2); + + // 设置初始居中位置 + textElement.style.left = centerStartPosition + 'px'; + + isScrolling = true; + + // 延迟2秒后开始滚动,让用户看清第一条信息 + setTimeout(() => { + if (isScrolling) { // 确保在延迟期间没有被停止 + // 计算滚动参数 - 只需要滚动到文字完全离开容器即可 + const scrollDistance = textWidth; + const scrollDuration = scrollDistance / ALARM_CONFIG.TIMING.SCROLL_SPEED; + + // 从居中位置开始滚动到文字完全离开容器 + animateScroll(textElement, centerStartPosition, -scrollDistance, scrollDuration * 1000); + } + }, 2000); // 2秒延迟 +} + +/** + * 执行滚动动画 + * @param {HTMLElement} element 文本元素 + * @param {number} startPos 开始位置(像素) + * @param {number} endPos 结束位置(像素) + * @param {number} duration 持续时间(毫秒) + */ +function animateScroll(element, startPos, endPos, duration) { + const startTime = Date.now(); + + function step() { + const elapsed = Date.now() - startTime; + const progress = Math.min(elapsed / duration, 1); + + // 线性插值计算当前位置 + const currentPos = startPos + (endPos - startPos) * progress; + element.style.left = currentPos + 'px'; + + if (progress < 1) { + // 继续动画 + alarmScrollTimer = requestAnimationFrame(step); + } else { + // 滚动完成,重新获取数据 + isScrolling = false; + updateAlarmStatus(); + } + } + + alarmScrollTimer = requestAnimationFrame(step); +} + +/** + * 停止报警滚动动画 + */ +function stopAlarmScroll() { + if (alarmScrollTimer) { + cancelAnimationFrame(alarmScrollTimer); + alarmScrollTimer = null; + } + isScrolling = false; +} + +/** + * 验证报警响应数据是否有效 + * @param {Object} response 响应数据 + * @returns {boolean} 数据是否有效 + */ +function isValidAlarmResponse(response) { + return response && + typeof response === 'object' && + Array.isArray(response.alarm); +} + +/** + * 从配置文件加载API设置 + */ +async function loadApiSettings() { + $.ajax({ + url: API_URLS.config, + type: 'GET', + dataType: 'json', + cache: false, + success(res){ + if (res.apiSettings){ + const settings = res.apiSettings; + + // 更新API URL配置 + API_URLS.stacker = settings.stackerApiUrl || 'json/ddj.json'; + API_URLS.conveyor = settings.conveyorApiUrl || 'json/Lssj.json'; + API_URLS.rgv = settings.rgvApiUrl || 'json/rgv.json'; + API_URLS.alarm = settings.alarmApiUrl || 'json/alarm.json'; + API_URLS.config = settings.configApiUrl || 'json/config.json'; + + console.log('API设置已更新:', API_URLS); + } + }, + error(error){ + console.error('加载API设置失败:', error); + } + }); + + +} + +/** + * 从配置文件加载报警设置 + */ +async function loadAlarmSettings() { + + $.ajax({ + url: API_URLS.config, + type: 'GET', + dataType: 'json', + cache: false, + success(res) { + if (res.alarmSettings) { + const settings = res.alarmSettings; + + // 检查报警设置是否有效(非空且非零) + const isValidSettings = settings.width > 0 && + settings.height > 0 && + settings.position && + settings.position.trim() !== ''; + + if (isValidSettings) { + // 更新配置 + ALARM_CONFIG.CONTAINER.width = settings.width; + ALARM_CONFIG.CONTAINER.height = settings.height; + ALARM_CONFIG.CONTAINER.x = settings.x || 20; + ALARM_CONFIG.CONTAINER.y = settings.y || 20; + ALARM_CONFIG.CONTAINER.position = settings.position; + ALARM_CONFIG.TIMING.SCROLL_SPEED = settings.scrollSpeed || 50; + + // 应用设置到报警框 + applyAlarmContainerSettings(); + + console.log('报警设置已加载并应用'); + } else { + // 报警设置无效,隐藏报警框 + hideAlarmContainer(); + console.log('报警设置无效,已隐藏报警框'); + } + } else { + // 没有报警设置,隐藏报警框 + hideAlarmContainer(); + console.log('未找到报警设置,已隐藏报警框'); + } + }, + error(error){ + console.error('加载报警设置失败:', error); + // 加载失败时也隐藏报警框 + hideAlarmContainer(); + } + }) +} + +/** + * 应用报警容器设置 + */ +function applyAlarmContainerSettings() { + const container = document.getElementById('alarm-container'); + if (!container) return; + + const settings = ALARM_CONFIG.CONTAINER; + + // 设置尺寸 + container.style.width = settings.width + 'px'; + container.style.height = settings.height + 'px'; + + // 设置位置 + switch (settings.position) { + case 'top-left': + container.style.top = settings.y + 'px'; + container.style.left = settings.x + 'px'; + container.style.bottom = 'auto'; + container.style.right = 'auto'; + container.style.transform = 'none'; + break; + case 'top-right': + container.style.top = settings.y + 'px'; + container.style.right = settings.x + 'px'; + container.style.bottom = 'auto'; + container.style.left = 'auto'; + container.style.transform = 'none'; + break; + case 'bottom-left': + container.style.bottom = settings.y + 'px'; + container.style.left = settings.x + 'px'; + container.style.top = 'auto'; + container.style.right = 'auto'; + container.style.transform = 'none'; + break; + case 'bottom-right': + container.style.bottom = settings.y + 'px'; + container.style.right = settings.x + 'px'; + container.style.top = 'auto'; + container.style.left = 'auto'; + container.style.transform = 'none'; + break; + case 'bottom-center': + default: + container.style.bottom = settings.y + 'px'; + container.style.left = '50%'; + container.style.top = 'auto'; + container.style.right = 'auto'; + container.style.transform = 'translateX(-50%)'; + break; + case 'custom': + container.style.left = settings.x + 'px'; + container.style.top = settings.y + 'px'; + container.style.bottom = 'auto'; + container.style.right = 'auto'; + container.style.transform = 'none'; + break; + } +} + +/** + * 监听配置设置更新 + */ +function setupConfigSettingsListener() { + if (window.BroadcastChannel) { + const bc = new BroadcastChannel('config_update_channel'); + bc.onmessage = (event) => { + if (event.data.type === 'alarm_settings_updated') { + console.log('收到报警设置更新通知'); + loadAlarmSettings(); + } else if (event.data.type === 'api_settings_updated') { + console.log('收到API设置更新通知'); + loadApiSettings(); + } + }; + } +} + +// 在页面加载时初始化配置设置 +$(document).ready(function() { + loadApiSettings(); + loadAlarmSettings(); + setupConfigSettingsListener(); +}); diff --git a/ruoyi-ui/public/monitor/js/jquery-3.1.1.min.js b/ruoyi-ui/public/monitor/js/jquery-3.1.1.min.js new file mode 100644 index 0000000..4c5be4c --- /dev/null +++ b/ruoyi-ui/public/monitor/js/jquery-3.1.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v3.1.1 | (c) jQuery Foundation | jquery.org/license */ +!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.1.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext,B=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,C=/^.[^:#\[\.,]*$/;function D(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):C.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(D(this,a||[],!1))},not:function(a){return this.pushStack(D(this,a||[],!0))},is:function(a){return!!D(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var E,F=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,G=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||E,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:F.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),B.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};G.prototype=r.fn,E=r(d);var H=/^(?:parents|prev(?:Until|All))/,I={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function J(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return J(a,"nextSibling")},prev:function(a){return J(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return a.contentDocument||r.merge([],a.childNodes)}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(I[a]||r.uniqueSort(e),H.test(a)&&e.reverse()),this.pushStack(e)}});var K=/[^\x20\t\r\n\f]+/g;function L(a){var b={};return r.each(a.match(K)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?L(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function M(a){return a}function N(a){throw a}function O(a,b,c){var d;try{a&&r.isFunction(d=a.promise)?d.call(a).done(b).fail(c):a&&r.isFunction(d=a.then)?d.call(a,b,c):b.call(void 0,a)}catch(a){c.call(void 0,a)}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==N&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:M,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:M)),c[2][3].add(g(0,a,r.isFunction(d)?d:N))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(O(a,g.done(h(c)).resolve,g.reject),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)O(e[c],h(c),g.reject);return g.promise()}});var P=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&P.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var Q=r.Deferred();r.fn.ready=function(a){return Q.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,holdReady:function(a){a?r.readyWait++:r.ready(!0)},ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||Q.resolveWith(d,[r]))}}),r.ready.then=Q.then;function R(){d.removeEventListener("DOMContentLoaded",R), +a.removeEventListener("load",R),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",R),a.addEventListener("load",R));var S=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)S(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){W.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=V.get(a,b),c&&(!d||r.isArray(c)?d=V.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return V.get(a,c)||V.access(a,c,{empty:r.Callbacks("once memory").add(function(){V.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,ka=/^$|\/(?:java|ecma)script/i,la={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};la.optgroup=la.option,la.tbody=la.tfoot=la.colgroup=la.caption=la.thead,la.th=la.td;function ma(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&r.nodeName(a,b)?r.merge([a],c):c}function na(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=ma(l.appendChild(f),"script"),j&&na(g),c){k=0;while(f=g[k++])ka.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var qa=d.documentElement,ra=/^key/,sa=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ta=/^([^.]*)(?:\.(.+)|)/;function ua(){return!0}function va(){return!1}function wa(){try{return d.activeElement}catch(a){}}function xa(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)xa(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=va;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(qa,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(K)||[""],j=b.length;while(j--)h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=V.hasData(a)&&V.get(a);if(q&&(i=q.events)){b=(b||"").match(K)||[""],j=b.length;while(j--)if(h=ta.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&V.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(V.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,za=/\s*$/g;function Da(a,b){return r.nodeName(a,"table")&&r.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a:a}function Ea(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Fa(a){var b=Ba.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ga(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(V.hasData(a)&&(f=V.access(a),g=V.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Aa.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ia(f,b,c,d)});if(m&&(e=pa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(ma(e,"script"),Ea),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=ma(h),f=ma(a),d=0,e=f.length;d0&&na(g,!i&&ma(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(T(c)){if(b=c[V.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[V.expando]=void 0}c[W.expando]&&(c[W.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ja(this,a,!0)},remove:function(a){return Ja(this,a)},text:function(a){return S(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.appendChild(a)}})},prepend:function(){return Ia(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Da(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ia(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(ma(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return S(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!za.test(a)&&!la[(ja.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function Ya(a,b,c,d,e){return new Ya.prototype.init(a,b,c,d,e)}r.Tween=Ya,Ya.prototype={constructor:Ya,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=Ya.propHooks[this.prop];return a&&a.get?a.get(this):Ya.propHooks._default.get(this)},run:function(a){var b,c=Ya.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Ya.propHooks._default.set(this),this}},Ya.prototype.init.prototype=Ya.prototype,Ya.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},Ya.propHooks.scrollTop=Ya.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=Ya.prototype.init,r.fx.step={};var Za,$a,_a=/^(?:toggle|show|hide)$/,ab=/queueHooks$/;function bb(){$a&&(a.requestAnimationFrame(bb),r.fx.tick())}function cb(){return a.setTimeout(function(){Za=void 0}),Za=r.now()}function db(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ba[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function eb(a,b,c){for(var d,e=(hb.tweeners[b]||[]).concat(hb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?ib:void 0)), +void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b),null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&r.nodeName(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(K);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),ib={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=jb[b]||r.find.attr;jb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=jb[g],jb[g]=e,e=null!=c(a,b,d)?g:null,jb[g]=f),e}});var kb=/^(?:input|select|textarea|button)$/i,lb=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return S(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):kb.test(a.nodeName)||lb.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function mb(a){var b=a.match(K)||[];return b.join(" ")}function nb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,nb(this)))});if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,nb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(K)||[];while(c=this[i++])if(e=nb(c),d=1===c.nodeType&&" "+mb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=mb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,nb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(K)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=nb(this),b&&V.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":V.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+mb(nb(c))+" ").indexOf(b)>-1)return!0;return!1}});var ob=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":r.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(ob,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:mb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(r.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var pb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!pb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,pb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(V.get(h,"events")||{})[b.type]&&V.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&T(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!T(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=V.access(d,b);e||d.addEventListener(a,c,!0),V.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=V.access(d,b)-1;e?V.access(d,b,e):(d.removeEventListener(a,c,!0),V.remove(d,b))}}});var qb=a.location,rb=r.now(),sb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var tb=/\[\]$/,ub=/\r?\n/g,vb=/^(?:submit|button|image|reset|file)$/i,wb=/^(?:input|select|textarea|keygen)/i;function xb(a,b,c,d){var e;if(r.isArray(b))r.each(b,function(b,e){c||tb.test(a)?d(a,e):xb(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)xb(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(r.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)xb(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&wb.test(this.nodeName)&&!vb.test(a)&&(this.checked||!ia.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:r.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(ub,"\r\n")}}):{name:b.name,value:c.replace(ub,"\r\n")}}).get()}});var yb=/%20/g,zb=/#.*$/,Ab=/([?&])_=[^&]*/,Bb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Cb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Db=/^(?:GET|HEAD)$/,Eb=/^\/\//,Fb={},Gb={},Hb="*/".concat("*"),Ib=d.createElement("a");Ib.href=qb.href;function Jb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(K)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Kb(a,b,c,d){var e={},f=a===Gb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Lb(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Mb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Nb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:qb.href,type:"GET",isLocal:Cb.test(qb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Hb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Lb(Lb(a,r.ajaxSettings),b):Lb(r.ajaxSettings,a)},ajaxPrefilter:Jb(Fb),ajaxTransport:Jb(Gb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Bb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||qb.href)+"").replace(Eb,qb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(K)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Ib.protocol+"//"+Ib.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Kb(Fb,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Db.test(o.type),f=o.url.replace(zb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(yb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(sb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Ab,"$1"),n=(sb.test(f)?"&":"?")+"_="+rb++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Hb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Kb(Gb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Mb(o,y,d)),v=Nb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Ob={0:200,1223:204},Pb=r.ajaxSettings.xhr();o.cors=!!Pb&&"withCredentials"in Pb,o.ajax=Pb=!!Pb,r.ajaxTransport(function(b){var c,d;if(o.cors||Pb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Ob[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" + diff --git a/ruoyi-ui/src/api/agv/queue.js b/ruoyi-ui/src/api/agv/queue.js new file mode 100644 index 0000000..1bda346 --- /dev/null +++ b/ruoyi-ui/src/api/agv/queue.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询AGV任务队列列表 +export function listQueue(query) { + return request({ + url: '/agv/queue/list', + method: 'get', + params: query + }) +} + +// 查询AGV任务队列详细 +export function getQueue(reqCode) { + return request({ + url: '/agv/queue/' + reqCode, + method: 'get' + }) +} + +// 新增AGV任务队列 +export function addQueue(data) { + return request({ + url: '/agv/queue', + method: 'post', + data: data + }) +} + +// 修改AGV任务队列 +export function updateQueue(data) { + return request({ + url: '/agv/queue', + method: 'put', + data: data + }) +} + +// 删除AGV任务队列 +export function delQueue(reqCode) { + return request({ + url: '/agv/queue/' + reqCode, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/agv/queueFinish.js b/ruoyi-ui/src/api/agv/queueFinish.js new file mode 100644 index 0000000..0e8e6fc --- /dev/null +++ b/ruoyi-ui/src/api/agv/queueFinish.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询agv历史队列列表 +export function listQueueFinish(query) { + return request({ + url: '/agv/queueFinish/list', + method: 'get', + params: query + }) +} + +// 查询agv历史队列详细 +export function getQueueFinish(reqCode) { + return request({ + url: '/agv/queueFinish/' + reqCode, + method: 'get' + }) +} + +// 新增agv历史队列 +export function addQueueFinish(data) { + return request({ + url: '/agv/queueFinish', + method: 'post', + data: data + }) +} + +// 修改agv历史队列 +export function updateQueueFinish(data) { + return request({ + url: '/agv/queueFinish', + method: 'put', + data: data + }) +} + +// 删除agv历史队列 +export function delQueueFinish(reqCode) { + return request({ + url: '/agv/queueFinish/' + reqCode, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/bas/fluent.js b/ruoyi-ui/src/api/bas/fluent.js new file mode 100644 index 0000000..7b1697e --- /dev/null +++ b/ruoyi-ui/src/api/bas/fluent.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询流利架位置管理列表 +export function listFluent(query) { + return request({ + url: '/bas/fluent/list', + method: 'get', + params: query + }) +} + +// 查询流利架位置管理详细 +export function getFluent(id) { + return request({ + url: '/bas/fluent/' + id, + method: 'get' + }) +} + +// 新增流利架位置管理 +export function addFluent(data) { + return request({ + url: '/bas/fluent', + method: 'post', + data: data + }) +} + +// 修改流利架位置管理 +export function updateFluent(data) { + return request({ + url: '/bas/fluent', + method: 'put', + data: data + }) +} + +// 删除流利架位置管理 +export function delFluent(id) { + return request({ + url: '/bas/fluent/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/bas/port.js b/ruoyi-ui/src/api/bas/port.js new file mode 100644 index 0000000..9e22fde --- /dev/null +++ b/ruoyi-ui/src/api/bas/port.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询点位管理列表 +export function listPort(query) { + return request({ + url: '/bas/port/list', + method: 'get', + params: query + }) +} + +// 查询点位管理详细 +export function getPort(id) { + return request({ + url: '/bas/port/' + id, + method: 'get' + }) +} + +// 新增点位管理 +export function addPort(data) { + return request({ + url: '/bas/port', + method: 'post', + data: data + }) +} + +// 修改点位管理 +export function updatePort(data) { + return request({ + url: '/bas/port', + method: 'put', + data: data + }) +} + +// 删除点位管理 +export function delPort(id) { + return request({ + url: '/bas/port/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/device/DeviceCar.js b/ruoyi-ui/src/api/device/DeviceCar.js new file mode 100644 index 0000000..126fd55 --- /dev/null +++ b/ruoyi-ui/src/api/device/DeviceCar.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询设备信息列表 +export function listDeviceCar(query) { + return request({ + url: '/device/DeviceCar/list', + method: 'get', + params: query + }) +} + +// 查询设备信息详细 +export function getDeviceCar(id) { + return request({ + url: '/device/DeviceCar/' + id, + method: 'get' + }) +} + +// 新增设备信息 +export function addDeviceCar(data) { + return request({ + url: '/device/DeviceCar', + method: 'post', + data: data + }) +} + +// 修改设备信息 +export function updateDeviceCar(data) { + return request({ + url: '/device/DeviceCar', + method: 'put', + data: data + }) +} + +// 删除设备信息 +export function delDeviceCar(id) { + return request({ + url: '/device/DeviceCar/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/device/api.js b/ruoyi-ui/src/api/device/api.js new file mode 100644 index 0000000..d8124c6 --- /dev/null +++ b/ruoyi-ui/src/api/device/api.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询接口记录列表 +export function listApi(query) { + return request({ + url: '/device/api/list', + method: 'get', + params: query + }) +} + +// 查询接口记录详细 +export function getApi(requestId) { + return request({ + url: '/device/api/' + requestId, + method: 'get' + }) +} + +// 新增接口记录 +export function addApi(data) { + return request({ + url: '/device/api', + method: 'post', + data: data + }) +} + +// 修改接口记录 +export function updateApi(data) { + return request({ + url: '/device/api', + method: 'put', + data: data + }) +} + +// 删除接口记录 +export function delApi(requestId) { + return request({ + url: '/device/api/' + requestId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/device/ddj.js b/ruoyi-ui/src/api/device/ddj.js new file mode 100644 index 0000000..4008a1a --- /dev/null +++ b/ruoyi-ui/src/api/device/ddj.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询堆垛机监控列表 +export function listDdj(query) { + return request({ + url: '/device/ddj/list', + method: 'get', + params: query + }) +} + +// 查询堆垛机监控详细 +export function getDdj(id) { + return request({ + url: '/device/ddj/' + id, + method: 'get' + }) +} + +// 新增堆垛机监控 +export function addDdj(data) { + return request({ + url: '/device/ddj', + method: 'post', + data: data + }) +} + +// 修改堆垛机监控 +export function updateDdj(data) { + return request({ + url: '/device/ddj', + method: 'put', + data: data + }) +} + +// 删除堆垛机监控 +export function delDdj(id) { + return request({ + url: '/device/ddj/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/device/ssj.js b/ruoyi-ui/src/api/device/ssj.js new file mode 100644 index 0000000..b1ffd11 --- /dev/null +++ b/ruoyi-ui/src/api/device/ssj.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询输送机列表 +export function listSsj(query) { + return request({ + url: '/device/ssj/list', + method: 'get', + params: query + }) +} + +// 查询输送机详细 +export function getSsj(id) { + return request({ + url: '/device/ssj/' + id, + method: 'get' + }) +} + +// 新增输送机 +export function addSsj(data) { + return request({ + url: '/device/ssj', + method: 'post', + data: data + }) +} + +// 修改输送机 +export function updateSsj(data) { + return request({ + url: '/device/ssj', + method: 'put', + data: data + }) +} + +// 删除输送机 +export function delSsj(id) { + return request({ + url: '/device/ssj/' + id, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/index.js b/ruoyi-ui/src/api/index.js new file mode 100644 index 0000000..15abe22 --- /dev/null +++ b/ruoyi-ui/src/api/index.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 获取设备统计数据 +export function getDeviceStats() { + return request({ + url: '/index/deviceStats', + method: 'get' + }) +} + +// 获取任务统计数据 +export function getTaskStats() { + return request({ + url: '/index/taskStats', + method: 'get' + }) +} + +// 获取当前任务队列 +export function getCurrentTasks() { + return request({ + url: '/index/currentTasks', + method: 'get' + }) +} + +// 获取设备列表 +export function getDeviceList() { + return request({ + url: '/index/deviceList', + method: 'get' + }) +} + +// 获取任务类型分布图表数据 +export function getTaskTypeChart() { + return request({ + url: '/index/taskTypeChart', + method: 'get' + }) +} + +// 获取设备状态分布图表数据 +export function getDeviceStatusChart() { + return request({ + url: '/index/deviceStatusChart', + method: 'get' + }) +} + +// 获取首页综合数据 +export function getIndexData() { + return request({ + url: '/index/data', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/job/mydoJob.js b/ruoyi-ui/src/api/job/mydoJob.js new file mode 100644 index 0000000..29cde34 --- /dev/null +++ b/ruoyi-ui/src/api/job/mydoJob.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 查询作业信息列表 +export function listMydoJob(query) { + return request({ + url: '/job/mydoJob/list', + method: 'get', + params: query + }) +} + +// 查询作业信息列表 +export function listMydoJobFinish(query) { + return request({ + url: '/job/mydoJob/listFinish', + method: 'get', + params: query + }) +} + +// 查询作业信息详细 +export function getMydoJob(jobId) { + return request({ + url: '/job/mydoJob/' + jobId, + method: 'get' + }) +} + +// 新增作业信息 +export function addMydoJob(data) { + return request({ + url: '/job/mydoJob', + method: 'post', + data: data + }) +} + +// 修改作业信息 +export function updateMydoJob(data) { + return request({ + url: '/job/mydoJob', + method: 'put', + data: data + }) +} + +// 删除作业信息 +export function delMydoJob(jobId) { + return request({ + url: '/job/mydoJob/' + jobId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/login.js b/ruoyi-ui/src/api/login.js new file mode 100644 index 0000000..19451f9 --- /dev/null +++ b/ruoyi-ui/src/api/login.js @@ -0,0 +1,74 @@ +import request from '@/utils/request' + +// 登录方法 +export function login(username, password, code, uuid) { + const data = { + username, + password, + code, + uuid + } + return request({ + url: '/login', + headers: { + isToken: false, + repeatSubmit: false + }, + method: 'post', + data: data + }) +} + +// 注册方法 +export function register(data) { + return request({ + url: '/register', + headers: { + isToken: false + }, + method: 'post', + data: data + }) +} + +// 修改语言 +export function changeLanguage(lang){ + return request({ + url: '/changeLanguage', + method: 'get', + headers: { + isToken: false, + }, + params: { + lang: lang + } + }) +} + +// 获取用户详细信息 +export function getInfo() { + return request({ + url: '/getInfo', + method: 'get' + }) +} + +// 退出方法 +export function logout() { + return request({ + url: '/logout', + method: 'post' + }) +} + +// 获取验证码 +export function getCodeImg() { + return request({ + url: '/captchaImage', + headers: { + isToken: false + }, + method: 'get', + timeout: 20000 + }) +} diff --git a/ruoyi-ui/src/api/menu.js b/ruoyi-ui/src/api/menu.js new file mode 100644 index 0000000..faef101 --- /dev/null +++ b/ruoyi-ui/src/api/menu.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取路由 +export const getRouters = () => { + return request({ + url: '/getRouters', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/cache.js b/ruoyi-ui/src/api/monitor/cache.js new file mode 100644 index 0000000..72c5f6a --- /dev/null +++ b/ruoyi-ui/src/api/monitor/cache.js @@ -0,0 +1,57 @@ +import request from '@/utils/request' + +// 查询缓存详细 +export function getCache() { + return request({ + url: '/monitor/cache', + method: 'get' + }) +} + +// 查询缓存名称列表 +export function listCacheName() { + return request({ + url: '/monitor/cache/getNames', + method: 'get' + }) +} + +// 查询缓存键名列表 +export function listCacheKey(cacheName) { + return request({ + url: '/monitor/cache/getKeys/' + cacheName, + method: 'get' + }) +} + +// 查询缓存内容 +export function getCacheValue(cacheName, cacheKey) { + return request({ + url: '/monitor/cache/getValue/' + cacheName + '/' + cacheKey, + method: 'get' + }) +} + +// 清理指定名称缓存 +export function clearCacheName(cacheName) { + return request({ + url: '/monitor/cache/clearCacheName/' + cacheName, + method: 'delete' + }) +} + +// 清理指定键名缓存 +export function clearCacheKey(cacheKey) { + return request({ + url: '/monitor/cache/clearCacheKey/' + cacheKey, + method: 'delete' + }) +} + +// 清理全部缓存 +export function clearCacheAll() { + return request({ + url: '/monitor/cache/clearCacheAll', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/job.js b/ruoyi-ui/src/api/monitor/job.js new file mode 100644 index 0000000..3815569 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/job.js @@ -0,0 +1,71 @@ +import request from '@/utils/request' + +// 查询定时任务调度列表 +export function listJob(query) { + return request({ + url: '/monitor/job/list', + method: 'get', + params: query + }) +} + +// 查询定时任务调度详细 +export function getJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'get' + }) +} + +// 新增定时任务调度 +export function addJob(data) { + return request({ + url: '/monitor/job', + method: 'post', + data: data + }) +} + +// 修改定时任务调度 +export function updateJob(data) { + return request({ + url: '/monitor/job', + method: 'put', + data: data + }) +} + +// 删除定时任务调度 +export function delJob(jobId) { + return request({ + url: '/monitor/job/' + jobId, + method: 'delete' + }) +} + +// 任务状态修改 +export function changeJobStatus(jobId, status) { + const data = { + jobId, + status + } + return request({ + url: '/monitor/job/changeStatus', + method: 'put', + data: data + }) +} + + +// 定时任务立即执行一次 +export function runJob(jobId, jobGroup) { + const data = { + jobId, + jobGroup + } + return request({ + url: '/monitor/job/run', + method: 'put', + data: data + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/monitor/jobLog.js b/ruoyi-ui/src/api/monitor/jobLog.js new file mode 100644 index 0000000..6e0be61 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/jobLog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询调度日志列表 +export function listJobLog(query) { + return request({ + url: '/monitor/jobLog/list', + method: 'get', + params: query + }) +} + +// 删除调度日志 +export function delJobLog(jobLogId) { + return request({ + url: '/monitor/jobLog/' + jobLogId, + method: 'delete' + }) +} + +// 清空调度日志 +export function cleanJobLog() { + return request({ + url: '/monitor/jobLog/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/logininfor.js b/ruoyi-ui/src/api/monitor/logininfor.js new file mode 100644 index 0000000..4d112b7 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/logininfor.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +// 查询登录日志列表 +export function list(query) { + return request({ + url: '/monitor/logininfor/list', + method: 'get', + params: query + }) +} + +// 删除登录日志 +export function delLogininfor(infoId) { + return request({ + url: '/monitor/logininfor/' + infoId, + method: 'delete' + }) +} + +// 解锁用户登录状态 +export function unlockLogininfor(userName) { + return request({ + url: '/monitor/logininfor/unlock/' + userName, + method: 'get' + }) +} + +// 清空登录日志 +export function cleanLogininfor() { + return request({ + url: '/monitor/logininfor/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/online.js b/ruoyi-ui/src/api/monitor/online.js new file mode 100644 index 0000000..bd22137 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/online.js @@ -0,0 +1,18 @@ +import request from '@/utils/request' + +// 查询在线用户列表 +export function list(query) { + return request({ + url: '/monitor/online/list', + method: 'get', + params: query + }) +} + +// 强退用户 +export function forceLogout(tokenId) { + return request({ + url: '/monitor/online/' + tokenId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/operlog.js b/ruoyi-ui/src/api/monitor/operlog.js new file mode 100644 index 0000000..a04bca8 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/operlog.js @@ -0,0 +1,26 @@ +import request from '@/utils/request' + +// 查询操作日志列表 +export function list(query) { + return request({ + url: '/monitor/operlog/list', + method: 'get', + params: query + }) +} + +// 删除操作日志 +export function delOperlog(operId) { + return request({ + url: '/monitor/operlog/' + operId, + method: 'delete' + }) +} + +// 清空操作日志 +export function cleanOperlog() { + return request({ + url: '/monitor/operlog/clean', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/monitor/server.js b/ruoyi-ui/src/api/monitor/server.js new file mode 100644 index 0000000..e1f9ca2 --- /dev/null +++ b/ruoyi-ui/src/api/monitor/server.js @@ -0,0 +1,9 @@ +import request from '@/utils/request' + +// 获取服务信息 +export function getServer() { + return request({ + url: '/monitor/server', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/queue/ddj.js b/ruoyi-ui/src/api/queue/ddj.js new file mode 100644 index 0000000..1a0635b --- /dev/null +++ b/ruoyi-ui/src/api/queue/ddj.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询堆垛机执行队列列表 +export function listDdj(query) { + return request({ + url: '/queue/ddj/list', + method: 'get', + params: query + }) +} + +export function listFinishDdj(query) { + return request({ + url: '/queue/ddjFinish/list', + method: 'get', + params: query + }) +} + +// 查询堆垛机执行队列详细 +export function getDdj(uuid) { + return request({ + url: '/queue/ddj/' + uuid, + method: 'get' + }) +} + +// 新增堆垛机执行队列 +export function addDdj(data) { + return request({ + url: '/queue/ddj', + method: 'post', + data: data + }) +} + +// 修改堆垛机执行队列 +export function updateDdj(data) { + return request({ + url: '/queue/ddj', + method: 'put', + data: data + }) +} + +// 删除堆垛机执行队列 +export function delDdj(uuid) { + return request({ + url: '/queue/ddj/' + uuid, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/scheduleTask/task.js b/ruoyi-ui/src/api/scheduleTask/task.js new file mode 100644 index 0000000..45c48f2 --- /dev/null +++ b/ruoyi-ui/src/api/scheduleTask/task.js @@ -0,0 +1,42 @@ +import request from '@/utils/request' + +// 开启plc读取线程 +export function startReadPlc() { + return request({ + url: '/device/operation/startAllThread', + method: 'get', + }) +} + +// 停止plc读取线程 +export function stopReadPlc() { + return request({ + url: '/device/operation/stopAllThread', + method: 'get' + }) +} + +// 开启plc调度线程 +export function startSchedule() { + return request({ + url: '/device/operation/startAllThread2', + method: 'get' + }) +} + +// 关闭plc调度线程 +export function stopSchedule() { + return request({ + url: '/device/operation/stopAllThread2', + method: 'get' + }) +} + +// 关闭plc调度线程 +export function getScheduleStatus() { + return request({ + url: '/device/operation/getScheduleStatus', + method: 'get' + }) +} + diff --git a/ruoyi-ui/src/api/system/config.js b/ruoyi-ui/src/api/system/config.js new file mode 100644 index 0000000..a404d82 --- /dev/null +++ b/ruoyi-ui/src/api/system/config.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询参数列表 +export function listConfig(query) { + return request({ + url: '/system/config/list', + method: 'get', + params: query + }) +} + +// 查询参数详细 +export function getConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'get' + }) +} + +// 根据参数键名查询参数值 +export function getConfigKey(configKey) { + return request({ + url: '/system/config/configKey/' + configKey, + method: 'get' + }) +} + +// 新增参数配置 +export function addConfig(data) { + return request({ + url: '/system/config', + method: 'post', + data: data + }) +} + +// 修改参数配置 +export function updateConfig(data) { + return request({ + url: '/system/config', + method: 'put', + data: data + }) +} + +// 删除参数配置 +export function delConfig(configId) { + return request({ + url: '/system/config/' + configId, + method: 'delete' + }) +} + +// 刷新参数缓存 +export function refreshCache() { + return request({ + url: '/system/config/refreshCache', + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/dept.js b/ruoyi-ui/src/api/system/dept.js new file mode 100644 index 0000000..fc943cd --- /dev/null +++ b/ruoyi-ui/src/api/system/dept.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询部门列表 +export function listDept(query) { + return request({ + url: '/system/dept/list', + method: 'get', + params: query + }) +} + +// 查询部门列表(排除节点) +export function listDeptExcludeChild(deptId) { + return request({ + url: '/system/dept/list/exclude/' + deptId, + method: 'get' + }) +} + +// 查询部门详细 +export function getDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'get' + }) +} + +// 新增部门 +export function addDept(data) { + return request({ + url: '/system/dept', + method: 'post', + data: data + }) +} + +// 修改部门 +export function updateDept(data) { + return request({ + url: '/system/dept', + method: 'put', + data: data + }) +} + +// 删除部门 +export function delDept(deptId) { + return request({ + url: '/system/dept/' + deptId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/dict/data.js b/ruoyi-ui/src/api/system/dict/data.js new file mode 100644 index 0000000..6c9eb79 --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/data.js @@ -0,0 +1,52 @@ +import request from '@/utils/request' + +// 查询字典数据列表 +export function listData(query) { + return request({ + url: '/system/dict/data/list', + method: 'get', + params: query + }) +} + +// 查询字典数据详细 +export function getData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'get' + }) +} + +// 根据字典类型查询字典数据信息 +export function getDicts(dictType) { + return request({ + url: '/system/dict/data/type/' + dictType, + method: 'get' + }) +} + +// 新增字典数据 +export function addData(data) { + return request({ + url: '/system/dict/data', + method: 'post', + data: data + }) +} + +// 修改字典数据 +export function updateData(data) { + return request({ + url: '/system/dict/data', + method: 'put', + data: data + }) +} + +// 删除字典数据 +export function delData(dictCode) { + return request({ + url: '/system/dict/data/' + dictCode, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/dict/type.js b/ruoyi-ui/src/api/system/dict/type.js new file mode 100644 index 0000000..a7a6e01 --- /dev/null +++ b/ruoyi-ui/src/api/system/dict/type.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询字典类型列表 +export function listType(query) { + return request({ + url: '/system/dict/type/list', + method: 'get', + params: query + }) +} + +// 查询字典类型详细 +export function getType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'get' + }) +} + +// 新增字典类型 +export function addType(data) { + return request({ + url: '/system/dict/type', + method: 'post', + data: data + }) +} + +// 修改字典类型 +export function updateType(data) { + return request({ + url: '/system/dict/type', + method: 'put', + data: data + }) +} + +// 删除字典类型 +export function delType(dictId) { + return request({ + url: '/system/dict/type/' + dictId, + method: 'delete' + }) +} + +// 刷新字典缓存 +export function refreshCache() { + return request({ + url: '/system/dict/type/refreshCache', + method: 'delete' + }) +} + +// 获取字典选择框列表 +export function optionselect() { + return request({ + url: '/system/dict/type/optionselect', + method: 'get' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/menu.js b/ruoyi-ui/src/api/system/menu.js new file mode 100644 index 0000000..f6415c6 --- /dev/null +++ b/ruoyi-ui/src/api/system/menu.js @@ -0,0 +1,60 @@ +import request from '@/utils/request' + +// 查询菜单列表 +export function listMenu(query) { + return request({ + url: '/system/menu/list', + method: 'get', + params: query + }) +} + +// 查询菜单详细 +export function getMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'get' + }) +} + +// 查询菜单下拉树结构 +export function treeselect() { + return request({ + url: '/system/menu/treeselect', + method: 'get' + }) +} + +// 根据角色ID查询菜单下拉树结构 +export function roleMenuTreeselect(roleId) { + return request({ + url: '/system/menu/roleMenuTreeselect/' + roleId, + method: 'get' + }) +} + +// 新增菜单 +export function addMenu(data) { + return request({ + url: '/system/menu', + method: 'post', + data: data + }) +} + +// 修改菜单 +export function updateMenu(data) { + return request({ + url: '/system/menu', + method: 'put', + data: data + }) +} + +// 删除菜单 +export function delMenu(menuId) { + return request({ + url: '/system/menu/' + menuId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/notice.js b/ruoyi-ui/src/api/system/notice.js new file mode 100644 index 0000000..c274ea5 --- /dev/null +++ b/ruoyi-ui/src/api/system/notice.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询公告列表 +export function listNotice(query) { + return request({ + url: '/system/notice/list', + method: 'get', + params: query + }) +} + +// 查询公告详细 +export function getNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'get' + }) +} + +// 新增公告 +export function addNotice(data) { + return request({ + url: '/system/notice', + method: 'post', + data: data + }) +} + +// 修改公告 +export function updateNotice(data) { + return request({ + url: '/system/notice', + method: 'put', + data: data + }) +} + +// 删除公告 +export function delNotice(noticeId) { + return request({ + url: '/system/notice/' + noticeId, + method: 'delete' + }) +} \ No newline at end of file diff --git a/ruoyi-ui/src/api/system/post.js b/ruoyi-ui/src/api/system/post.js new file mode 100644 index 0000000..1a8e9ca --- /dev/null +++ b/ruoyi-ui/src/api/system/post.js @@ -0,0 +1,44 @@ +import request from '@/utils/request' + +// 查询岗位列表 +export function listPost(query) { + return request({ + url: '/system/post/list', + method: 'get', + params: query + }) +} + +// 查询岗位详细 +export function getPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'get' + }) +} + +// 新增岗位 +export function addPost(data) { + return request({ + url: '/system/post', + method: 'post', + data: data + }) +} + +// 修改岗位 +export function updatePost(data) { + return request({ + url: '/system/post', + method: 'put', + data: data + }) +} + +// 删除岗位 +export function delPost(postId) { + return request({ + url: '/system/post/' + postId, + method: 'delete' + }) +} diff --git a/ruoyi-ui/src/api/system/role.js b/ruoyi-ui/src/api/system/role.js new file mode 100644 index 0000000..f13e6f4 --- /dev/null +++ b/ruoyi-ui/src/api/system/role.js @@ -0,0 +1,119 @@ +import request from '@/utils/request' + +// 查询角色列表 +export function listRole(query) { + return request({ + url: '/system/role/list', + method: 'get', + params: query + }) +} + +// 查询角色详细 +export function getRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'get' + }) +} + +// 新增角色 +export function addRole(data) { + return request({ + url: '/system/role', + method: 'post', + data: data + }) +} + +// 修改角色 +export function updateRole(data) { + return request({ + url: '/system/role', + method: 'put', + data: data + }) +} + +// 角色数据权限 +export function dataScope(data) { + return request({ + url: '/system/role/dataScope', + method: 'put', + data: data + }) +} + +// 角色状态修改 +export function changeRoleStatus(roleId, status) { + const data = { + roleId, + status + } + return request({ + url: '/system/role/changeStatus', + method: 'put', + data: data + }) +} + +// 删除角色 +export function delRole(roleId) { + return request({ + url: '/system/role/' + roleId, + method: 'delete' + }) +} + +// 查询角色已授权用户列表 +export function allocatedUserList(query) { + return request({ + url: '/system/role/authUser/allocatedList', + method: 'get', + params: query + }) +} + +// 查询角色未授权用户列表 +export function unallocatedUserList(query) { + return request({ + url: '/system/role/authUser/unallocatedList', + method: 'get', + params: query + }) +} + +// 取消用户授权角色 +export function authUserCancel(data) { + return request({ + url: '/system/role/authUser/cancel', + method: 'put', + data: data + }) +} + +// 批量取消用户授权角色 +export function authUserCancelAll(data) { + return request({ + url: '/system/role/authUser/cancelAll', + method: 'put', + params: data + }) +} + +// 授权用户选择 +export function authUserSelectAll(data) { + return request({ + url: '/system/role/authUser/selectAll', + method: 'put', + params: data + }) +} + +// 根据角色ID查询部门树结构 +export function deptTreeSelect(roleId) { + return request({ + url: '/system/role/deptTree/' + roleId, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/system/user.js b/ruoyi-ui/src/api/system/user.js new file mode 100644 index 0000000..b5e3edd --- /dev/null +++ b/ruoyi-ui/src/api/system/user.js @@ -0,0 +1,136 @@ +import request from '@/utils/request' +import { parseStrEmpty } from "@/utils/ruoyi"; + +// 查询用户列表 +export function listUser(query) { + return request({ + url: '/system/user/list', + method: 'get', + params: query + }) +} + +// 查询用户详细 +export function getUser(userId) { + return request({ + url: '/system/user/' + parseStrEmpty(userId), + method: 'get' + }) +} + +// 新增用户 +export function addUser(data) { + return request({ + url: '/system/user', + method: 'post', + data: data + }) +} + +// 修改用户 +export function updateUser(data) { + return request({ + url: '/system/user', + method: 'put', + data: data + }) +} + +// 删除用户 +export function delUser(userId) { + return request({ + url: '/system/user/' + userId, + method: 'delete' + }) +} + +// 用户密码重置 +export function resetUserPwd(userId, password) { + const data = { + userId, + password + } + return request({ + url: '/system/user/resetPwd', + method: 'put', + data: data + }) +} + +// 用户状态修改 +export function changeUserStatus(userId, status) { + const data = { + userId, + status + } + return request({ + url: '/system/user/changeStatus', + method: 'put', + data: data + }) +} + +// 查询用户个人信息 +export function getUserProfile() { + return request({ + url: '/system/user/profile', + method: 'get' + }) +} + +// 修改用户个人信息 +export function updateUserProfile(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) +} + +// 用户密码重置 +export function updateUserPwd(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + data: data + }) +} + +// 用户头像上传 +export function uploadAvatar(data) { + return request({ + url: '/system/user/profile/avatar', + method: 'post', + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + data: data + }) +} + +// 查询授权角色 +export function getAuthRole(userId) { + return request({ + url: '/system/user/authRole/' + userId, + method: 'get' + }) +} + +// 保存授权角色 +export function updateAuthRole(data) { + return request({ + url: '/system/user/authRole', + method: 'put', + params: data + }) +} + +// 查询部门下拉树结构 +export function deptTreeSelect() { + return request({ + url: '/system/user/deptTree', + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/tool/gen.js b/ruoyi-ui/src/api/tool/gen.js new file mode 100644 index 0000000..2075677 --- /dev/null +++ b/ruoyi-ui/src/api/tool/gen.js @@ -0,0 +1,85 @@ +import request from '@/utils/request' + +// 查询生成表数据 +export function listTable(query) { + return request({ + url: '/tool/gen/list', + method: 'get', + params: query + }) +} +// 查询db数据库列表 +export function listDbTable(query) { + return request({ + url: '/tool/gen/db/list', + method: 'get', + params: query + }) +} + +// 查询表详细信息 +export function getGenTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'get' + }) +} + +// 修改代码生成信息 +export function updateGenTable(data) { + return request({ + url: '/tool/gen', + method: 'put', + data: data + }) +} + +// 导入表 +export function importTable(data) { + return request({ + url: '/tool/gen/importTable', + method: 'post', + params: data + }) +} + +// 创建表 +export function createTable(data) { + return request({ + url: '/tool/gen/createTable', + method: 'post', + params: data + }) +} + +// 预览生成代码 +export function previewTable(tableId) { + return request({ + url: '/tool/gen/preview/' + tableId, + method: 'get' + }) +} + +// 删除表数据 +export function delTable(tableId) { + return request({ + url: '/tool/gen/' + tableId, + method: 'delete' + }) +} + +// 生成代码(自定义路径) +export function genCode(tableName) { + return request({ + url: '/tool/gen/genCode/' + tableName, + method: 'get' + }) +} + +// 同步数据库 +export function synchDb(tableName) { + return request({ + url: '/tool/gen/synchDb/' + tableName, + method: 'get' + }) +} diff --git a/ruoyi-ui/src/api/work.js b/ruoyi-ui/src/api/work.js new file mode 100644 index 0000000..79ebb34 --- /dev/null +++ b/ruoyi-ui/src/api/work.js @@ -0,0 +1,34 @@ +import request from '@/utils/request' + +// 已完成入库任务 +export function listWork1(query) { + return request({ + url: '/work/list1', + method: 'get', + params: query + }) +} +// 已完成出库任务 +export function listWork2(query) { + return request({ + url: '/work/list2', + method: 'get', + params: query + }) +} +// 未完成入库任务 +export function listWork3(query) { + return request({ + url: '/work/list3', + method: 'get', + params: query + }) +} +// 未完成出库任务 +export function listWork4(query) { + return request({ + url: '/work/list4', + method: 'get', + params: query + }) +} diff --git a/ruoyi-ui/src/assets/401_images/401.gif b/ruoyi-ui/src/assets/401_images/401.gif new file mode 100644 index 0000000..cd6e0d9 Binary files /dev/null and b/ruoyi-ui/src/assets/401_images/401.gif differ diff --git a/ruoyi-ui/src/assets/404_images/404.png b/ruoyi-ui/src/assets/404_images/404.png new file mode 100644 index 0000000..3d8e230 Binary files /dev/null and b/ruoyi-ui/src/assets/404_images/404.png differ diff --git a/ruoyi-ui/src/assets/404_images/404_cloud.png b/ruoyi-ui/src/assets/404_images/404_cloud.png new file mode 100644 index 0000000..c6281d0 Binary files /dev/null and b/ruoyi-ui/src/assets/404_images/404_cloud.png differ diff --git a/ruoyi-ui/src/assets/icons/index.js b/ruoyi-ui/src/assets/icons/index.js new file mode 100644 index 0000000..2c6b309 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/index.js @@ -0,0 +1,9 @@ +import Vue from 'vue' +import SvgIcon from '@/components/SvgIcon'// svg component + +// register globally +Vue.component('svg-icon', SvgIcon) + +const req = require.context('./svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys().map(requireContext) +requireAll(req) diff --git a/ruoyi-ui/src/assets/icons/svg/404.svg b/ruoyi-ui/src/assets/icons/svg/404.svg new file mode 100644 index 0000000..6df5019 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/404.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/bug.svg b/ruoyi-ui/src/assets/icons/svg/bug.svg new file mode 100644 index 0000000..05a150d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/bug.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/build.svg b/ruoyi-ui/src/assets/icons/svg/build.svg new file mode 100644 index 0000000..97c4688 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/build.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/button.svg b/ruoyi-ui/src/assets/icons/svg/button.svg new file mode 100644 index 0000000..904fddc --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/button.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/cascader.svg b/ruoyi-ui/src/assets/icons/svg/cascader.svg new file mode 100644 index 0000000..e256024 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/cascader.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/chart.svg b/ruoyi-ui/src/assets/icons/svg/chart.svg new file mode 100644 index 0000000..27728fb --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/chart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/checkbox.svg b/ruoyi-ui/src/assets/icons/svg/checkbox.svg new file mode 100644 index 0000000..013fd3a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/checkbox.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/clipboard.svg b/ruoyi-ui/src/assets/icons/svg/clipboard.svg new file mode 100644 index 0000000..90923ff --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/clipboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/code.svg b/ruoyi-ui/src/assets/icons/svg/code.svg new file mode 100644 index 0000000..5f9c5ab --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/code.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/color.svg b/ruoyi-ui/src/assets/icons/svg/color.svg new file mode 100644 index 0000000..44a81aa --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/color.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/component.svg b/ruoyi-ui/src/assets/icons/svg/component.svg new file mode 100644 index 0000000..29c3458 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/component.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dashboard.svg b/ruoyi-ui/src/assets/icons/svg/dashboard.svg new file mode 100644 index 0000000..5317d37 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/date-range.svg b/ruoyi-ui/src/assets/icons/svg/date-range.svg new file mode 100644 index 0000000..fda571e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/date-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/date.svg b/ruoyi-ui/src/assets/icons/svg/date.svg new file mode 100644 index 0000000..52dc73e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/date.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/dict.svg b/ruoyi-ui/src/assets/icons/svg/dict.svg new file mode 100644 index 0000000..4849377 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/dict.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/documentation.svg b/ruoyi-ui/src/assets/icons/svg/documentation.svg new file mode 100644 index 0000000..7043122 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/documentation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/download.svg b/ruoyi-ui/src/assets/icons/svg/download.svg new file mode 100644 index 0000000..c896951 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/drag.svg b/ruoyi-ui/src/assets/icons/svg/drag.svg new file mode 100644 index 0000000..4185d3c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/drag.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/druid.svg b/ruoyi-ui/src/assets/icons/svg/druid.svg new file mode 100644 index 0000000..a2b4b4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/druid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/edit.svg b/ruoyi-ui/src/assets/icons/svg/edit.svg new file mode 100644 index 0000000..d26101f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/education.svg b/ruoyi-ui/src/assets/icons/svg/education.svg new file mode 100644 index 0000000..7bfb01d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/education.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/email.svg b/ruoyi-ui/src/assets/icons/svg/email.svg new file mode 100644 index 0000000..74d25e2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/email.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/enter.svg b/ruoyi-ui/src/assets/icons/svg/enter.svg new file mode 100644 index 0000000..f7cabf2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/enter.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/example.svg b/ruoyi-ui/src/assets/icons/svg/example.svg new file mode 100644 index 0000000..46f42b5 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/example.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/excel.svg b/ruoyi-ui/src/assets/icons/svg/excel.svg new file mode 100644 index 0000000..74d97b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/excel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg new file mode 100644 index 0000000..485c128 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/exit-fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye-open.svg b/ruoyi-ui/src/assets/icons/svg/eye-open.svg new file mode 100644 index 0000000..88dcc98 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/eye.svg b/ruoyi-ui/src/assets/icons/svg/eye.svg new file mode 100644 index 0000000..16ed2d8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/eye.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/form.svg b/ruoyi-ui/src/assets/icons/svg/form.svg new file mode 100644 index 0000000..dcbaa18 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/form.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/fullscreen.svg b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg new file mode 100644 index 0000000..0e86b6f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/fullscreen.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/github.svg b/ruoyi-ui/src/assets/icons/svg/github.svg new file mode 100644 index 0000000..db0a0d4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/guide.svg b/ruoyi-ui/src/assets/icons/svg/guide.svg new file mode 100644 index 0000000..b271001 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/guide.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/icon.svg b/ruoyi-ui/src/assets/icons/svg/icon.svg new file mode 100644 index 0000000..82be8ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/input.svg b/ruoyi-ui/src/assets/icons/svg/input.svg new file mode 100644 index 0000000..ab91381 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/input.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/international.svg b/ruoyi-ui/src/assets/icons/svg/international.svg new file mode 100644 index 0000000..e9b56ee --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/international.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/job.svg b/ruoyi-ui/src/assets/icons/svg/job.svg new file mode 100644 index 0000000..2a93a25 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/job.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/language.svg b/ruoyi-ui/src/assets/icons/svg/language.svg new file mode 100644 index 0000000..0082b57 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/language.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/link.svg b/ruoyi-ui/src/assets/icons/svg/link.svg new file mode 100644 index 0000000..48197ba --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/link.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/list.svg b/ruoyi-ui/src/assets/icons/svg/list.svg new file mode 100644 index 0000000..20259ed --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/list.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/lock.svg b/ruoyi-ui/src/assets/icons/svg/lock.svg new file mode 100644 index 0000000..74fee54 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/lock.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/log.svg b/ruoyi-ui/src/assets/icons/svg/log.svg new file mode 100644 index 0000000..d879d33 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/log.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/logininfor.svg b/ruoyi-ui/src/assets/icons/svg/logininfor.svg new file mode 100644 index 0000000..267f844 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/logininfor.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/message.svg b/ruoyi-ui/src/assets/icons/svg/message.svg new file mode 100644 index 0000000..14ca817 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/money.svg b/ruoyi-ui/src/assets/icons/svg/money.svg new file mode 100644 index 0000000..c1580de --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/money.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/monitor.svg b/ruoyi-ui/src/assets/icons/svg/monitor.svg new file mode 100644 index 0000000..bc308cb --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/monitor.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/more-up.svg b/ruoyi-ui/src/assets/icons/svg/more-up.svg new file mode 100644 index 0000000..d30ac11 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/more-up.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/nested.svg b/ruoyi-ui/src/assets/icons/svg/nested.svg new file mode 100644 index 0000000..06713a8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/nested.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/number.svg b/ruoyi-ui/src/assets/icons/svg/number.svg new file mode 100644 index 0000000..ad5ce9a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/number.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/online.svg b/ruoyi-ui/src/assets/icons/svg/online.svg new file mode 100644 index 0000000..330a202 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/online.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/password.svg b/ruoyi-ui/src/assets/icons/svg/password.svg new file mode 100644 index 0000000..6c64def --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/password.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/pdf.svg b/ruoyi-ui/src/assets/icons/svg/pdf.svg new file mode 100644 index 0000000..957aa0c --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/people.svg b/ruoyi-ui/src/assets/icons/svg/people.svg new file mode 100644 index 0000000..2bd54ae --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/people.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/peoples.svg b/ruoyi-ui/src/assets/icons/svg/peoples.svg new file mode 100644 index 0000000..aab852e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/peoples.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/phone.svg b/ruoyi-ui/src/assets/icons/svg/phone.svg new file mode 100644 index 0000000..ab8e8c4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/phone.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/post.svg b/ruoyi-ui/src/assets/icons/svg/post.svg new file mode 100644 index 0000000..2922c61 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/post.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/qq.svg b/ruoyi-ui/src/assets/icons/svg/qq.svg new file mode 100644 index 0000000..ee13d4e --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/qq.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/question.svg b/ruoyi-ui/src/assets/icons/svg/question.svg new file mode 100644 index 0000000..cf75bd4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/question.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/radio.svg b/ruoyi-ui/src/assets/icons/svg/radio.svg new file mode 100644 index 0000000..0cde345 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/radio.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/rate.svg b/ruoyi-ui/src/assets/icons/svg/rate.svg new file mode 100644 index 0000000..aa3b14d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/rate.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/redis-list.svg b/ruoyi-ui/src/assets/icons/svg/redis-list.svg new file mode 100644 index 0000000..98a15b2 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/redis-list.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/redis.svg b/ruoyi-ui/src/assets/icons/svg/redis.svg new file mode 100644 index 0000000..2f1d62d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/redis.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/row.svg b/ruoyi-ui/src/assets/icons/svg/row.svg new file mode 100644 index 0000000..0780992 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/row.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/search.svg b/ruoyi-ui/src/assets/icons/svg/search.svg new file mode 100644 index 0000000..84233dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/search.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/select.svg b/ruoyi-ui/src/assets/icons/svg/select.svg new file mode 100644 index 0000000..d628382 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/server.svg b/ruoyi-ui/src/assets/icons/svg/server.svg new file mode 100644 index 0000000..eb287e3 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/server.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/shopping.svg b/ruoyi-ui/src/assets/icons/svg/shopping.svg new file mode 100644 index 0000000..87513e7 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/shopping.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/size.svg b/ruoyi-ui/src/assets/icons/svg/size.svg new file mode 100644 index 0000000..ddb25b8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/size.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/skill.svg b/ruoyi-ui/src/assets/icons/svg/skill.svg new file mode 100644 index 0000000..a3b7312 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/skill.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/slider.svg b/ruoyi-ui/src/assets/icons/svg/slider.svg new file mode 100644 index 0000000..fbe4f39 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/slider.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/star.svg b/ruoyi-ui/src/assets/icons/svg/star.svg new file mode 100644 index 0000000..6cf86e6 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/star.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/swagger.svg b/ruoyi-ui/src/assets/icons/svg/swagger.svg new file mode 100644 index 0000000..05d4e7b --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/swagger.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/switch.svg b/ruoyi-ui/src/assets/icons/svg/switch.svg new file mode 100644 index 0000000..0ba61e3 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/switch.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/system.svg b/ruoyi-ui/src/assets/icons/svg/system.svg new file mode 100644 index 0000000..5992593 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/system.svg @@ -0,0 +1,2 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tab.svg b/ruoyi-ui/src/assets/icons/svg/tab.svg new file mode 100644 index 0000000..b4b48e4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/table.svg b/ruoyi-ui/src/assets/icons/svg/table.svg new file mode 100644 index 0000000..0e3dc9d --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/textarea.svg b/ruoyi-ui/src/assets/icons/svg/textarea.svg new file mode 100644 index 0000000..2709f29 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/textarea.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/theme.svg b/ruoyi-ui/src/assets/icons/svg/theme.svg new file mode 100644 index 0000000..5982a2f --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/theme.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/time-range.svg b/ruoyi-ui/src/assets/icons/svg/time-range.svg new file mode 100644 index 0000000..13c1202 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/time-range.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/time.svg b/ruoyi-ui/src/assets/icons/svg/time.svg new file mode 100644 index 0000000..b376e32 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/time.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tool.svg b/ruoyi-ui/src/assets/icons/svg/tool.svg new file mode 100644 index 0000000..48e0e35 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tool.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree-table.svg b/ruoyi-ui/src/assets/icons/svg/tree-table.svg new file mode 100644 index 0000000..8aafdb8 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree-table.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/tree.svg b/ruoyi-ui/src/assets/icons/svg/tree.svg new file mode 100644 index 0000000..dd4b7dd --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/tree.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/upload.svg b/ruoyi-ui/src/assets/icons/svg/upload.svg new file mode 100644 index 0000000..bae49c0 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/upload.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/user.svg b/ruoyi-ui/src/assets/icons/svg/user.svg new file mode 100644 index 0000000..0ba0716 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/validCode.svg b/ruoyi-ui/src/assets/icons/svg/validCode.svg new file mode 100644 index 0000000..cfb1021 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/validCode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/wechat.svg b/ruoyi-ui/src/assets/icons/svg/wechat.svg new file mode 100644 index 0000000..c586e55 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/wechat.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svg/zip.svg b/ruoyi-ui/src/assets/icons/svg/zip.svg new file mode 100644 index 0000000..f806fc4 --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svg/zip.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/icons/svgo.yml b/ruoyi-ui/src/assets/icons/svgo.yml new file mode 100644 index 0000000..d11906a --- /dev/null +++ b/ruoyi-ui/src/assets/icons/svgo.yml @@ -0,0 +1,22 @@ +# replace default config + +# multipass: true +# full: true + +plugins: + + # - name + # + # or: + # - name: false + # - name: true + # + # or: + # - name: + # param1: 1 + # param2: 2 + +- removeAttrs: + attrs: + - 'fill' + - 'fill-rule' diff --git a/ruoyi-ui/src/assets/images/dark.svg b/ruoyi-ui/src/assets/images/dark.svg new file mode 100644 index 0000000..f646bd7 --- /dev/null +++ b/ruoyi-ui/src/assets/images/dark.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/images/light.svg b/ruoyi-ui/src/assets/images/light.svg new file mode 100644 index 0000000..ab7cc08 --- /dev/null +++ b/ruoyi-ui/src/assets/images/light.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/images/login-background.jpg b/ruoyi-ui/src/assets/images/login-background.jpg new file mode 100644 index 0000000..8a89eb8 Binary files /dev/null and b/ruoyi-ui/src/assets/images/login-background.jpg differ diff --git a/ruoyi-ui/src/assets/images/new.svg b/ruoyi-ui/src/assets/images/new.svg new file mode 100644 index 0000000..7ba0456 --- /dev/null +++ b/ruoyi-ui/src/assets/images/new.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/assets/images/pay.png b/ruoyi-ui/src/assets/images/pay.png new file mode 100644 index 0000000..bb8b967 Binary files /dev/null and b/ruoyi-ui/src/assets/images/pay.png differ diff --git a/ruoyi-ui/src/assets/images/profile.jpg b/ruoyi-ui/src/assets/images/profile.jpg new file mode 100644 index 0000000..b3a940b Binary files /dev/null and b/ruoyi-ui/src/assets/images/profile.jpg differ diff --git a/ruoyi-ui/src/assets/logo/logo.png b/ruoyi-ui/src/assets/logo/logo.png new file mode 100644 index 0000000..e263760 Binary files /dev/null and b/ruoyi-ui/src/assets/logo/logo.png differ diff --git a/ruoyi-ui/src/assets/styles/btn.scss b/ruoyi-ui/src/assets/styles/btn.scss new file mode 100644 index 0000000..e6ba1a8 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/btn.scss @@ -0,0 +1,99 @@ +@import './variables.scss'; + +@mixin colorBtn($color) { + background: $color; + + &:hover { + color: $color; + + &:before, + &:after { + background: $color; + } + } +} + +.blue-btn { + @include colorBtn($blue) +} + +.light-blue-btn { + @include colorBtn($light-blue) +} + +.red-btn { + @include colorBtn($red) +} + +.pink-btn { + @include colorBtn($pink) +} + +.green-btn { + @include colorBtn($green) +} + +.tiffany-btn { + @include colorBtn($tiffany) +} + +.yellow-btn { + @include colorBtn($yellow) +} + +.pan-btn { + font-size: 14px; + color: #fff; + padding: 14px 36px; + border-radius: 8px; + border: none; + outline: none; + transition: 600ms ease all; + position: relative; + display: inline-block; + + &:hover { + background: #fff; + + &:before, + &:after { + width: 100%; + transition: 600ms ease all; + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + height: 2px; + width: 0; + transition: 400ms ease all; + } + + &::after { + right: inherit; + top: inherit; + left: 0; + bottom: 0; + } +} + +.custom-button { + display: inline-block; + line-height: 1; + white-space: nowrap; + cursor: pointer; + background: #fff; + color: #fff; + -webkit-appearance: none; + text-align: center; + box-sizing: border-box; + outline: 0; + margin: 0; + padding: 10px 15px; + font-size: 14px; + border-radius: 4px; +} diff --git a/ruoyi-ui/src/assets/styles/element-ui.scss b/ruoyi-ui/src/assets/styles/element-ui.scss new file mode 100644 index 0000000..363092a --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-ui.scss @@ -0,0 +1,92 @@ +// cover some element-ui styles + +.el-breadcrumb__inner, +.el-breadcrumb__inner a { + font-weight: 400 !important; +} + +.el-upload { + input[type="file"] { + display: none !important; + } +} + +.el-upload__input { + display: none; +} + +.cell { + .el-tag { + margin-right: 0px; + } +} + +.small-padding { + .cell { + padding-left: 5px; + padding-right: 5px; + } +} + +.fixed-width { + .el-button--mini { + padding: 7px 10px; + width: 60px; + } +} + +.status-col { + .cell { + padding: 0 10px; + text-align: center; + + .el-tag { + margin-right: 0px; + } + } +} + +// to fixed https://github.com/ElemeFE/element/issues/2461 +.el-dialog { + transform: none; + left: 0; + position: relative; + margin: 0 auto; +} + +// refine element ui upload +.upload-container { + .el-upload { + width: 100%; + + .el-upload-dragger { + width: 100%; + height: 200px; + } + } +} + +// dropdown +.el-dropdown-menu { + a { + display: block + } +} + +// fix date-picker ui bug in filter-item +.el-range-editor.el-input__inner { + display: inline-flex !important; +} + +// to fix el-date-picker css style +.el-range-separator { + box-sizing: content-box; +} + +.el-menu--collapse + > div + > .el-submenu + > .el-submenu__title + .el-submenu__icon-arrow { + display: none; +} \ No newline at end of file diff --git a/ruoyi-ui/src/assets/styles/element-variables.scss b/ruoyi-ui/src/assets/styles/element-variables.scss new file mode 100644 index 0000000..964174a --- /dev/null +++ b/ruoyi-ui/src/assets/styles/element-variables.scss @@ -0,0 +1,31 @@ +/** +* I think element-ui's default theme color is too light for long-term use. +* So I modified the default color and you can modify it to your liking. +**/ + +/* theme color */ +$--color-primary: #2a7fff; +$--color-success: #5ec468; +$--color-warning: #f97e19; +$--color-danger: #ff4949; +// $--color-info: #1E1E1E; + +$--button-font-weight: 400; + +// $--color-text-regular: #1f2d3d; + +$--border-color-light: #dfe4ed; +$--border-color-lighter: #e6ebf5; + +$--table-border: 1px solid #dfe6ec; + +/* icon font path, required */ +$--font-path: '~element-ui/lib/theme-chalk/fonts'; + +@import "~element-ui/packages/theme-chalk/src/index"; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + theme: $--color-primary; +} diff --git a/ruoyi-ui/src/assets/styles/index.scss b/ruoyi-ui/src/assets/styles/index.scss new file mode 100644 index 0000000..2be1ca7 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/index.scss @@ -0,0 +1,196 @@ +@import './variables.scss'; +@import './mixin.scss'; +@import './transition.scss'; +@import './element-ui.scss'; +@import './sidebar.scss'; +@import './btn.scss'; + +body { + height: 100%; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif; +} + +label { + font-weight: 700; +} + +html { + height: 100%; + box-sizing: border-box; +} + +#app { + height: 100%; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +.no-padding { + padding: 0px !important; +} + +.padding-content { + padding: 4px 0; +} + +a:focus, +a:active { + outline: none; +} + +a, +a:focus, +a:hover { + cursor: pointer; + color: inherit; + text-decoration: none; +} + +div:focus { + outline: none; +} + +.fr { + float: right; +} + +.fl { + float: left; +} + +.pr-5 { + padding-right: 5px; +} + +.pl-5 { + padding-left: 5px; +} + +.block { + display: block; +} + +.pointer { + cursor: pointer; +} + +.inlineBlock { + display: block; +} + +.clearfix { + &:after { + visibility: hidden; + display: block; + font-size: 0; + content: " "; + clear: both; + height: 0; + } +} + +aside { + background: #eef1f6; + padding: 8px 24px; + margin-bottom: 20px; + border-radius: 2px; + display: block; + line-height: 32px; + font-size: 16px; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + color: #2c3e50; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + a { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } + } +} + +//main-container全局样式 +.app-container { + padding: 20px; +} + +.components-container { + margin: 30px 50px; + position: relative; +} + +.text-center { + text-align: center +} + +.sub-navbar { + height: 50px; + line-height: 50px; + position: relative; + width: 100%; + text-align: right; + padding-right: 20px; + transition: 600ms ease position; + background: linear-gradient(90deg, rgba(32, 182, 249, 1) 0%, rgba(32, 182, 249, 1) 0%, rgba(33, 120, 241, 1) 100%, rgba(33, 120, 241, 1) 100%); + + .subtitle { + font-size: 20px; + color: #fff; + } + + &.draft { + background: #d0d0d0; + } + + &.deleted { + background: #d0d0d0; + } +} + +.link-type, +.link-type:focus { + color: #337ab7; + cursor: pointer; + + &:hover { + color: rgb(32, 160, 255); + } +} + +.filter-container { + padding-bottom: 10px; + + .filter-item { + display: inline-block; + vertical-align: middle; + margin-bottom: 10px; + } +} + +// 固定导航栏 +.fixed-header { + position: fixed; + top: 0; + right: 0; + // left: 0; + width: 100%; + z-index: 999; + box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); +} + +// 调整内容区域位置(防止被导航栏遮挡) +.app-main { + padding-top: 78px; // 根据导航栏实际高度调整 + min-height: calc(100vh - 78px); // 保持高度正确 + height: auto; +} diff --git a/ruoyi-ui/src/assets/styles/mixin.scss b/ruoyi-ui/src/assets/styles/mixin.scss new file mode 100644 index 0000000..06fa061 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/mixin.scss @@ -0,0 +1,66 @@ +@mixin clearfix { + &:after { + content: ""; + display: table; + clear: both; + } +} + +@mixin scrollBar { + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } +} + +@mixin relative { + position: relative; + width: 100%; + height: 100%; +} + +@mixin pct($pct) { + width: #{$pct}; + position: relative; + margin: 0 auto; +} + +@mixin triangle($width, $height, $color, $direction) { + $width: $width/2; + $color-border-style: $height solid $color; + $transparent-border-style: $width solid transparent; + height: 0; + width: 0; + + @if $direction==up { + border-bottom: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==right { + border-left: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } + + @else if $direction==down { + border-top: $color-border-style; + border-left: $transparent-border-style; + border-right: $transparent-border-style; + } + + @else if $direction==left { + border-right: $color-border-style; + border-top: $transparent-border-style; + border-bottom: $transparent-border-style; + } +} diff --git a/ruoyi-ui/src/assets/styles/ruoyi.scss b/ruoyi-ui/src/assets/styles/ruoyi.scss new file mode 100644 index 0000000..bf5d06a --- /dev/null +++ b/ruoyi-ui/src/assets/styles/ruoyi.scss @@ -0,0 +1,296 @@ +/** +* 通用css样式布局处理 +* Copyright (c) 2019 ruoyi +*/ + +/** 主题变量 **/ +:root[data-theme='theme-new'] { + --navbar-background: #1897fe; + --navbar-color: #ffffff; + --tags-view-background: #ffffff; + --tags-view-text-color: #495060; + --tags-view-item-background: #ffffff; +} + +/** 基础通用 **/ +.pt5 { + padding-top: 5px; +} + +.pr5 { + padding-right: 5px; +} + +.pb5 { + padding-bottom: 5px; +} + +.mt5 { + margin-top: 5px; +} + +.mr5 { + margin-right: 5px; +} + +.mb5 { + margin-bottom: 5px; +} + +.mb8 { + margin-bottom: 8px; +} + +.ml5 { + margin-left: 5px; +} + +.mt10 { + margin-top: 10px; +} + +.mr10 { + margin-right: 10px; +} + +.mb10 { + margin-bottom: 10px; +} +.ml10 { + margin-left: 10px; +} + +.mt20 { + margin-top: 20px; +} + +.mr20 { + margin-right: 20px; +} + +.mb20 { + margin-bottom: 20px; +} +.ml20 { + margin-left: 20px; +} + +.h1, .h2, .h3, .h4, .h5, .h6, h1, h2, h3, h4, h5, h6 { + font-family: inherit; + font-weight: 500; + line-height: 1.1; + color: inherit; +} + +.el-message-box__status + .el-message-box__message{ + word-break: break-word; +} + +.el-dialog:not(.is-fullscreen) { + margin-top: 6vh !important; +} + +.el-dialog__wrapper.scrollbar .el-dialog .el-dialog__body { + overflow: auto; + overflow-x: hidden; + max-height: 70vh; + padding: 10px 20px 0; +} + +.el-table { + .el-table__header-wrapper, .el-table__fixed-header-wrapper { + th { + word-break: break-word; + background-color: #f8f8f9; + color: #515a6e; + height: 40px; + font-size: 13px; + } + } + + .el-table__body-wrapper { + .el-button [class*="el-icon-"] + span { + margin-left: 1px; + } + } +} + +/** 表单布局 **/ +.form-header { + font-size: 15px; + color: #6379bb; + border-bottom: 1px solid #ddd; + margin: 8px 10px 25px 10px; + padding-bottom: 5px +} + +/** 表格布局 **/ +.pagination-container { + display: flex; + justify-content: flex-end; + margin-top: 20px; +} + +/* tree border */ +.tree-border { + margin-top: 5px; + border: 1px solid #e5e6e7; + background: #FFFFFF none; + border-radius: 4px; +} + +@media (max-width: 768px) { + .pagination-container .el-pagination > .el-pagination__jump { + display: none !important; + } + .pagination-container .el-pagination > .el-pagination__sizes { + display: none !important; + } +} + +.el-table .fixed-width .el-button--mini { + padding-left: 0; + padding-right: 0; + width: inherit; +} + +/** 表格更多操作下拉样式 */ +.el-table .el-dropdown-link,.el-table .el-dropdown-selfdefine { + cursor: pointer; + margin-left: 5px; +} + +.el-table .el-dropdown, .el-icon-arrow-down { + font-size: 12px; +} + +.el-tree-node__content > .el-checkbox { + margin-right: 8px; +} + +.list-group-striped > .list-group-item { + border-left: 0; + border-right: 0; + border-radius: 0; + padding-left: 0; + padding-right: 0; +} + +.list-group { + padding-left: 0px; + list-style: none; +} + +.list-group-item { + border-bottom: 1px solid #e7eaec; + border-top: 1px solid #e7eaec; + margin-bottom: -1px; + padding: 11px 0px; + font-size: 13px; +} + +.pull-right { + float: right !important; +} + +.el-card__header { + padding: 14px 15px 7px; + min-height: 40px; +} + +.el-card__body { + padding: 15px 20px 20px 20px; +} + +.card-box { + margin-bottom: 10px; +} + +/* button color */ +.el-button--cyan.is-active, +.el-button--cyan:active { + background: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +.el-button--cyan:focus, +.el-button--cyan:hover { + background: #48D1CC; + border-color: #48D1CC; + color: #FFFFFF; +} + +.el-button--cyan { + background-color: #20B2AA; + border-color: #20B2AA; + color: #FFFFFF; +} + +/* text color */ +.text-navy { + color: #1ab394; +} + +.text-primary { + color: inherit; +} + +.text-success { + color: #1c84c6; +} + +.text-info { + color: #23c6c8; +} + +.text-warning { + color: #f8ac59; +} + +.text-danger { + color: #ed5565; +} + +.text-muted { + color: #888888; +} + +/* image */ +.img-circle { + border-radius: 50%; +} + +.img-lg { + width: 120px; + height: 120px; +} + +.avatar-upload-preview { + position: relative; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 200px; + height: 200px; + border-radius: 50%; + box-shadow: 0 0 4px #ccc; + overflow: hidden; +} + +/* 拖拽列样式 */ +.sortable-ghost { + opacity: .8; + color: #fff !important; + background: #42b983 !important; +} + +.top-right-btn { + position: relative; + float: right; +} + +/* 分割面板样式 */ +.splitpanes.default-theme .splitpanes__pane { + background-color: #fff!important; +} diff --git a/ruoyi-ui/src/assets/styles/sidebar.scss b/ruoyi-ui/src/assets/styles/sidebar.scss new file mode 100644 index 0000000..d1cb69e --- /dev/null +++ b/ruoyi-ui/src/assets/styles/sidebar.scss @@ -0,0 +1,321 @@ +#app { + + .main-container { + height: 100%; + transition: margin-left .28s; + margin-left: $base-sidebar-width; + position: relative; + } + + .sidebarHide { + margin-left: 0!important; + } + + .sidebar-container { + -webkit-transition: width .28s; + transition: width 0.28s; + width: $base-sidebar-width !important; + background-color: $base-menu-background; + height: 100%; + position: fixed; + font-size: 0px; + top: 0; + bottom: 0; + left: 0; + z-index: 1001; + overflow: hidden; + -webkit-box-shadow: 2px 0 6px rgba(0,21,41,.35); + box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 0.1); + + // reset element-ui css + .horizontal-collapse-transition { + transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; + } + + .scrollbar-wrapper { + overflow-x: hidden !important; + } + + .el-scrollbar__bar.is-vertical { + right: 0px; + } + + .el-scrollbar { + height: 100%; + } + + &.has-logo { + .el-scrollbar { + height: calc(100% - 50px); + } + } + + .is-horizontal { + display: none; + } + + a { + display: inline-block; + width: 100%; + overflow: hidden; + } + + .svg-icon { + margin-right: 16px; + } + + .el-menu { + border: none; + height: 100%; + width: 100% !important; + + [data-theme='theme-light'] & { + .el-menu-item, + .el-submenu__title { + color: rgba(0,0,0,.70); + background-color: #ebebeb; + + &:hover, + &:focus { + color: #2a7ffe !important; + background-color: #ebebeb !important; + } + } + + .el-menu-item.is-active, + .el-submenu.is-active > .el-submenu__title { + color: #2a7ffe !important; + background-color: #ebebeb !important; + } + + .el-submenu .el-menu-item { + background-color: #f5f5f5 !important; + padding-left: 34px !important; + padding-right: 20px !important; + width: calc(100% - 3px) !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + + &:hover, + &:focus { + color: #ffffff !important; + background-color: #2a7ffe !important; + } + + &.is-active { + color: #ffffff !important; + background-color: #2a7ffe !important; + border-left: 3px solid #2a7ffe; + padding-left: 31px !important; + } + } + } + + [data-theme='theme-new'] & { + .el-menu-item, + .el-submenu__title { + color: #495060; + background-color: #ffffff; + + &:hover, + &:focus { + color: #1879da !important; + background-color: #ffffff !important; + } + } + + .el-menu-item.is-active, + .el-submenu.is-active > .el-submenu__title { + color: #1879da !important; + background-color: #ffffff !important; + } + + .el-submenu .el-menu-item { + background-color: #f5f5f5 !important; + padding-left: 34px !important; + padding-right: 20px !important; + width: calc(100% - 3px) !important; + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + + &:hover, + &:focus { + color: #117ae0 !important; + background-color: #e4f5ff !important; + } + + &.is-active { + color: #117ae0 !important; + background-color: #e4f5ff !important; + border-left: 3px solid #117ae0; + padding-left: 31px !important; + } + } + } + } + + .el-menu-item, .el-submenu__title { + overflow: hidden !important; + text-overflow: ellipsis !important; + white-space: nowrap !important; + } + + // menu hover + .submenu-title-noDropdown, + .el-submenu__title { + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .is-active > .el-submenu__title { + color: $base-menu-color-active !important; + background-color: $base-menu-background !important; + } + + & .nest-menu .el-submenu>.el-submenu__title, + & .el-submenu .el-menu-item { + min-width: $base-sidebar-width !important; + + &:hover { + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + & .theme-dark .nest-menu .el-submenu>.el-submenu__title, + & .theme-dark .el-submenu .el-menu-item { + background-color: $base-sub-menu-background !important; + color: $base-menu-color !important; + + &:hover { + background-color: $base-sub-menu-hover !important; + color: $base-sub-menu-hover-color !important; + } + + &.is-active { + background-color: $base-sub-menu-hover !important; + color: $base-sub-menu-hover-color !important; + } + } + } + + .hideSidebar { + .sidebar-container { + width: 54px !important; + } + + .main-container { + margin-left: 54px; + } + + .submenu-title-noDropdown { + padding: 0 !important; + position: relative; + + .el-tooltip { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + } + } + + .el-submenu { + overflow: hidden; + + &>.el-submenu__title { + padding: 0 !important; + + .svg-icon { + margin-left: 20px; + } + + } + } + + .el-menu--collapse { + .el-submenu { + &>.el-submenu__title { + &>span { + height: 0; + width: 0; + overflow: hidden; + visibility: hidden; + display: inline-block; + } + } + } + } + } + + .el-menu--collapse .el-menu .el-submenu { + min-width: $base-sidebar-width !important; + } + + // mobile responsive + .mobile { + .main-container { + margin-left: 0px; + } + + .sidebar-container { + transition: transform .28s; + width: $base-sidebar-width !important; + } + + &.hideSidebar { + .sidebar-container { + pointer-events: none; + transition-duration: 0.3s; + transform: translate3d(-$base-sidebar-width, 0, 0); + } + } + } + + .withoutAnimation { + + .main-container, + .sidebar-container { + transition: none; + } + } +} + +// when menu collapsed +.el-menu--vertical { + &>.el-menu { + .svg-icon { + margin-right: 16px; + } + } + + .nest-menu .el-submenu>.el-submenu__title, + .el-menu-item { + &:hover { + // you can use $subMenuHover + background-color: rgba(0, 0, 0, 0.06) !important; + } + } + + // the scroll bar appears when the subMenu is too long + >.el-menu--popup { + max-height: 100vh; + overflow-y: auto; + + &::-webkit-scrollbar-track-piece { + background: #d3dce6; + } + + &::-webkit-scrollbar { + width: 6px; + } + + &::-webkit-scrollbar-thumb { + background: #99a9bf; + border-radius: 20px; + } + } +} diff --git a/ruoyi-ui/src/assets/styles/transition.scss b/ruoyi-ui/src/assets/styles/transition.scss new file mode 100644 index 0000000..073f8c6 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/transition.scss @@ -0,0 +1,49 @@ +// global transition css + +/* fade */ +.fade-enter-active, +.fade-leave-active { + transition: opacity 0.28s; +} + +.fade-enter, +.fade-leave-active { + opacity: 0; +} + +/* fade-transform */ +.fade-transform--move, +.fade-transform-leave-active, +.fade-transform-enter-active { + transition: all .5s; +} + +.fade-transform-enter { + opacity: 0; + transform: translateX(-30px); +} + +.fade-transform-leave-to { + opacity: 0; + transform: translateX(30px); +} + +/* breadcrumb transition */ +.breadcrumb-enter-active, +.breadcrumb-leave-active { + transition: all .5s; +} + +.breadcrumb-enter, +.breadcrumb-leave-active { + opacity: 0; + transform: translateX(20px); +} + +.breadcrumb-move { + transition: all .5s; +} + +.breadcrumb-leave-active { + position: absolute; +} diff --git a/ruoyi-ui/src/assets/styles/variables.scss b/ruoyi-ui/src/assets/styles/variables.scss new file mode 100644 index 0000000..754ca89 --- /dev/null +++ b/ruoyi-ui/src/assets/styles/variables.scss @@ -0,0 +1,92 @@ +// base color +$blue:#324157; +$light-blue:#3A71A8; +$red:#C03639; +$pink: #E65D6E; +$green: #30B08F; +$tiffany: #4AB7BD; +$yellow:#FEC171; +$panGreen: #30B08F; + +// 默认菜单主题风格 +$base-menu-color:#bfcbd9; +$base-menu-color-active:#007dfe; +$base-menu-background:#2c2e3d; +$base-logo-title-color: #ffffff; +$base-logo-title-background: #2c2e3d; +$base-tags-view-background: #2c2e3d; +$base-tags-view-color: #ffffff; + +$base-sub-menu-background:#383b46d0; +$base-sub-menu-hover:#007dfe; +$base-sub-menu-hover-color:#ffffff; + +// 浅色主题 +$base-menu-light-color:rgba(0,0,0,.70); +$base-menu-light-color-active: #2a7ffe; +$base-menu-light-background:#ebebeb; +$base-logo-light-title-color: #000000; +$base-logo-light-title-background: #ebebeb; +$base-tags-view-light-background: #f6f6f6; +$base-tags-view-light-color: #000000; +$base-sub-menu-light-background: #f5f5f5; +$base-sub-menu-light-hover: #2a7ffe; +$base-sub-menu-light-hover-color: #ffffff; + +// 新主题 +$base-menu-new-color:rgba(0,0,0,.70); +$base-menu-new-background:#ebebeb; +$base-logo-new-title-color: #000000; +$base-logo-new-title-background: #1897fe; +$base-tags-view-new-background: #1897fe; +$base-tags-view-new-color: #ffffff; + + +// 新主题风格 +$base-menu-new-color: $base-menu-light-color; +$base-menu-new-color-active: #1879da; +$base-menu-new-background: #ffffff; +$base-logo-new-title-color: #ffffff; +$base-sub-menu-new-background: #f5f5f5; +$base-nav-new-background: #1897fe; +$base-nav-new-color: #ffffff; + +// 侧边栏基础宽度,可以通过公式 200+8n 进行调整 +$base-sidebar-width: 200px; + +// the :export directive is the magic sauce for webpack +// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass +:export { + menuColor: $base-menu-color; + menuLightColor: $base-menu-light-color; + menuLightColorActive: $base-menu-light-color-active; + menuColorActive: $base-menu-color-active; + subMenuLightBackground: $base-sub-menu-light-background; + subMenuLightHover: $base-sub-menu-light-hover; + subMenuLightHoverColor: $base-sub-menu-light-hover-color; + menuBackground: $base-menu-background; + menuLightBackground: $base-menu-light-background; + subMenuBackground: $base-sub-menu-background; + subMenuHover: $base-sub-menu-hover; + subMenuHoverColor: $base-sub-menu-hover-color; + sideBarWidth: $base-sidebar-width; + logoTitleColor: $base-logo-title-color; + logoLightTitleColor: $base-logo-light-title-color; + // 新主题变量导出 + menuNewColor: $base-menu-new-color; + menuNewColorActive: $base-menu-new-color-active; + menuNewBackground: $base-menu-new-background; + logoNewTitleColor: $base-logo-new-title-color; + logoTitleBackground: $base-logo-title-background; + logoLightTitleBackground: $base-logo-light-title-background; + logoNewTitleBackground: $base-logo-new-title-background; + subMenuNewBackground: $base-sub-menu-new-background; + navNewBackground: $base-nav-new-background; + navNewColor: $base-nav-new-color; + tagsViewBackground: $base-tags-view-background; + tagsViewColor: $base-tags-view-color; + tagsViewLightBackground: $base-tags-view-light-background; + tagsViewLightColor: $base-tags-view-light-color; + tagsViewNewBackground: $base-tags-view-new-background; + tagsViewNewColor: $base-tags-view-new-color +} diff --git a/ruoyi-ui/src/components/Breadcrumb/index.vue b/ruoyi-ui/src/components/Breadcrumb/index.vue new file mode 100644 index 0000000..f348dc9 --- /dev/null +++ b/ruoyi-ui/src/components/Breadcrumb/index.vue @@ -0,0 +1,121 @@ + + + + + diff --git a/ruoyi-ui/src/components/Crontab/day.vue b/ruoyi-ui/src/components/Crontab/day.vue new file mode 100644 index 0000000..d3faed7 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/day.vue @@ -0,0 +1,161 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/hour.vue b/ruoyi-ui/src/components/Crontab/hour.vue new file mode 100644 index 0000000..5659bfb --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/hour.vue @@ -0,0 +1,120 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/index.vue b/ruoyi-ui/src/components/Crontab/index.vue new file mode 100644 index 0000000..395f615 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/index.vue @@ -0,0 +1,430 @@ + + + + diff --git a/ruoyi-ui/src/components/Crontab/min.vue b/ruoyi-ui/src/components/Crontab/min.vue new file mode 100644 index 0000000..ec56a44 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/min.vue @@ -0,0 +1,116 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/Crontab/month.vue b/ruoyi-ui/src/components/Crontab/month.vue new file mode 100644 index 0000000..da60132 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/month.vue @@ -0,0 +1,114 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/result.vue b/ruoyi-ui/src/components/Crontab/result.vue new file mode 100644 index 0000000..4e2952d --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/result.vue @@ -0,0 +1,558 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/second.vue b/ruoyi-ui/src/components/Crontab/second.vue new file mode 100644 index 0000000..9201e06 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/second.vue @@ -0,0 +1,117 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/week.vue b/ruoyi-ui/src/components/Crontab/week.vue new file mode 100644 index 0000000..75c42ed --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/week.vue @@ -0,0 +1,202 @@ + + + diff --git a/ruoyi-ui/src/components/Crontab/year.vue b/ruoyi-ui/src/components/Crontab/year.vue new file mode 100644 index 0000000..8c18c78 --- /dev/null +++ b/ruoyi-ui/src/components/Crontab/year.vue @@ -0,0 +1,131 @@ + + + diff --git a/ruoyi-ui/src/components/DictData/index.js b/ruoyi-ui/src/components/DictData/index.js new file mode 100644 index 0000000..f6c3a2b --- /dev/null +++ b/ruoyi-ui/src/components/DictData/index.js @@ -0,0 +1,57 @@ +import Vue from 'vue' +import store from '@/store' +import DataDict from '@/utils/dict' +import { getDicts as getDicts } from '@/api/system/dict/data' +import i18n from "@/lang"; + +function searchDictByKey(dict, key) { + if (key == null && key == "") { + return null + } + try { + for (let i = 0; i < dict.length; i++) { + if (dict[i].key == key) { + return dict[i].value + } + } + } catch (e) { + return null + } +} + +function install() { + Vue.use(DataDict, { + metas: { + '*': { + labelField: 'dictLabel', + valueField: 'dictValue', + request(dictMeta) { + const storeDict = searchDictByKey(store.getters.dict, dictMeta.type) + if (storeDict) { + return new Promise(resolve => { resolve(storeDict) }) + } else { + return new Promise((resolve, reject) => { + getDicts(dictMeta.type).then(res => { + // store.dispatch('dict/setDict', { key: dictMeta.type, value: res.data }) + // resolve(res.data) + const translatedData = res.data.map(item => ({ + ...item, + dictLabel: i18n.t(item.dictLabel) || item.dictLabel + })) + //console.log(translatedData) + store.dispatch('dict/setDict', { key: dictMeta.type, value: translatedData }) + resolve(translatedData) + }).catch(error => { + reject(error) + }) + }) + } + }, + }, + }, + }) +} + +export default { + install, +} diff --git a/ruoyi-ui/src/components/DictTag/index.vue b/ruoyi-ui/src/components/DictTag/index.vue new file mode 100644 index 0000000..73dcf91 --- /dev/null +++ b/ruoyi-ui/src/components/DictTag/index.vue @@ -0,0 +1,89 @@ + + + + diff --git a/ruoyi-ui/src/components/Editor/index.vue b/ruoyi-ui/src/components/Editor/index.vue new file mode 100644 index 0000000..553f952 --- /dev/null +++ b/ruoyi-ui/src/components/Editor/index.vue @@ -0,0 +1,297 @@ + + + + + diff --git a/ruoyi-ui/src/components/FileUpload/index.vue b/ruoyi-ui/src/components/FileUpload/index.vue new file mode 100644 index 0000000..ca37c55 --- /dev/null +++ b/ruoyi-ui/src/components/FileUpload/index.vue @@ -0,0 +1,262 @@ + + + + + diff --git a/ruoyi-ui/src/components/Hamburger/index.vue b/ruoyi-ui/src/components/Hamburger/index.vue new file mode 100644 index 0000000..31d1863 --- /dev/null +++ b/ruoyi-ui/src/components/Hamburger/index.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/ruoyi-ui/src/components/HeaderSearch/index.vue b/ruoyi-ui/src/components/HeaderSearch/index.vue new file mode 100644 index 0000000..a936bdf --- /dev/null +++ b/ruoyi-ui/src/components/HeaderSearch/index.vue @@ -0,0 +1,264 @@ + + + + + + diff --git a/ruoyi-ui/src/components/IconSelect/index.vue b/ruoyi-ui/src/components/IconSelect/index.vue new file mode 100644 index 0000000..8dadc02 --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/index.vue @@ -0,0 +1,104 @@ + + + + + + diff --git a/ruoyi-ui/src/components/IconSelect/requireIcons.js b/ruoyi-ui/src/components/IconSelect/requireIcons.js new file mode 100644 index 0000000..99e5c54 --- /dev/null +++ b/ruoyi-ui/src/components/IconSelect/requireIcons.js @@ -0,0 +1,11 @@ + +const req = require.context('../../assets/icons/svg', false, /\.svg$/) +const requireAll = requireContext => requireContext.keys() + +const re = /\.\/(.*)\.svg/ + +const icons = requireAll(req).map(i => { + return i.match(re)[1] +}) + +export default icons diff --git a/ruoyi-ui/src/components/ImagePreview/index.vue b/ruoyi-ui/src/components/ImagePreview/index.vue new file mode 100644 index 0000000..8b4738b --- /dev/null +++ b/ruoyi-ui/src/components/ImagePreview/index.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/ruoyi-ui/src/components/ImageUpload/index.vue b/ruoyi-ui/src/components/ImageUpload/index.vue new file mode 100644 index 0000000..6b24f6e --- /dev/null +++ b/ruoyi-ui/src/components/ImageUpload/index.vue @@ -0,0 +1,272 @@ + + + + + diff --git a/ruoyi-ui/src/components/LangSelect/index.vue b/ruoyi-ui/src/components/LangSelect/index.vue new file mode 100644 index 0000000..1b9f16e --- /dev/null +++ b/ruoyi-ui/src/components/LangSelect/index.vue @@ -0,0 +1,41 @@ + + + diff --git a/ruoyi-ui/src/components/Pagination/index.vue b/ruoyi-ui/src/components/Pagination/index.vue new file mode 100644 index 0000000..08ac487 --- /dev/null +++ b/ruoyi-ui/src/components/Pagination/index.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/ruoyi-ui/src/components/PanThumb/index.vue b/ruoyi-ui/src/components/PanThumb/index.vue new file mode 100644 index 0000000..b24e026 --- /dev/null +++ b/ruoyi-ui/src/components/PanThumb/index.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/ruoyi-ui/src/components/ParentView/index.vue b/ruoyi-ui/src/components/ParentView/index.vue new file mode 100644 index 0000000..7bf6148 --- /dev/null +++ b/ruoyi-ui/src/components/ParentView/index.vue @@ -0,0 +1,3 @@ + diff --git a/ruoyi-ui/src/components/RightPanel/index.vue b/ruoyi-ui/src/components/RightPanel/index.vue new file mode 100644 index 0000000..5abeecb --- /dev/null +++ b/ruoyi-ui/src/components/RightPanel/index.vue @@ -0,0 +1,106 @@ + + + + + diff --git a/ruoyi-ui/src/components/RightToolbar/index.vue b/ruoyi-ui/src/components/RightToolbar/index.vue new file mode 100644 index 0000000..7865214 --- /dev/null +++ b/ruoyi-ui/src/components/RightToolbar/index.vue @@ -0,0 +1,154 @@ + + + diff --git a/ruoyi-ui/src/components/RuoYi/Doc/index.vue b/ruoyi-ui/src/components/RuoYi/Doc/index.vue new file mode 100644 index 0000000..75fa864 --- /dev/null +++ b/ruoyi-ui/src/components/RuoYi/Doc/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/RuoYi/Git/index.vue b/ruoyi-ui/src/components/RuoYi/Git/index.vue new file mode 100644 index 0000000..bdafbae --- /dev/null +++ b/ruoyi-ui/src/components/RuoYi/Git/index.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/components/Screenfull/index.vue b/ruoyi-ui/src/components/Screenfull/index.vue new file mode 100644 index 0000000..d4e539c --- /dev/null +++ b/ruoyi-ui/src/components/Screenfull/index.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/ruoyi-ui/src/components/SizeSelect/index.vue b/ruoyi-ui/src/components/SizeSelect/index.vue new file mode 100644 index 0000000..e513948 --- /dev/null +++ b/ruoyi-ui/src/components/SizeSelect/index.vue @@ -0,0 +1,55 @@ + + + diff --git a/ruoyi-ui/src/components/SvgIcon/index.vue b/ruoyi-ui/src/components/SvgIcon/index.vue new file mode 100644 index 0000000..e4bf5ad --- /dev/null +++ b/ruoyi-ui/src/components/SvgIcon/index.vue @@ -0,0 +1,61 @@ + + + + + diff --git a/ruoyi-ui/src/components/ThemePicker/index.vue b/ruoyi-ui/src/components/ThemePicker/index.vue new file mode 100644 index 0000000..a69ee45 --- /dev/null +++ b/ruoyi-ui/src/components/ThemePicker/index.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/ruoyi-ui/src/components/TopNav/index.vue b/ruoyi-ui/src/components/TopNav/index.vue new file mode 100644 index 0000000..b3f3331 --- /dev/null +++ b/ruoyi-ui/src/components/TopNav/index.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/ruoyi-ui/src/components/iFrame/index.vue b/ruoyi-ui/src/components/iFrame/index.vue new file mode 100644 index 0000000..f4334f3 --- /dev/null +++ b/ruoyi-ui/src/components/iFrame/index.vue @@ -0,0 +1,36 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Navbar.vue b/ruoyi-ui/src/layout/components/Navbar.vue new file mode 100644 index 0000000..5646eac --- /dev/null +++ b/ruoyi-ui/src/layout/components/Navbar.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Settings/index.vue b/ruoyi-ui/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..2485eb8 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Settings/index.vue @@ -0,0 +1,274 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..6823726 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,25 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/ruoyi-ui/src/layout/components/Sidebar/Item.vue b/ruoyi-ui/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..be3285d --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,33 @@ + diff --git a/ruoyi-ui/src/layout/components/Sidebar/Link.vue b/ruoyi-ui/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..8b0bc93 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,43 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/Logo.vue b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..6e7adb8 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..bddb65f --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,99 @@ + + + diff --git a/ruoyi-ui/src/layout/components/Sidebar/index.vue b/ruoyi-ui/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..13d8e82 --- /dev/null +++ b/ruoyi-ui/src/layout/components/Sidebar/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..bb753a1 --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/ruoyi-ui/src/layout/components/TagsView/index.vue b/ruoyi-ui/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..f870e0d --- /dev/null +++ b/ruoyi-ui/src/layout/components/TagsView/index.vue @@ -0,0 +1,357 @@ + + + + + + + diff --git a/ruoyi-ui/src/layout/components/index.js b/ruoyi-ui/src/layout/components/index.js new file mode 100644 index 0000000..104bd3a --- /dev/null +++ b/ruoyi-ui/src/layout/components/index.js @@ -0,0 +1,5 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/ruoyi-ui/src/layout/index.vue b/ruoyi-ui/src/layout/index.vue new file mode 100644 index 0000000..05e2fa9 --- /dev/null +++ b/ruoyi-ui/src/layout/index.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/ruoyi-ui/src/layout/mixin/ResizeHandler.js b/ruoyi-ui/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/ruoyi-ui/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} diff --git a/ruoyi-ui/src/main.js b/ruoyi-ui/src/main.js new file mode 100644 index 0000000..b13a2e6 --- /dev/null +++ b/ruoyi-ui/src/main.js @@ -0,0 +1,86 @@ +import Vue from 'vue' + +import Cookies from 'js-cookie' + +import Element from 'element-ui' +import './assets/styles/element-variables.scss' + +import '@/assets/styles/index.scss' // global css +import '@/assets/styles/ruoyi.scss' // ruoyi css +import App from './App' +import store from './store' +import router from './router' +import directive from './directive' // directive +import plugins from './plugins' // plugins +import { download } from '@/utils/request' + +import './assets/icons' // icon +import './permission' // permission control +import { getDicts } from "@/api/system/dict/data" +import { getConfigKey } from "@/api/system/config" +import { parseTime, resetForm, addDateRange, selectDictLabel, selectDictLabels, handleTree } from "@/utils/ruoyi" +import i18n from './lang' +// 分页组件 +import Pagination from "@/components/Pagination" +// 自定义表格工具组件 +import RightToolbar from "@/components/RightToolbar" +// 富文本组件 +import Editor from "@/components/Editor" +// 文件上传组件 +import FileUpload from "@/components/FileUpload" +// 图片上传组件 +import ImageUpload from "@/components/ImageUpload" +// 图片预览组件 +import ImagePreview from "@/components/ImagePreview" +// 字典标签组件 +import DictTag from '@/components/DictTag' +// 字典数据组件 +import DictData from '@/components/DictData' + +// 全局方法挂载 +Vue.prototype.getDicts = getDicts +Vue.prototype.getConfigKey = getConfigKey +Vue.prototype.parseTime = parseTime +Vue.prototype.resetForm = resetForm +Vue.prototype.addDateRange = addDateRange +Vue.prototype.selectDictLabel = selectDictLabel +Vue.prototype.selectDictLabels = selectDictLabels +Vue.prototype.download = download +Vue.prototype.handleTree = handleTree + +// 全局组件挂载 +Vue.component('DictTag', DictTag) +Vue.component('Pagination', Pagination) +Vue.component('RightToolbar', RightToolbar) +Vue.component('Editor', Editor) +Vue.component('FileUpload', FileUpload) +Vue.component('ImageUpload', ImageUpload) +Vue.component('ImagePreview', ImagePreview) + +Vue.use(directive) +Vue.use(plugins) +DictData.install() + +/** + * If you don't want to use mock-server + * you want to use MockJs for mock api + * you can execute: mockXHR() + * + * Currently MockJs will be used in the production environment, + * please remove it before going online! ! ! + */ + +Vue.use(Element, { + i18n: (key, value) => i18n.t(key, value), + size: Cookies.get('size') || 'medium' +}) + +Vue.config.productionTip = false + +new Vue({ + el: '#app', + router, + store, + i18n, + render: h => h(App) +}) diff --git a/ruoyi-ui/src/permission.js b/ruoyi-ui/src/permission.js new file mode 100644 index 0000000..b66190b --- /dev/null +++ b/ruoyi-ui/src/permission.js @@ -0,0 +1,63 @@ +import router from './router' +import store from './store' +import { Message } from 'element-ui' +import NProgress from 'nprogress' +import 'nprogress/nprogress.css' +import { getToken } from '@/utils/auth' +import { isPathMatch } from '@/utils/validate' +import { isRelogin } from '@/utils/request' + +NProgress.configure({ showSpinner: false }) + +const whiteList = ['/login', '/register'] + +const isWhiteList = (path) => { + return whiteList.some(pattern => isPathMatch(pattern, path)) +} + +router.beforeEach((to, from, next) => { + NProgress.start() + if (getToken()) { + to.meta.title && store.dispatch('settings/setTitle', to.meta.title) + /* has token*/ + if (to.path === '/login') { + next({ path: '/' }) + NProgress.done() + } else if (isWhiteList(to.path)) { + next() + } else { + if (store.getters.roles.length === 0) { + isRelogin.show = true + // 判断当前用户是否已拉取完user_info信息 + store.dispatch('GetInfo').then(() => { + isRelogin.show = false + store.dispatch('GenerateRoutes').then(accessRoutes => { + // 根据roles权限生成可访问的路由表 + router.addRoutes(accessRoutes) // 动态添加可访问路由表 + next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 + }) + }).catch(err => { + store.dispatch('LogOut').then(() => { + Message.error(err) + next({ path: '/' }) + }) + }) + } else { + next() + } + } + } else { + // 没有token + if (isWhiteList(to.path)) { + // 在免登录白名单,直接进入 + next() + } else { + next(`/login?redirect=${encodeURIComponent(to.fullPath)}`) // 否则全部重定向到登录页 + NProgress.done() + } + } +}) + +router.afterEach(() => { + NProgress.done() +}) diff --git a/ruoyi-ui/src/plugins/LodopFuncs.js b/ruoyi-ui/src/plugins/LodopFuncs.js new file mode 100644 index 0000000..c86ac7b --- /dev/null +++ b/ruoyi-ui/src/plugins/LodopFuncs.js @@ -0,0 +1,188 @@ +//==本JS是加载Lodop插件或Web打印服务CLodop/Lodop7的综合示例,可直接使用,建议理解后融入自己程序== + +//用双端口加载主JS文件Lodop.js(或CLodopfuncs.js兼容老版本)以防其中某端口被占: +var MainJS ="CLodopfuncs.js", + URL_WS1 = "ws://localhost:8000/"+MainJS, //ws用8000/18000 + URL_WS2 = "ws://localhost:18000/"+MainJS, + URL_HTTP1 = "http://localhost:8000/"+MainJS, //http用8000/18000 + URL_HTTP2 = "http://localhost:18000/"+MainJS, + URL_HTTP3 = "https://localhost.lodop.net:8443/"+MainJS; //https用8000/8443 + +var CreatedOKLodopObject, CLodopIsLocal, LoadJsState; + +//==判断是否需要CLodop(那些不支持插件的浏览器):== +function needCLodop() { + try { + var ua = navigator.userAgent; + if (ua.match(/Windows\sPhone/i) || + ua.match(/iPhone|iPod|iPad/i) || + ua.match(/Android/i) || + ua.match(/Edge\D?\d+/i)) + return true; + var verTrident = ua.match(/Trident\D?\d+/i); + var verIE = ua.match(/MSIE\D?\d+/i); + var verOPR = ua.match(/OPR\D?\d+/i); + var verFF = ua.match(/Firefox\D?\d+/i); + var x64 = ua.match(/x64/i); + if ((!verTrident) && (!verIE) && (x64)) return true; + else if (verFF) { + verFF = verFF[0].match(/\d+/); + if ((verFF[0] >= 41) || (x64)) return true; + } else if (verOPR) { + verOPR = verOPR[0].match(/\d+/); + if (verOPR[0] >= 32) return true; + } else if ((!verTrident) && (!verIE)) { + var verChrome = ua.match(/Chrome\D?\d+/i); + if (verChrome) { + verChrome = verChrome[0].match(/\d+/); + if (verChrome[0] >= 41) return true; + } + } + return false; + } catch (err) { + return true; + } +} + +//==检查加载成功与否,如没成功则用http(s)再试== +//==低版本CLODOP6.561/Lodop7.043及前)用本方法== +function checkOrTryHttp() { + if (window.getCLodop) { + LoadJsState = "complete"; + return true; + } + if (LoadJsState == "loadingB" || LoadJsState == "complete") return; + LoadJsState = "loadingB"; + var head = document.head || document.getElementsByTagName("head")[0] || document.documentElement; + var JS1 = document.createElement("script") + ,JS2 = document.createElement("script") + ,JS3 = document.createElement("script"); + JS1.src = URL_HTTP1; + JS2.src = URL_HTTP2; + JS3.src = URL_HTTP3; + JS1.onload = JS2.onload = JS3.onload = JS2.onerror = JS3.onerror=function(){LoadJsState = "complete";} + JS1.onerror = function(e) { + if (window.location.protocol !== 'https:') + head.insertBefore(JS2, head.firstChild); else + head.insertBefore(JS3, head.firstChild); + } + head.insertBefore(JS1,head.firstChild); +} + +//==加载Lodop对象的主过程:== +(function loadCLodop(){ + if (!needCLodop()) return; + CLodopIsLocal = !!((URL_WS1 + URL_WS2).match(/\/\/localho|\/\/127.0.0./i)); + LoadJsState = "loadingA"; + if (!window.WebSocket && window.MozWebSocket) window.WebSocket=window.MozWebSocket; + //ws方式速度快(小于200ms)且可避免CORS错误,但要求Lodop版本足够新: + try { + var WSK1=new WebSocket(URL_WS1); + WSK1.onopen = function(e) { setTimeout("checkOrTryHttp()",200); } + WSK1.onmessage = function(e) {if (!window.getCLodop) eval(e.data);} + WSK1.onerror = function(e) { + var WSK2=new WebSocket(URL_WS2); + WSK2.onopen = function(e) {setTimeout("checkOrTryHttp()",200);} + WSK2.onmessage = function(e) {if (!window.getCLodop) eval(e.data);} + WSK2.onerror= function(e) {checkOrTryHttp();} + } + } catch(e){ + checkOrTryHttp(); + } +})(); + +//==获取LODOP对象主过程,判断是否安装、需否升级:== +export function getLodop(oOBJECT, oEMBED) { + var strFontTag = "
打印控件"; + var strLodopInstall = strFontTag + "未安装!点击这里执行安装"; + var strLodopUpdate = strFontTag + "需要升级!点击这里执行升级"; + var strLodop64Install = strFontTag + "未安装!点击这里执行安装"; + var strLodop64Update = strFontTag + "需要升级!点击这里执行升级"; + var strCLodopInstallA = "
Web打印服务CLodop未安装启动,点击这里下载执行安装"; + var strCLodopInstallB = "
(若此前已安装过,可点这里直接再次启动)"; + var strCLodopUpdate = "
Web打印服务CLodop需升级!点击这里执行升级"; + var strLodop7FontTag = "
Web打印服务Lodop7"; + var strLodop7HrefX86 = "点击这里下载安装(下载后解压,点击lodop文件开始执行)"; + var strLodop7HrefARM = "点击这里下载安装(下载后解压,点击lodop文件开始执行)"; + var strLodop7Install_X86 = strLodop7FontTag + "未安装启动," + strLodop7HrefX86; + var strLodop7Install_ARM = strLodop7FontTag + "未安装启动," + strLodop7HrefARM; + var strLodop7Update_X86 = strLodop7FontTag + "需升级," + strLodop7HrefX86; + var strLodop7Update_ARM = strLodop7FontTag + "需升级," + strLodop7HrefARM; + var strInstallOK = ",成功后请刷新本页面或重启浏览器。"; + var LODOP; + try { + var isWinIE = (/MSIE/i.test(navigator.userAgent)) || (/Trident/i.test(navigator.userAgent)); + var isWinIE64 = isWinIE && (/x64/i.test(navigator.userAgent)); + var isLinuxX86 = (/Linux/i.test(navigator.platform)) && (/x86/i.test(navigator.platform)); + var isLinuxARM = (/Linux/i.test(navigator.platform)) && (/aarch/i.test(navigator.platform)); + + if (needCLodop() || isLinuxX86 || isLinuxARM) { + try { + LODOP = window.getCLodop(); + } catch (err) {} + if (!LODOP && LoadJsState !== "complete") { + if (!LoadJsState) + alert("未曾加载Lodop主JS文件,请先调用loadCLodop过程."); else + alert("网页还没下载完毕,请稍等一下再操作."); + return; + } + var strAlertMessage; + if (!LODOP) { + if (isLinuxX86) + strAlertMessage = strLodop7Install_X86; + else if (isLinuxARM) + strAlertMessage = strLodop7Install_ARM; + else + strAlertMessage = strCLodopInstallA + (CLodopIsLocal ? strCLodopInstallB : ""); + document.body.innerHTML = strAlertMessage + strInstallOK + document.body.innerHTML; + return; + } else { + if (isLinuxX86 && LODOP.CVERSION < "7.1.0.1") + strAlertMessage = strLodop7Update_X86; + else if (isLinuxARM && LODOP.CVERSION < "7.1.0.1") + strAlertMessage = strLodop7Update_ARM; + else if (CLODOP.CVERSION < "6.6.2.3") + strAlertMessage = strCLodopUpdate; + + if (strAlertMessage) + document.body.innerHTML = strAlertMessage + strInstallOK + document.body.innerHTML; + } + } else { + //==如果页面有Lodop插件就直接使用,否则新建:== + if (oOBJECT || oEMBED) { + if (isWinIE) + LODOP = oOBJECT; + else + LODOP = oEMBED; + } else if (!CreatedOKLodopObject) { + LODOP = document.createElement("object"); + LODOP.setAttribute("width", 0); + LODOP.setAttribute("height", 0); + LODOP.setAttribute("style", "position:absolute;left:0px;top:-100px;width:0px;height:0px;"); + if (isWinIE) + LODOP.setAttribute("classid", "clsid:2105C259-1E0C-4534-8141-A753534CB4CA"); + else + LODOP.setAttribute("type", "application/x-print-lodop"); + document.documentElement.appendChild(LODOP); + CreatedOKLodopObject = LODOP; + } else + LODOP = CreatedOKLodopObject; + //==Lodop插件未安装时提示下载地址:== + if ((!LODOP) || (!LODOP.VERSION)) { + document.body.innerHTML = (isWinIE64 ? strLodop64Install : strLodopInstall) + strInstallOK + document.body.innerHTML; + return LODOP; + } + if (LODOP.VERSION < "6.2.2.6") { + document.body.innerHTML = (isWinIE64 ? strLodop64Update : strLodopUpdate) + strInstallOK + document.body.innerHTML; + } + } + //===如下空白位置适合调用统一功能(如注册语句、语言选择等):======================= + + + //=============================================================================== + return LODOP; + } catch (err) { + alert("getLodop出错:" + err); + } +} + diff --git a/ruoyi-ui/src/plugins/auth.js b/ruoyi-ui/src/plugins/auth.js new file mode 100644 index 0000000..3b91c14 --- /dev/null +++ b/ruoyi-ui/src/plugins/auth.js @@ -0,0 +1,60 @@ +import store from '@/store' + +function authPermission(permission) { + const all_permission = "*:*:*" + const permissions = store.getters && store.getters.permissions + if (permission && permission.length > 0) { + return permissions.some(v => { + return all_permission === v || v === permission + }) + } else { + return false + } +} + +function authRole(role) { + const super_admin = "admin" + const roles = store.getters && store.getters.roles + if (role && role.length > 0) { + return roles.some(v => { + return super_admin === v || v === role + }) + } else { + return false + } +} + +export default { + // 验证用户是否具备某权限 + hasPermi(permission) { + return authPermission(permission) + }, + // 验证用户是否含有指定权限,只需包含其中一个 + hasPermiOr(permissions) { + return permissions.some(item => { + return authPermission(item) + }) + }, + // 验证用户是否含有指定权限,必须全部拥有 + hasPermiAnd(permissions) { + return permissions.every(item => { + return authPermission(item) + }) + }, + // 验证用户是否具备某角色 + hasRole(role) { + return authRole(role) + }, + // 验证用户是否含有指定角色,只需包含其中一个 + hasRoleOr(roles) { + return roles.some(item => { + return authRole(item) + }) + }, + // 验证用户是否含有指定角色,必须全部拥有 + hasRoleAnd(roles) { + return roles.every(item => { + return authRole(item) + }) + } +} diff --git a/ruoyi-ui/src/plugins/cache.js b/ruoyi-ui/src/plugins/cache.js new file mode 100644 index 0000000..ea8a53f --- /dev/null +++ b/ruoyi-ui/src/plugins/cache.js @@ -0,0 +1,79 @@ +const sessionCache = { + set (key, value) { + if (!sessionStorage) { + return + } + if (key != null && value != null) { + sessionStorage.setItem(key, value) + } + }, + get (key) { + if (!sessionStorage) { + return null + } + if (key == null) { + return null + } + return sessionStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key) { + sessionStorage.removeItem(key) + } +} +const localCache = { + set (key, value) { + if (!localStorage) { + return + } + if (key != null && value != null) { + localStorage.setItem(key, value) + } + }, + get (key) { + if (!localStorage) { + return null + } + if (key == null) { + return null + } + return localStorage.getItem(key) + }, + setJSON (key, jsonValue) { + if (jsonValue != null) { + this.set(key, JSON.stringify(jsonValue)) + } + }, + getJSON (key) { + const value = this.get(key) + if (value != null) { + return JSON.parse(value) + } + return null + }, + remove (key) { + localStorage.removeItem(key) + } +} + +export default { + /** + * 会话级缓存 + */ + session: sessionCache, + /** + * 本地缓存 + */ + local: localCache +} diff --git a/ruoyi-ui/src/plugins/download.js b/ruoyi-ui/src/plugins/download.js new file mode 100644 index 0000000..8dc83ea --- /dev/null +++ b/ruoyi-ui/src/plugins/download.js @@ -0,0 +1,79 @@ +import axios from 'axios' +import {Loading, Message} from 'element-ui' +import { saveAs } from 'file-saver' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { blobValidate } from "@/utils/ruoyi" + +const baseURL = process.env.VUE_APP_BASE_API +let downloadLoadingInstance + +export default { + name(name, isDelete = true) { + var url = baseURL + "/common/download?fileName=" + encodeURIComponent(name) + "&delete=" + isDelete + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data) + } + }) + }, + resource(resource) { + var url = baseURL + "/common/download/resource?resource=" + encodeURIComponent(resource) + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data]) + this.saveAs(blob, decodeURIComponent(res.headers['download-filename'])) + } else { + this.printErrMsg(res.data) + } + }) + }, + zip(url, name) { + var url = baseURL + url + downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + axios({ + method: 'get', + url: url, + responseType: 'blob', + headers: { 'Authorization': 'Bearer ' + getToken() } + }).then((res) => { + const isBlob = blobValidate(res.data) + if (isBlob) { + const blob = new Blob([res.data], { type: 'application/zip' }) + this.saveAs(blob, name) + } else { + this.printErrMsg(res.data) + } + downloadLoadingInstance.close() + }).catch((r) => { + console.error(r) + Message.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close() + }) + }, + saveAs(text, name, opts) { + saveAs(text, name, opts) + }, + async printErrMsg(data) { + const resText = await data.text() + const rspObj = JSON.parse(resText) + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg) + } +} + diff --git a/ruoyi-ui/src/plugins/echarts.min.js b/ruoyi-ui/src/plugins/echarts.min.js new file mode 100644 index 0000000..3f86a0f --- /dev/null +++ b/ruoyi-ui/src/plugins/echarts.min.js @@ -0,0 +1,25224 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you 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. +*/ + + + +!function (t, e) { + "object" == typeof exports && "undefined" != typeof module ? e(exports) : "function" == typeof define && define.amd ? define(["exports"], e) : e(t.echarts = {}) +}(this, function (t) { + "use strict"; + + function e(t, e) { + "createCanvas" === t && (v_ = null), g_[t] = e + } + + function i(t) { + if (null == t || "object" != typeof t) return t; + var e = t, n = l_.call(t); + if ("[object Array]" === n) { + if (!O(t)) { + e = []; + for (var o = 0, a = t.length; o < a; o++) e[o] = i(t[o]) + } + } else if (s_[n]) { + if (!O(t)) { + var r = t.constructor; + if (t.constructor.from) e = r.from(t); else { + e = new r(t.length); + for (var o = 0, a = t.length; o < a; o++) e[o] = i(t[o]) + } + } + } else if (!r_[n] && !O(t) && !M(t)) { + e = {}; + for (var s in t) t.hasOwnProperty(s) && (e[s] = i(t[s])) + } + return e + } + + function n(t, e, o) { + if (!w(e) || !w(t)) return o ? i(e) : t; + for (var a in e) if (e.hasOwnProperty(a)) { + var r = t[a], s = e[a]; + !w(s) || !w(r) || y(s) || y(r) || M(s) || M(r) || b(s) || b(r) || O(s) || O(r) ? !o && a in t || (t[a] = i(e[a], !0)) : n(r, s, o) + } + return t + } + + function o(t, e) { + for (var i = t[0], o = 1, a = t.length; o < a; o++) i = n(i, t[o], e); + return i + } + + function a(t, e) { + for (var i in e) e.hasOwnProperty(i) && (t[i] = e[i]); + return t + } + + function r(t, e, i) { + for (var n in e) e.hasOwnProperty(n) && (i ? null != e[n] : null == t[n]) && (t[n] = e[n]); + return t + } + + function s() { + return v_ || (v_ = m_().getContext("2d")), v_ + } + + function l(t, e) { + if (t) { + if (t.indexOf) return t.indexOf(e); + for (var i = 0, n = t.length; i < n; i++) if (t[i] === e) return i + } + return -1 + } + + function u(t, e) { + function i() { + } + + var n = t.prototype; + i.prototype = e.prototype, t.prototype = new i; + for (var o in n) t.prototype[o] = n[o]; + t.prototype.constructor = t, t.superClass = e + } + + function h(t, e, i) { + r(t = "prototype" in t ? t.prototype : t, e = "prototype" in e ? e.prototype : e, i) + } + + function c(t) { + if (t) return "string" != typeof t && "number" == typeof t.length + } + + function d(t, e, i) { + if (t && e) if (t.forEach && t.forEach === h_) t.forEach(e, i); else if (t.length === +t.length) for (var n = 0, o = t.length; n < o; n++) e.call(i, t[n], n, t); else for (var a in t) t.hasOwnProperty(a) && e.call(i, t[a], a, t) + } + + function f(t, e, i) { + if (t && e) { + if (t.map && t.map === f_) return t.map(e, i); + for (var n = [], o = 0, a = t.length; o < a; o++) n.push(e.call(i, t[o], o, t)); + return n + } + } + + function p(t, e, i, n) { + if (t && e) { + if (t.reduce && t.reduce === p_) return t.reduce(e, i, n); + for (var o = 0, a = t.length; o < a; o++) i = e.call(n, i, t[o], o, t); + return i + } + } + + function g(t, e, i) { + if (t && e) { + if (t.filter && t.filter === c_) return t.filter(e, i); + for (var n = [], o = 0, a = t.length; o < a; o++) e.call(i, t[o], o, t) && n.push(t[o]); + return n + } + } + + function m(t, e) { + var i = d_.call(arguments, 2); + return function () { + return t.apply(e, i.concat(d_.call(arguments))) + } + } + + function v(t) { + var e = d_.call(arguments, 1); + return function () { + return t.apply(this, e.concat(d_.call(arguments))) + } + } + + function y(t) { + return "[object Array]" === l_.call(t) + } + + function x(t) { + return "function" == typeof t + } + + function _(t) { + return "[object String]" === l_.call(t) + } + + function w(t) { + var e = typeof t; + return "function" === e || !!t && "object" == e + } + + function b(t) { + return !!r_[l_.call(t)] + } + + function S(t) { + return !!s_[l_.call(t)] + } + + function M(t) { + return "object" == typeof t && "number" == typeof t.nodeType && "object" == typeof t.ownerDocument + } + + function I(t) { + return t !== t + } + + function T(t) { + for (var e = 0, i = arguments.length; e < i; e++) if (null != arguments[e]) return arguments[e] + } + + function D(t, e) { + return null != t ? t : e + } + + function A(t, e, i) { + return null != t ? t : null != e ? e : i + } + + function C() { + return Function.call.apply(d_, arguments) + } + + function L(t) { + if ("number" == typeof t) return [t, t, t, t]; + var e = t.length; + return 2 === e ? [t[0], t[1], t[0], t[1]] : 3 === e ? [t[0], t[1], t[2], t[1]] : t + } + + function k(t, e) { + if (!t) throw new Error(e) + } + + function P(t) { + return null == t ? null : "function" == typeof t.trim ? t.trim() : t.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "") + } + + function N(t) { + t[y_] = !0 + } + + function O(t) { + return t[y_] + } + + function E(t) { + function e(t, e) { + i ? n.set(t, e) : n.set(e, t) + } + + var i = y(t), n = this; + t instanceof E ? t.each(e) : t && d(t, e) + } + + function R(t) { + return new E(t) + } + + function z(t, e) { + for (var i = new t.constructor(t.length + e.length), n = 0; n < t.length; n++) i[n] = t[n]; + var o = t.length; + for (n = 0; n < e.length; n++) i[n + o] = e[n]; + return i + } + + function B() { + } + + function V(t, e) { + var i = new __(2); + return null == t && (t = 0), null == e && (e = 0), i[0] = t, i[1] = e, i + } + + function G(t, e) { + return t[0] = e[0], t[1] = e[1], t + } + + function W(t) { + var e = new __(2); + return e[0] = t[0], e[1] = t[1], e + } + + function F(t, e, i) { + return t[0] = e, t[1] = i, t + } + + function H(t, e, i) { + return t[0] = e[0] + i[0], t[1] = e[1] + i[1], t + } + + function Z(t, e, i, n) { + return t[0] = e[0] + i[0] * n, t[1] = e[1] + i[1] * n, t + } + + function U(t, e, i) { + return t[0] = e[0] - i[0], t[1] = e[1] - i[1], t + } + + function X(t) { + return Math.sqrt(j(t)) + } + + function j(t) { + return t[0] * t[0] + t[1] * t[1] + } + + function Y(t, e, i) { + return t[0] = e[0] * i, t[1] = e[1] * i, t + } + + function q(t, e) { + var i = X(e); + return 0 === i ? (t[0] = 0, t[1] = 0) : (t[0] = e[0] / i, t[1] = e[1] / i), t + } + + function K(t, e) { + return Math.sqrt((t[0] - e[0]) * (t[0] - e[0]) + (t[1] - e[1]) * (t[1] - e[1])) + } + + function $(t, e) { + return (t[0] - e[0]) * (t[0] - e[0]) + (t[1] - e[1]) * (t[1] - e[1]) + } + + function J(t, e, i, n) { + return t[0] = e[0] + n * (i[0] - e[0]), t[1] = e[1] + n * (i[1] - e[1]), t + } + + function Q(t, e, i) { + var n = e[0], o = e[1]; + return t[0] = i[0] * n + i[2] * o + i[4], t[1] = i[1] * n + i[3] * o + i[5], t + } + + function tt(t, e, i) { + return t[0] = Math.min(e[0], i[0]), t[1] = Math.min(e[1], i[1]), t + } + + function et(t, e, i) { + return t[0] = Math.max(e[0], i[0]), t[1] = Math.max(e[1], i[1]), t + } + + function it() { + this.on("mousedown", this._dragStart, this), this.on("mousemove", this._drag, this), this.on("mouseup", this._dragEnd, this), this.on("globalout", this._dragEnd, this) + } + + function nt(t, e) { + return {target: t, topTarget: e && e.topTarget} + } + + function ot(t, e, i) { + return { + type: t, + event: i, + target: e.target, + topTarget: e.topTarget, + cancelBubble: !1, + offsetX: i.zrX, + offsetY: i.zrY, + gestureEvent: i.gestureEvent, + pinchX: i.pinchX, + pinchY: i.pinchY, + pinchScale: i.pinchScale, + wheelDelta: i.zrDelta, + zrByTouch: i.zrByTouch, + which: i.which + } + } + + function at() { + } + + function rt(t, e, i) { + if (t[t.rectHover ? "rectContain" : "contain"](e, i)) { + for (var n, o = t; o;) { + if (o.clipPath && !o.clipPath.contain(e, i)) return !1; + o.silent && (n = !0), o = o.parent + } + return !n || A_ + } + return !1 + } + + function st() { + var t = new k_(6); + return lt(t), t + } + + function lt(t) { + return t[0] = 1, t[1] = 0, t[2] = 0, t[3] = 1, t[4] = 0, t[5] = 0, t + } + + function ut(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4], t[5] = e[5], t + } + + function ht(t, e, i) { + var n = e[0] * i[0] + e[2] * i[1], o = e[1] * i[0] + e[3] * i[1], a = e[0] * i[2] + e[2] * i[3], + r = e[1] * i[2] + e[3] * i[3], s = e[0] * i[4] + e[2] * i[5] + e[4], l = e[1] * i[4] + e[3] * i[5] + e[5]; + return t[0] = n, t[1] = o, t[2] = a, t[3] = r, t[4] = s, t[5] = l, t + } + + function ct(t, e, i) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t[4] = e[4] + i[0], t[5] = e[5] + i[1], t + } + + function dt(t, e, i) { + var n = e[0], o = e[2], a = e[4], r = e[1], s = e[3], l = e[5], u = Math.sin(i), h = Math.cos(i); + return t[0] = n * h + r * u, t[1] = -n * u + r * h, t[2] = o * h + s * u, t[3] = -o * u + h * s, t[4] = h * a + u * l, t[5] = h * l - u * a, t + } + + function ft(t, e, i) { + var n = i[0], o = i[1]; + return t[0] = e[0] * n, t[1] = e[1] * o, t[2] = e[2] * n, t[3] = e[3] * o, t[4] = e[4] * n, t[5] = e[5] * o, t + } + + function pt(t, e) { + var i = e[0], n = e[2], o = e[4], a = e[1], r = e[3], s = e[5], l = i * r - a * n; + return l ? (l = 1 / l, t[0] = r * l, t[1] = -a * l, t[2] = -n * l, t[3] = i * l, t[4] = (n * s - r * o) * l, t[5] = (a * o - i * s) * l, t) : null + } + + function gt(t) { + var e = st(); + return ut(e, t), e + } + + function mt(t) { + return t > O_ || t < -O_ + } + + function vt(t) { + this._target = t.target, this._life = t.life || 1e3, this._delay = t.delay || 0, this._initialized = !1, this.loop = null != t.loop && t.loop, this.gap = t.gap || 0, this.easing = t.easing || "Linear", this.onframe = t.onframe, this.ondestroy = t.ondestroy, this.onrestart = t.onrestart, this._pausedTime = 0, this._paused = !1 + } + + function yt(t) { + return (t = Math.round(t)) < 0 ? 0 : t > 255 ? 255 : t + } + + function xt(t) { + return (t = Math.round(t)) < 0 ? 0 : t > 360 ? 360 : t + } + + function _t(t) { + return t < 0 ? 0 : t > 1 ? 1 : t + } + + function wt(t) { + return yt(t.length && "%" === t.charAt(t.length - 1) ? parseFloat(t) / 100 * 255 : parseInt(t, 10)) + } + + function bt(t) { + return _t(t.length && "%" === t.charAt(t.length - 1) ? parseFloat(t) / 100 : parseFloat(t)) + } + + function St(t, e, i) { + return i < 0 ? i += 1 : i > 1 && (i -= 1), 6 * i < 1 ? t + (e - t) * i * 6 : 2 * i < 1 ? e : 3 * i < 2 ? t + (e - t) * (2 / 3 - i) * 6 : t + } + + function Mt(t, e, i) { + return t + (e - t) * i + } + + function It(t, e, i, n, o) { + return t[0] = e, t[1] = i, t[2] = n, t[3] = o, t + } + + function Tt(t, e) { + return t[0] = e[0], t[1] = e[1], t[2] = e[2], t[3] = e[3], t + } + + function Dt(t, e) { + X_ && Tt(X_, e), X_ = U_.put(t, X_ || e.slice()) + } + + function At(t, e) { + if (t) { + e = e || []; + var i = U_.get(t); + if (i) return Tt(e, i); + var n = (t += "").replace(/ /g, "").toLowerCase(); + if (n in Z_) return Tt(e, Z_[n]), Dt(t, e), e; + if ("#" !== n.charAt(0)) { + var o = n.indexOf("("), a = n.indexOf(")"); + if (-1 !== o && a + 1 === n.length) { + var r = n.substr(0, o), s = n.substr(o + 1, a - (o + 1)).split(","), l = 1; + switch (r) { + case"rgba": + if (4 !== s.length) return void It(e, 0, 0, 0, 1); + l = bt(s.pop()); + case"rgb": + return 3 !== s.length ? void It(e, 0, 0, 0, 1) : (It(e, wt(s[0]), wt(s[1]), wt(s[2]), l), Dt(t, e), e); + case"hsla": + return 4 !== s.length ? void It(e, 0, 0, 0, 1) : (s[3] = bt(s[3]), Ct(s, e), Dt(t, e), e); + case"hsl": + return 3 !== s.length ? void It(e, 0, 0, 0, 1) : (Ct(s, e), Dt(t, e), e); + default: + return + } + } + It(e, 0, 0, 0, 1) + } else { + if (4 === n.length) return (u = parseInt(n.substr(1), 16)) >= 0 && u <= 4095 ? (It(e, (3840 & u) >> 4 | (3840 & u) >> 8, 240 & u | (240 & u) >> 4, 15 & u | (15 & u) << 4, 1), Dt(t, e), e) : void It(e, 0, 0, 0, 1); + if (7 === n.length) { + var u = parseInt(n.substr(1), 16); + return u >= 0 && u <= 16777215 ? (It(e, (16711680 & u) >> 16, (65280 & u) >> 8, 255 & u, 1), Dt(t, e), e) : void It(e, 0, 0, 0, 1) + } + } + } + } + + function Ct(t, e) { + var i = (parseFloat(t[0]) % 360 + 360) % 360 / 360, n = bt(t[1]), o = bt(t[2]), + a = o <= .5 ? o * (n + 1) : o + n - o * n, r = 2 * o - a; + return e = e || [], It(e, yt(255 * St(r, a, i + 1 / 3)), yt(255 * St(r, a, i)), yt(255 * St(r, a, i - 1 / 3)), 1), 4 === t.length && (e[3] = t[3]), e + } + + function Lt(t) { + if (t) { + var e, i, n = t[0] / 255, o = t[1] / 255, a = t[2] / 255, r = Math.min(n, o, a), s = Math.max(n, o, a), + l = s - r, u = (s + r) / 2; + if (0 === l) e = 0, i = 0; else { + i = u < .5 ? l / (s + r) : l / (2 - s - r); + var h = ((s - n) / 6 + l / 2) / l, c = ((s - o) / 6 + l / 2) / l, d = ((s - a) / 6 + l / 2) / l; + n === s ? e = d - c : o === s ? e = 1 / 3 + h - d : a === s && (e = 2 / 3 + c - h), e < 0 && (e += 1), e > 1 && (e -= 1) + } + var f = [360 * e, i, u]; + return null != t[3] && f.push(t[3]), f + } + } + + function kt(t, e) { + var i = At(t); + if (i) { + for (var n = 0; n < 3; n++) i[n] = e < 0 ? i[n] * (1 - e) | 0 : (255 - i[n]) * e + i[n] | 0, i[n] > 255 ? i[n] = 255 : t[n] < 0 && (i[n] = 0); + return zt(i, 4 === i.length ? "rgba" : "rgb") + } + } + + function Pt(t) { + var e = At(t); + if (e) return ((1 << 24) + (e[0] << 16) + (e[1] << 8) + +e[2]).toString(16).slice(1) + } + + function Nt(t, e, i) { + if (e && e.length && t >= 0 && t <= 1) { + i = i || []; + var n = t * (e.length - 1), o = Math.floor(n), a = Math.ceil(n), r = e[o], s = e[a], l = n - o; + return i[0] = yt(Mt(r[0], s[0], l)), i[1] = yt(Mt(r[1], s[1], l)), i[2] = yt(Mt(r[2], s[2], l)), i[3] = _t(Mt(r[3], s[3], l)), i + } + } + + function Ot(t, e, i) { + if (e && e.length && t >= 0 && t <= 1) { + var n = t * (e.length - 1), o = Math.floor(n), a = Math.ceil(n), r = At(e[o]), s = At(e[a]), l = n - o, + u = zt([yt(Mt(r[0], s[0], l)), yt(Mt(r[1], s[1], l)), yt(Mt(r[2], s[2], l)), _t(Mt(r[3], s[3], l))], "rgba"); + return i ? {color: u, leftIndex: o, rightIndex: a, value: n} : u + } + } + + function Et(t, e, i, n) { + if (t = At(t)) return t = Lt(t), null != e && (t[0] = xt(e)), null != i && (t[1] = bt(i)), null != n && (t[2] = bt(n)), zt(Ct(t), "rgba") + } + + function Rt(t, e) { + if ((t = At(t)) && null != e) return t[3] = _t(e), zt(t, "rgba") + } + + function zt(t, e) { + if (t && t.length) { + var i = t[0] + "," + t[1] + "," + t[2]; + return "rgba" !== e && "hsva" !== e && "hsla" !== e || (i += "," + t[3]), e + "(" + i + ")" + } + } + + function Bt(t, e) { + return t[e] + } + + function Vt(t, e, i) { + t[e] = i + } + + function Gt(t, e, i) { + return (e - t) * i + t + } + + function Wt(t, e, i) { + return i > .5 ? e : t + } + + function Ft(t, e, i, n, o) { + var a = t.length; + if (1 == o) for (s = 0; s < a; s++) n[s] = Gt(t[s], e[s], i); else for (var r = a && t[0].length, s = 0; s < a; s++) for (var l = 0; l < r; l++) n[s][l] = Gt(t[s][l], e[s][l], i) + } + + function Ht(t, e, i) { + var n = t.length, o = e.length; + if (n !== o) if (n > o) t.length = o; else for (r = n; r < o; r++) t.push(1 === i ? e[r] : K_.call(e[r])); + for (var a = t[0] && t[0].length, r = 0; r < t.length; r++) if (1 === i) isNaN(t[r]) && (t[r] = e[r]); else for (var s = 0; s < a; s++) isNaN(t[r][s]) && (t[r][s] = e[r][s]) + } + + function Zt(t, e, i) { + if (t === e) return !0; + var n = t.length; + if (n !== e.length) return !1; + if (1 === i) { + for (a = 0; a < n; a++) if (t[a] !== e[a]) return !1 + } else for (var o = t[0].length, a = 0; a < n; a++) for (var r = 0; r < o; r++) if (t[a][r] !== e[a][r]) return !1; + return !0 + } + + function Ut(t, e, i, n, o, a, r, s, l) { + var u = t.length; + if (1 == l) for (c = 0; c < u; c++) s[c] = Xt(t[c], e[c], i[c], n[c], o, a, r); else for (var h = t[0].length, c = 0; c < u; c++) for (var d = 0; d < h; d++) s[c][d] = Xt(t[c][d], e[c][d], i[c][d], n[c][d], o, a, r) + } + + function Xt(t, e, i, n, o, a, r) { + var s = .5 * (i - t), l = .5 * (n - e); + return (2 * (e - i) + s + l) * r + (-3 * (e - i) - 2 * s - l) * a + s * o + e + } + + function jt(t) { + if (c(t)) { + var e = t.length; + if (c(t[0])) { + for (var i = [], n = 0; n < e; n++) i.push(K_.call(t[n])); + return i + } + return K_.call(t) + } + return t + } + + function Yt(t) { + return t[0] = Math.floor(t[0]), t[1] = Math.floor(t[1]), t[2] = Math.floor(t[2]), "rgba(" + t.join(",") + ")" + } + + function qt(t) { + var e = t[t.length - 1].value; + return c(e && e[0]) ? 2 : 1 + } + + function Kt(t, e, i, n, o, a) { + var r = t._getter, s = t._setter, l = "spline" === e, u = n.length; + if (u) { + var h, d = c(n[0].value), f = !1, p = !1, g = d ? qt(n) : 0; + n.sort(function (t, e) { + return t.time - e.time + }), h = n[u - 1].time; + for (var m = [], v = [], y = n[0].value, x = !0, _ = 0; _ < u; _++) { + m.push(n[_].time / h); + var w = n[_].value; + if (d && Zt(w, y, g) || !d && w === y || (x = !1), y = w, "string" == typeof w) { + var b = At(w); + b ? (w = b, f = !0) : p = !0 + } + v.push(w) + } + if (a || !x) { + for (var S = v[u - 1], _ = 0; _ < u - 1; _++) d ? Ht(v[_], S, g) : !isNaN(v[_]) || isNaN(S) || p || f || (v[_] = S); + d && Ht(r(t._target, o), S, g); + var M, I, T, D, A, C, L = 0, k = 0; + if (f) var P = [0, 0, 0, 0]; + var N = new vt({ + target: t._target, life: h, loop: t._loop, delay: t._delay, onframe: function (t, e) { + var i; + if (e < 0) i = 0; else if (e < k) { + for (i = M = Math.min(L + 1, u - 1); i >= 0 && !(m[i] <= e); i--) ; + i = Math.min(i, u - 2) + } else { + for (i = L; i < u && !(m[i] > e); i++) ; + i = Math.min(i - 1, u - 2) + } + L = i, k = e; + var n = m[i + 1] - m[i]; + if (0 !== n) if (I = (e - m[i]) / n, l) if (D = v[i], T = v[0 === i ? i : i - 1], A = v[i > u - 2 ? u - 1 : i + 1], C = v[i > u - 3 ? u - 1 : i + 2], d) Ut(T, D, A, C, I, I * I, I * I * I, r(t, o), g); else { + if (f) a = Ut(T, D, A, C, I, I * I, I * I * I, P, 1), a = Yt(P); else { + if (p) return Wt(D, A, I); + a = Xt(T, D, A, C, I, I * I, I * I * I) + } + s(t, o, a) + } else if (d) Ft(v[i], v[i + 1], I, r(t, o), g); else { + var a; + if (f) Ft(v[i], v[i + 1], I, P, 1), a = Yt(P); else { + if (p) return Wt(v[i], v[i + 1], I); + a = Gt(v[i], v[i + 1], I) + } + s(t, o, a) + } + }, ondestroy: i + }); + return e && "spline" !== e && (N.easing = e), N + } + } + } + + function $t(t, e, i, n) { + i < 0 && (t += i, i = -i), n < 0 && (e += n, n = -n), this.x = t, this.y = e, this.width = i, this.height = n + } + + function Jt(t) { + for (var e = 0; t >= lw;) e |= 1 & t, t >>= 1; + return t + e + } + + function Qt(t, e, i, n) { + var o = e + 1; + if (o === i) return 1; + if (n(t[o++], t[e]) < 0) { + for (; o < i && n(t[o], t[o - 1]) < 0;) o++; + te(t, e, o) + } else for (; o < i && n(t[o], t[o - 1]) >= 0;) o++; + return o - e + } + + function te(t, e, i) { + for (i--; e < i;) { + var n = t[e]; + t[e++] = t[i], t[i--] = n + } + } + + function ee(t, e, i, n, o) { + for (n === e && n++; n < i; n++) { + for (var a, r = t[n], s = e, l = n; s < l;) o(r, t[a = s + l >>> 1]) < 0 ? l = a : s = a + 1; + var u = n - s; + switch (u) { + case 3: + t[s + 3] = t[s + 2]; + case 2: + t[s + 2] = t[s + 1]; + case 1: + t[s + 1] = t[s]; + break; + default: + for (; u > 0;) t[s + u] = t[s + u - 1], u-- + } + t[s] = r + } + } + + function ie(t, e, i, n, o, a) { + var r = 0, s = 0, l = 1; + if (a(t, e[i + o]) > 0) { + for (s = n - o; l < s && a(t, e[i + o + l]) > 0;) r = l, (l = 1 + (l << 1)) <= 0 && (l = s); + l > s && (l = s), r += o, l += o + } else { + for (s = o + 1; l < s && a(t, e[i + o - l]) <= 0;) r = l, (l = 1 + (l << 1)) <= 0 && (l = s); + l > s && (l = s); + var u = r; + r = o - l, l = o - u + } + for (r++; r < l;) { + var h = r + (l - r >>> 1); + a(t, e[i + h]) > 0 ? r = h + 1 : l = h + } + return l + } + + function ne(t, e, i, n, o, a) { + var r = 0, s = 0, l = 1; + if (a(t, e[i + o]) < 0) { + for (s = o + 1; l < s && a(t, e[i + o - l]) < 0;) r = l, (l = 1 + (l << 1)) <= 0 && (l = s); + l > s && (l = s); + var u = r; + r = o - l, l = o - u + } else { + for (s = n - o; l < s && a(t, e[i + o + l]) >= 0;) r = l, (l = 1 + (l << 1)) <= 0 && (l = s); + l > s && (l = s), r += o, l += o + } + for (r++; r < l;) { + var h = r + (l - r >>> 1); + a(t, e[i + h]) < 0 ? l = h : r = h + 1 + } + return l + } + + function oe(t, e) { + function i(i) { + var s = a[i], u = r[i], h = a[i + 1], c = r[i + 1]; + r[i] = u + c, i === l - 3 && (a[i + 1] = a[i + 2], r[i + 1] = r[i + 2]), l--; + var d = ne(t[h], t, s, u, 0, e); + s += d, 0 !== (u -= d) && 0 !== (c = ie(t[s + u - 1], t, h, c, c - 1, e)) && (u <= c ? n(s, u, h, c) : o(s, u, h, c)) + } + + function n(i, n, o, a) { + var r = 0; + for (r = 0; r < n; r++) u[r] = t[i + r]; + var l = 0, h = o, c = i; + if (t[c++] = t[h++], 0 != --a) if (1 !== n) { + for (var d, f, p, g = s; ;) { + d = 0, f = 0, p = !1; + do { + if (e(t[h], u[l]) < 0) { + if (t[c++] = t[h++], f++, d = 0, 0 == --a) { + p = !0; + break + } + } else if (t[c++] = u[l++], d++, f = 0, 1 == --n) { + p = !0; + break + } + } while ((d | f) < g); + if (p) break; + do { + if (0 !== (d = ne(t[h], u, l, n, 0, e))) { + for (r = 0; r < d; r++) t[c + r] = u[l + r]; + if (c += d, l += d, (n -= d) <= 1) { + p = !0; + break + } + } + if (t[c++] = t[h++], 0 == --a) { + p = !0; + break + } + if (0 !== (f = ie(u[l], t, h, a, 0, e))) { + for (r = 0; r < f; r++) t[c + r] = t[h + r]; + if (c += f, h += f, 0 === (a -= f)) { + p = !0; + break + } + } + if (t[c++] = u[l++], 1 == --n) { + p = !0; + break + } + g-- + } while (d >= uw || f >= uw); + if (p) break; + g < 0 && (g = 0), g += 2 + } + if ((s = g) < 1 && (s = 1), 1 === n) { + for (r = 0; r < a; r++) t[c + r] = t[h + r]; + t[c + a] = u[l] + } else { + if (0 === n) throw new Error; + for (r = 0; r < n; r++) t[c + r] = u[l + r] + } + } else { + for (r = 0; r < a; r++) t[c + r] = t[h + r]; + t[c + a] = u[l] + } else for (r = 0; r < n; r++) t[c + r] = u[l + r] + } + + function o(i, n, o, a) { + var r = 0; + for (r = 0; r < a; r++) u[r] = t[o + r]; + var l = i + n - 1, h = a - 1, c = o + a - 1, d = 0, f = 0; + if (t[c--] = t[l--], 0 != --n) if (1 !== a) { + for (var p = s; ;) { + var g = 0, m = 0, v = !1; + do { + if (e(u[h], t[l]) < 0) { + if (t[c--] = t[l--], g++, m = 0, 0 == --n) { + v = !0; + break + } + } else if (t[c--] = u[h--], m++, g = 0, 1 == --a) { + v = !0; + break + } + } while ((g | m) < p); + if (v) break; + do { + if (0 != (g = n - ne(u[h], t, i, n, n - 1, e))) { + for (n -= g, f = (c -= g) + 1, d = (l -= g) + 1, r = g - 1; r >= 0; r--) t[f + r] = t[d + r]; + if (0 === n) { + v = !0; + break + } + } + if (t[c--] = u[h--], 1 == --a) { + v = !0; + break + } + if (0 != (m = a - ie(t[l], u, 0, a, a - 1, e))) { + for (a -= m, f = (c -= m) + 1, d = (h -= m) + 1, r = 0; r < m; r++) t[f + r] = u[d + r]; + if (a <= 1) { + v = !0; + break + } + } + if (t[c--] = t[l--], 0 == --n) { + v = !0; + break + } + p-- + } while (g >= uw || m >= uw); + if (v) break; + p < 0 && (p = 0), p += 2 + } + if ((s = p) < 1 && (s = 1), 1 === a) { + for (f = (c -= n) + 1, d = (l -= n) + 1, r = n - 1; r >= 0; r--) t[f + r] = t[d + r]; + t[c] = u[h] + } else { + if (0 === a) throw new Error; + for (d = c - (a - 1), r = 0; r < a; r++) t[d + r] = u[r] + } + } else { + for (f = (c -= n) + 1, d = (l -= n) + 1, r = n - 1; r >= 0; r--) t[f + r] = t[d + r]; + t[c] = u[h] + } else for (d = c - (a - 1), r = 0; r < a; r++) t[d + r] = u[r] + } + + var a, r, s = uw, l = 0, u = []; + a = [], r = [], this.mergeRuns = function () { + for (; l > 1;) { + var t = l - 2; + if (t >= 1 && r[t - 1] <= r[t] + r[t + 1] || t >= 2 && r[t - 2] <= r[t] + r[t - 1]) r[t - 1] < r[t + 1] && t--; else if (r[t] > r[t + 1]) break; + i(t) + } + }, this.forceMergeRuns = function () { + for (; l > 1;) { + var t = l - 2; + t > 0 && r[t - 1] < r[t + 1] && t--, i(t) + } + }, this.pushRun = function (t, e) { + a[l] = t, r[l] = e, l += 1 + } + } + + function ae(t, e, i, n) { + i || (i = 0), n || (n = t.length); + var o = n - i; + if (!(o < 2)) { + var a = 0; + if (o < lw) return a = Qt(t, i, n, e), void ee(t, i, n, i + a, e); + var r = new oe(t, e), s = Jt(o); + do { + if ((a = Qt(t, i, n, e)) < s) { + var l = o; + l > s && (l = s), ee(t, i, i + l, i + a, e), a = l + } + r.pushRun(i, a), r.mergeRuns(), o -= a, i += a + } while (0 !== o); + r.forceMergeRuns() + } + } + + function re(t, e) { + return t.zlevel === e.zlevel ? t.z === e.z ? t.z2 - e.z2 : t.z - e.z : t.zlevel - e.zlevel + } + + function se(t, e, i) { + var n = null == e.x ? 0 : e.x, o = null == e.x2 ? 1 : e.x2, a = null == e.y ? 0 : e.y, + r = null == e.y2 ? 0 : e.y2; + return e.global || (n = n * i.width + i.x, o = o * i.width + i.x, a = a * i.height + i.y, r = r * i.height + i.y), n = isNaN(n) ? 0 : n, o = isNaN(o) ? 1 : o, a = isNaN(a) ? 0 : a, r = isNaN(r) ? 0 : r, t.createLinearGradient(n, a, o, r) + } + + function le(t, e, i) { + var n = i.width, o = i.height, a = Math.min(n, o), r = null == e.x ? .5 : e.x, s = null == e.y ? .5 : e.y, + l = null == e.r ? .5 : e.r; + return e.global || (r = r * n + i.x, s = s * o + i.y, l *= a), t.createRadialGradient(r, s, 0, r, s, l) + } + + function ue() { + return !1 + } + + function he(t, e, i) { + var n = m_(), o = e.getWidth(), a = e.getHeight(), r = n.style; + return r && (r.position = "absolute", r.left = 0, r.top = 0, r.width = o + "px", r.height = a + "px", n.setAttribute("data-zr-dom-id", t)), n.width = o * i, n.height = a * i, n + } + + function ce(t) { + if ("string" == typeof t) { + var e = ww.get(t); + return e && e.image + } + return t + } + + function de(t, e, i, n, o) { + if (t) { + if ("string" == typeof t) { + if (e && e.__zrImageSrc === t || !i) return e; + var a = ww.get(t), r = {hostEl: i, cb: n, cbPayload: o}; + return a ? !pe(e = a.image) && a.pending.push(r) : (!e && (e = new Image), e.onload = fe, ww.put(t, e.__cachedImgObj = { + image: e, + pending: [r] + }), e.src = e.__zrImageSrc = t), e + } + return t + } + return e + } + + function fe() { + var t = this.__cachedImgObj; + this.onload = this.__cachedImgObj = null; + for (var e = 0; e < t.pending.length; e++) { + var i = t.pending[e], n = i.cb; + n && n(this, i.cbPayload), i.hostEl.dirty() + } + t.pending.length = 0 + } + + function pe(t) { + return t && t.width && t.height + } + + function ge(t, e) { + var i = t + ":" + (e = e || Tw); + if (bw[i]) return bw[i]; + for (var n = (t + "").split("\n"), o = 0, a = 0, r = n.length; a < r; a++) o = Math.max(De(n[a], e).width, o); + return Sw > Mw && (Sw = 0, bw = {}), Sw++, bw[i] = o, o + } + + function me(t, e, i, n, o, a, r) { + return a ? ye(t, e, i, n, o, a, r) : ve(t, e, i, n, o, r) + } + + function ve(t, e, i, n, o, a) { + var r = Ae(t, e, o, a), s = ge(t, e); + o && (s += o[1] + o[3]); + var l = r.outerHeight, u = new $t(xe(0, s, i), _e(0, l, n), s, l); + return u.lineHeight = r.lineHeight, u + } + + function ye(t, e, i, n, o, a, r) { + var s = Ce(t, {rich: a, truncate: r, font: e, textAlign: i, textPadding: o}), l = s.outerWidth, + u = s.outerHeight; + return new $t(xe(0, l, i), _e(0, u, n), l, u) + } + + function xe(t, e, i) { + return "right" === i ? t -= e : "center" === i && (t -= e / 2), t + } + + function _e(t, e, i) { + return "middle" === i ? t -= e / 2 : "bottom" === i && (t -= e), t + } + + function we(t, e, i) { + var n = e.x, o = e.y, a = e.height, r = e.width, s = a / 2, l = "left", u = "top"; + switch (t) { + case"left": + n -= i, o += s, l = "right", u = "middle"; + break; + case"right": + n += i + r, o += s, u = "middle"; + break; + case"top": + n += r / 2, o -= i, l = "center", u = "bottom"; + break; + case"bottom": + n += r / 2, o += a + i, l = "center"; + break; + case"inside": + n += r / 2, o += s, l = "center", u = "middle"; + break; + case"insideLeft": + n += i, o += s, u = "middle"; + break; + case"insideRight": + n += r - i, o += s, l = "right", u = "middle"; + break; + case"insideTop": + n += r / 2, o += i, l = "center"; + break; + case"insideBottom": + n += r / 2, o += a - i, l = "center", u = "bottom"; + break; + case"insideTopLeft": + n += i, o += i; + break; + case"insideTopRight": + n += r - i, o += i, l = "right"; + break; + case"insideBottomLeft": + n += i, o += a - i, u = "bottom"; + break; + case"insideBottomRight": + n += r - i, o += a - i, l = "right", u = "bottom" + } + return {x: n, y: o, textAlign: l, textVerticalAlign: u} + } + + function be(t, e, i, n, o) { + if (!e) return ""; + var a = (t + "").split("\n"); + o = Se(e, i, n, o); + for (var r = 0, s = a.length; r < s; r++) a[r] = Me(a[r], o); + return a.join("\n") + } + + function Se(t, e, i, n) { + (n = a({}, n)).font = e; + var i = D(i, "..."); + n.maxIterations = D(n.maxIterations, 2); + var o = n.minChar = D(n.minChar, 0); + n.cnCharWidth = ge("国", e); + var r = n.ascCharWidth = ge("a", e); + n.placeholder = D(n.placeholder, ""); + for (var s = t = Math.max(0, t - 1), l = 0; l < o && s >= r; l++) s -= r; + var u = ge(i); + return u > s && (i = "", u = 0), s = t - u, n.ellipsis = i, n.ellipsisWidth = u, n.contentWidth = s, n.containerWidth = t, n + } + + function Me(t, e) { + var i = e.containerWidth, n = e.font, o = e.contentWidth; + if (!i) return ""; + var a = ge(t, n); + if (a <= i) return t; + for (var r = 0; ; r++) { + if (a <= o || r >= e.maxIterations) { + t += e.ellipsis; + break + } + var s = 0 === r ? Ie(t, o, e.ascCharWidth, e.cnCharWidth) : a > 0 ? Math.floor(t.length * o / a) : 0; + a = ge(t = t.substr(0, s), n) + } + return "" === t && (t = e.placeholder), t + } + + function Ie(t, e, i, n) { + for (var o = 0, a = 0, r = t.length; a < r && o < e; a++) { + var s = t.charCodeAt(a); + o += 0 <= s && s <= 127 ? i : n + } + return a + } + + function Te(t) { + return ge("国", t) + } + + function De(t, e) { + return Dw.measureText(t, e) + } + + function Ae(t, e, i, n) { + null != t && (t += ""); + var o = Te(e), a = t ? t.split("\n") : [], r = a.length * o, s = r; + if (i && (s += i[0] + i[2]), t && n) { + var l = n.outerHeight, u = n.outerWidth; + if (null != l && s > l) t = "", a = []; else if (null != u) for (var h = Se(u - (i ? i[1] + i[3] : 0), e, n.ellipsis, { + minChar: n.minChar, + placeholder: n.placeholder + }), c = 0, d = a.length; c < d; c++) a[c] = Me(a[c], h) + } + return {lines: a, height: r, outerHeight: s, lineHeight: o} + } + + function Ce(t, e) { + var i = {lines: [], width: 0, height: 0}; + if (null != t && (t += ""), !t) return i; + for (var n, o = Iw.lastIndex = 0; null != (n = Iw.exec(t));) { + var a = n.index; + a > o && Le(i, t.substring(o, a)), Le(i, n[2], n[1]), o = Iw.lastIndex + } + o < t.length && Le(i, t.substring(o, t.length)); + var r = i.lines, s = 0, l = 0, u = [], h = e.textPadding, c = e.truncate, d = c && c.outerWidth, + f = c && c.outerHeight; + h && (null != d && (d -= h[1] + h[3]), null != f && (f -= h[0] + h[2])); + for (L = 0; L < r.length; L++) { + for (var p = r[L], g = 0, m = 0, v = 0; v < p.tokens.length; v++) { + var y = (k = p.tokens[v]).styleName && e.rich[k.styleName] || {}, x = k.textPadding = y.textPadding, + _ = k.font = y.font || e.font, w = k.textHeight = D(y.textHeight, Te(_)); + if (x && (w += x[0] + x[2]), k.height = w, k.lineHeight = A(y.textLineHeight, e.textLineHeight, w), k.textAlign = y && y.textAlign || e.textAlign, k.textVerticalAlign = y && y.textVerticalAlign || "middle", null != f && s + k.lineHeight > f) return { + lines: [], + width: 0, + height: 0 + }; + k.textWidth = ge(k.text, _); + var b = y.textWidth, S = null == b || "auto" === b; + if ("string" == typeof b && "%" === b.charAt(b.length - 1)) k.percentWidth = b, u.push(k), b = 0; else { + if (S) { + b = k.textWidth; + var M = y.textBackgroundColor, I = M && M.image; + I && pe(I = ce(I)) && (b = Math.max(b, I.width * w / I.height)) + } + var T = x ? x[1] + x[3] : 0; + b += T; + var C = null != d ? d - m : null; + null != C && C < b && (!S || C < T ? (k.text = "", k.textWidth = b = 0) : (k.text = be(k.text, C - T, _, c.ellipsis, {minChar: c.minChar}), k.textWidth = ge(k.text, _), b = k.textWidth + T)) + } + m += k.width = b, y && (g = Math.max(g, k.lineHeight)) + } + p.width = m, p.lineHeight = g, s += g, l = Math.max(l, m) + } + i.outerWidth = i.width = D(e.textWidth, l), i.outerHeight = i.height = D(e.textHeight, s), h && (i.outerWidth += h[1] + h[3], i.outerHeight += h[0] + h[2]); + for (var L = 0; L < u.length; L++) { + var k = u[L], P = k.percentWidth; + k.width = parseInt(P, 10) / 100 * l + } + return i + } + + function Le(t, e, i) { + for (var n = "" === e, o = e.split("\n"), a = t.lines, r = 0; r < o.length; r++) { + var s = o[r], l = {styleName: i, text: s, isLineHolder: !s && !n}; + if (r) a.push({tokens: [l]}); else { + var u = (a[a.length - 1] || (a[0] = {tokens: []})).tokens, h = u.length; + 1 === h && u[0].isLineHolder ? u[0] = l : (s || !h || n) && u.push(l) + } + } + } + + function ke(t) { + var e = (t.fontSize || t.fontFamily) && [t.fontStyle, t.fontWeight, (t.fontSize || 12) + "px", t.fontFamily || "sans-serif"].join(" "); + return e && P(e) || t.textFont || t.font + } + + function Pe(t, e) { + var i, n, o, a, r = e.x, s = e.y, l = e.width, u = e.height, h = e.r; + l < 0 && (r += l, l = -l), u < 0 && (s += u, u = -u), "number" == typeof h ? i = n = o = a = h : h instanceof Array ? 1 === h.length ? i = n = o = a = h[0] : 2 === h.length ? (i = o = h[0], n = a = h[1]) : 3 === h.length ? (i = h[0], n = a = h[1], o = h[2]) : (i = h[0], n = h[1], o = h[2], a = h[3]) : i = n = o = a = 0; + var c; + i + n > l && (i *= l / (c = i + n), n *= l / c), o + a > l && (o *= l / (c = o + a), a *= l / c), n + o > u && (n *= u / (c = n + o), o *= u / c), i + a > u && (i *= u / (c = i + a), a *= u / c), t.moveTo(r + i, s), t.lineTo(r + l - n, s), 0 !== n && t.arc(r + l - n, s + n, n, -Math.PI / 2, 0), t.lineTo(r + l, s + u - o), 0 !== o && t.arc(r + l - o, s + u - o, o, 0, Math.PI / 2), t.lineTo(r + a, s + u), 0 !== a && t.arc(r + a, s + u - a, a, Math.PI / 2, Math.PI), t.lineTo(r, s + i), 0 !== i && t.arc(r + i, s + i, i, Math.PI, 1.5 * Math.PI) + } + + function Ne(t) { + return Oe(t), d(t.rich, Oe), t + } + + function Oe(t) { + if (t) { + t.font = ke(t); + var e = t.textAlign; + "middle" === e && (e = "center"), t.textAlign = null == e || Aw[e] ? e : "left"; + var i = t.textVerticalAlign || t.textBaseline; + "center" === i && (i = "middle"), t.textVerticalAlign = null == i || Cw[i] ? i : "top", t.textPadding && (t.textPadding = L(t.textPadding)) + } + } + + function Ee(t, e, i, n, o) { + n.rich ? ze(t, e, i, n, o) : Re(t, e, i, n, o) + } + + function Re(t, e, i, n, o) { + var a = Ue(e, "font", n.font || Tw), r = n.textPadding, s = t.__textCotentBlock; + s && !t.__dirty || (s = t.__textCotentBlock = Ae(i, a, r, n.truncate)); + var l = s.outerHeight, u = s.lines, h = s.lineHeight, c = Ze(l, n, o), d = c.baseX, f = c.baseY, + p = c.textAlign, g = c.textVerticalAlign; + Ve(e, n, o, d, f); + var m = _e(f, l, g), v = d, y = m, x = We(n); + if (x || r) { + var _ = ge(i, a); + r && (_ += r[1] + r[3]); + var w = xe(d, _, p); + x && Fe(t, e, n, w, m, _, l), r && (v = qe(d, p, r), y += r[0]) + } + Ue(e, "textAlign", p || "left"), Ue(e, "textBaseline", "middle"), Ue(e, "shadowBlur", n.textShadowBlur || 0), Ue(e, "shadowColor", n.textShadowColor || "transparent"), Ue(e, "shadowOffsetX", n.textShadowOffsetX || 0), Ue(e, "shadowOffsetY", n.textShadowOffsetY || 0), y += h / 2; + var b = n.textStrokeWidth, S = Xe(n.textStroke, b), M = je(n.textFill); + S && (Ue(e, "lineWidth", b), Ue(e, "strokeStyle", S)), M && Ue(e, "fillStyle", M); + for (var I = 0; I < u.length; I++) S && e.strokeText(u[I], v, y), M && e.fillText(u[I], v, y), y += h + } + + function ze(t, e, i, n, o) { + var a = t.__textCotentBlock; + a && !t.__dirty || (a = t.__textCotentBlock = Ce(i, n)), Be(t, e, a, n, o) + } + + function Be(t, e, i, n, o) { + var a = i.width, r = i.outerWidth, s = i.outerHeight, l = n.textPadding, u = Ze(s, n, o), h = u.baseX, + c = u.baseY, d = u.textAlign, f = u.textVerticalAlign; + Ve(e, n, o, h, c); + var p = xe(h, r, d), g = _e(c, s, f), m = p, v = g; + l && (m += l[3], v += l[0]); + var y = m + a; + We(n) && Fe(t, e, n, p, g, r, s); + for (var x = 0; x < i.lines.length; x++) { + for (var _, w = i.lines[x], b = w.tokens, S = b.length, M = w.lineHeight, I = w.width, T = 0, D = m, A = y, C = S - 1; T < S && (!(_ = b[T]).textAlign || "left" === _.textAlign);) Ge(t, e, _, n, M, v, D, "left"), I -= _.width, D += _.width, T++; + for (; C >= 0 && "right" === (_ = b[C]).textAlign;) Ge(t, e, _, n, M, v, A, "right"), I -= _.width, A -= _.width, C--; + for (D += (a - (D - m) - (y - A) - I) / 2; T <= C;) Ge(t, e, _ = b[T], n, M, v, D + _.width / 2, "center"), D += _.width, T++; + v += M + } + } + + function Ve(t, e, i, n, o) { + if (i && e.textRotation) { + var a = e.textOrigin; + "center" === a ? (n = i.width / 2 + i.x, o = i.height / 2 + i.y) : a && (n = a[0] + i.x, o = a[1] + i.y), t.translate(n, o), t.rotate(-e.textRotation), t.translate(-n, -o) + } + } + + function Ge(t, e, i, n, o, a, r, s) { + var l = n.rich[i.styleName] || {}, u = i.textVerticalAlign, h = a + o / 2; + "top" === u ? h = a + i.height / 2 : "bottom" === u && (h = a + o - i.height / 2), !i.isLineHolder && We(l) && Fe(t, e, l, "right" === s ? r - i.width : "center" === s ? r - i.width / 2 : r, h - i.height / 2, i.width, i.height); + var c = i.textPadding; + c && (r = qe(r, s, c), h -= i.height / 2 - c[2] - i.textHeight / 2), Ue(e, "shadowBlur", A(l.textShadowBlur, n.textShadowBlur, 0)), Ue(e, "shadowColor", l.textShadowColor || n.textShadowColor || "transparent"), Ue(e, "shadowOffsetX", A(l.textShadowOffsetX, n.textShadowOffsetX, 0)), Ue(e, "shadowOffsetY", A(l.textShadowOffsetY, n.textShadowOffsetY, 0)), Ue(e, "textAlign", s), Ue(e, "textBaseline", "middle"), Ue(e, "font", i.font || Tw); + var d = Xe(l.textStroke || n.textStroke, p), f = je(l.textFill || n.textFill), + p = D(l.textStrokeWidth, n.textStrokeWidth); + d && (Ue(e, "lineWidth", p), Ue(e, "strokeStyle", d), e.strokeText(i.text, r, h)), f && (Ue(e, "fillStyle", f), e.fillText(i.text, r, h)) + } + + function We(t) { + return t.textBackgroundColor || t.textBorderWidth && t.textBorderColor + } + + function Fe(t, e, i, n, o, a, r) { + var s = i.textBackgroundColor, l = i.textBorderWidth, u = i.textBorderColor, h = _(s); + if (Ue(e, "shadowBlur", i.textBoxShadowBlur || 0), Ue(e, "shadowColor", i.textBoxShadowColor || "transparent"), Ue(e, "shadowOffsetX", i.textBoxShadowOffsetX || 0), Ue(e, "shadowOffsetY", i.textBoxShadowOffsetY || 0), h || l && u) { + e.beginPath(); + var c = i.textBorderRadius; + c ? Pe(e, {x: n, y: o, width: a, height: r, r: c}) : e.rect(n, o, a, r), e.closePath() + } + if (h) Ue(e, "fillStyle", s), e.fill(); else if (w(s)) { + var d = s.image; + (d = de(d, null, t, He, s)) && pe(d) && e.drawImage(d, n, o, a, r) + } + l && u && (Ue(e, "lineWidth", l), Ue(e, "strokeStyle", u), e.stroke()) + } + + function He(t, e) { + e.image = t + } + + function Ze(t, e, i) { + var n = e.x || 0, o = e.y || 0, a = e.textAlign, r = e.textVerticalAlign; + if (i) { + var s = e.textPosition; + if (s instanceof Array) n = i.x + Ye(s[0], i.width), o = i.y + Ye(s[1], i.height); else { + var l = we(s, i, e.textDistance); + n = l.x, o = l.y, a = a || l.textAlign, r = r || l.textVerticalAlign + } + var u = e.textOffset; + u && (n += u[0], o += u[1]) + } + return {baseX: n, baseY: o, textAlign: a, textVerticalAlign: r} + } + + function Ue(t, e, i) { + return t[e] = dw(t, e, i), t[e] + } + + function Xe(t, e) { + return null == t || e <= 0 || "transparent" === t || "none" === t ? null : t.image || t.colorStops ? "#000" : t + } + + function je(t) { + return null == t || "none" === t ? null : t.image || t.colorStops ? "#000" : t + } + + function Ye(t, e) { + return "string" == typeof t ? t.lastIndexOf("%") >= 0 ? parseFloat(t) / 100 * e : parseFloat(t) : t + } + + function qe(t, e, i) { + return "right" === e ? t - i[1] : "center" === e ? t + i[3] / 2 - i[1] / 2 : t + i[3] + } + + function Ke(t, e) { + return null != t && (t || e.textBackgroundColor || e.textBorderWidth && e.textBorderColor || e.textPadding) + } + + function $e(t) { + t = t || {}, nw.call(this, t); + for (var e in t) t.hasOwnProperty(e) && "style" !== e && (this[e] = t[e]); + this.style = new pw(t.style, this), this._rect = null, this.__clipPaths = [] + } + + function Je(t) { + $e.call(this, t) + } + + function Qe(t) { + return parseInt(t, 10) + } + + function ti(t) { + return !!t && (!!t.__builtin__ || "function" == typeof t.resize && "function" == typeof t.refresh) + } + + function ei(t, e, i) { + return Pw.copy(t.getBoundingRect()), t.transform && Pw.applyTransform(t.transform), Nw.width = e, Nw.height = i, !Pw.intersect(Nw) + } + + function ii(t, e) { + if (t == e) return !1; + if (!t || !e || t.length !== e.length) return !0; + for (var i = 0; i < t.length; i++) if (t[i] !== e[i]) return !0 + } + + function ni(t, e) { + for (var i = 0; i < t.length; i++) { + var n = t[i]; + n.setTransform(e), e.beginPath(), n.buildPath(e, n.shape), e.clip(), n.restoreTransform(e) + } + } + + function oi(t, e) { + var i = document.createElement("div"); + return i.style.cssText = ["position:relative", "overflow:hidden", "width:" + t + "px", "height:" + e + "px", "padding:0", "margin:0", "border-width:0"].join(";") + ";", i + } + + function ai(t) { + return t.getBoundingClientRect ? t.getBoundingClientRect() : {left: 0, top: 0} + } + + function ri(t, e, i, n) { + return i = i || {}, n || !a_.canvasSupported ? si(t, e, i) : a_.browser.firefox && null != e.layerX && e.layerX !== e.offsetX ? (i.zrX = e.layerX, i.zrY = e.layerY) : null != e.offsetX ? (i.zrX = e.offsetX, i.zrY = e.offsetY) : si(t, e, i), i + } + + function si(t, e, i) { + var n = ai(t); + i.zrX = e.clientX - n.left, i.zrY = e.clientY - n.top + } + + function li(t, e, i) { + if (null != (e = e || window.event).zrX) return e; + var n = e.type; + if (n && n.indexOf("touch") >= 0) { + var o = "touchend" != n ? e.targetTouches[0] : e.changedTouches[0]; + o && ri(t, o, e, i) + } else ri(t, e, e, i), e.zrDelta = e.wheelDelta ? e.wheelDelta / 120 : -(e.detail || 0) / 3; + var a = e.button; + return null == e.which && void 0 !== a && Rw.test(e.type) && (e.which = 1 & a ? 1 : 2 & a ? 3 : 4 & a ? 2 : 0), e + } + + function ui(t, e, i) { + Ew ? t.addEventListener(e, i) : t.attachEvent("on" + e, i) + } + + function hi(t, e, i) { + Ew ? t.removeEventListener(e, i) : t.detachEvent("on" + e, i) + } + + function ci(t) { + return t.which > 1 + } + + function di(t) { + var e = t[1][0] - t[0][0], i = t[1][1] - t[0][1]; + return Math.sqrt(e * e + i * i) + } + + function fi(t) { + return [(t[0][0] + t[1][0]) / 2, (t[0][1] + t[1][1]) / 2] + } + + function pi(t) { + return "mousewheel" === t && a_.browser.firefox ? "DOMMouseScroll" : t + } + + function gi(t, e, i) { + var n = t._gestureMgr; + "start" === i && n.clear(); + var o = n.recognize(e, t.handler.findHover(e.zrX, e.zrY, null).target, t.dom); + if ("end" === i && n.clear(), o) { + var a = o.type; + e.gestureEvent = a, t.handler.dispatchToElement({target: o.target}, a, o.event) + } + } + + function mi(t) { + t._touching = !0, clearTimeout(t._touchTimer), t._touchTimer = setTimeout(function () { + t._touching = !1 + }, 700) + } + + function vi(t) { + var e = t.pointerType; + return "pen" === e || "touch" === e + } + + function yi(t) { + function e(t, e) { + return function () { + if (!e._touching) return t.apply(e, arguments) + } + } + + d(Fw, function (e) { + t._handlers[e] = m(Uw[e], t) + }), d(Zw, function (e) { + t._handlers[e] = m(Uw[e], t) + }), d(Ww, function (i) { + t._handlers[i] = e(Uw[i], t) + }) + } + + function xi(t) { + function e(e, i) { + d(e, function (e) { + ui(t, pi(e), i._handlers[e]) + }, i) + } + + D_.call(this), this.dom = t, this._touching = !1, this._touchTimer, this._gestureMgr = new Vw, this._handlers = {}, yi(this), a_.pointerEventsSupported ? e(Zw, this) : (a_.touchEventsSupported && e(Fw, this), e(Ww, this)) + } + + function _i(t, e) { + var i = new Kw(n_(), t, e); + return qw[i.id] = i, i + } + + function wi(t, e) { + Yw[t] = e + } + + function bi(t) { + delete qw[t] + } + + function Si(t) { + return t instanceof Array ? t : null == t ? [] : [t] + } + + function Mi(t, e, i) { + if (t) { + t[e] = t[e] || {}, t.emphasis = t.emphasis || {}, t.emphasis[e] = t.emphasis[e] || {}; + for (var n = 0, o = i.length; n < o; n++) { + var a = i[n]; + !t.emphasis[e].hasOwnProperty(a) && t[e].hasOwnProperty(a) && (t.emphasis[e][a] = t[e][a]) + } + } + } + + function Ii(t) { + return !Qw(t) || tb(t) || t instanceof Date ? t : t.value + } + + function Ti(t) { + return Qw(t) && !(t instanceof Array) + } + + function Di(t, e) { + e = (e || []).slice(); + var i = f(t || [], function (t, e) { + return {exist: t} + }); + return Jw(e, function (t, n) { + if (Qw(t)) { + for (o = 0; o < i.length; o++) if (!i[o].option && null != t.id && i[o].exist.id === t.id + "") return i[o].option = t, void (e[n] = null); + for (var o = 0; o < i.length; o++) { + var a = i[o].exist; + if (!(i[o].option || null != a.id && null != t.id || null == t.name || Li(t) || Li(a) || a.name !== t.name + "")) return i[o].option = t, void (e[n] = null) + } + } + }), Jw(e, function (t, e) { + if (Qw(t)) { + for (var n = 0; n < i.length; n++) { + var o = i[n].exist; + if (!i[n].option && !Li(o) && null == t.id) { + i[n].option = t; + break + } + } + n >= i.length && i.push({option: t}) + } + }), i + } + + function Ai(t) { + var e = R(); + Jw(t, function (t, i) { + var n = t.exist; + n && e.set(n.id, t) + }), Jw(t, function (t, i) { + var n = t.option; + k(!n || null == n.id || !e.get(n.id) || e.get(n.id) === t, "id duplicates: " + (n && n.id)), n && null != n.id && e.set(n.id, t), !t.keyInfo && (t.keyInfo = {}) + }), Jw(t, function (t, i) { + var n = t.exist, o = t.option, a = t.keyInfo; + if (Qw(o)) { + if (a.name = null != o.name ? o.name + "" : n ? n.name : eb + i, n) a.id = n.id; else if (null != o.id) a.id = o.id + ""; else { + var r = 0; + do { + a.id = "\0" + a.name + "\0" + r++ + } while (e.get(a.id)) + } + e.set(a.id, t) + } + }) + } + + function Ci(t) { + var e = t.name; + return !(!e || !e.indexOf(eb)) + } + + function Li(t) { + return Qw(t) && t.id && 0 === (t.id + "").indexOf("\0_ec_\0") + } + + function ki(t, e) { + function i(t, e, i) { + for (var n = 0, o = t.length; n < o; n++) for (var a = t[n].seriesId, r = Si(t[n].dataIndex), s = i && i[a], l = 0, u = r.length; l < u; l++) { + var h = r[l]; + s && s[h] ? s[h] = null : (e[a] || (e[a] = {}))[h] = 1 + } + } + + function n(t, e) { + var i = []; + for (var o in t) if (t.hasOwnProperty(o) && null != t[o]) if (e) i.push(+o); else { + var a = n(t[o], !0); + a.length && i.push({seriesId: o, dataIndex: a}) + } + return i + } + + var o = {}, a = {}; + return i(t || [], o), i(e || [], a, o), [n(o), n(a)] + } + + function Pi(t, e) { + return null != e.dataIndexInside ? e.dataIndexInside : null != e.dataIndex ? y(e.dataIndex) ? f(e.dataIndex, function (e) { + return t.indexOfRawIndex(e) + }) : t.indexOfRawIndex(e.dataIndex) : null != e.name ? y(e.name) ? f(e.name, function (e) { + return t.indexOfName(e) + }) : t.indexOfName(e.name) : void 0 + } + + function Ni() { + var t = "__\0ec_inner_" + nb++ + "_" + Math.random().toFixed(5); + return function (e) { + return e[t] || (e[t] = {}) + } + } + + function Oi(t, e, i) { + if (_(e)) { + var n = {}; + n[e + "Index"] = 0, e = n + } + var o = i && i.defaultMainType; + !o || Ei(e, o + "Index") || Ei(e, o + "Id") || Ei(e, o + "Name") || (e[o + "Index"] = 0); + var a = {}; + return Jw(e, function (n, o) { + var n = e[o]; + if ("dataIndex" !== o && "dataIndexInside" !== o) { + var r = o.match(/^(\w+)(Index|Id|Name)$/) || [], s = r[1], u = (r[2] || "").toLowerCase(); + if (!(!s || !u || null == n || "index" === u && "none" === n || i && i.includeMainTypes && l(i.includeMainTypes, s) < 0)) { + var h = {mainType: s}; + "index" === u && "all" === n || (h[u] = n); + var c = t.queryComponents(h); + a[s + "Models"] = c, a[s + "Model"] = c[0] + } + } else a[o] = n + }), a + } + + function Ei(t, e) { + return t && t.hasOwnProperty(e) + } + + function Ri(t, e, i) { + t.setAttribute ? t.setAttribute(e, i) : t[e] = i + } + + function zi(t, e) { + return t.getAttribute ? t.getAttribute(e) : t[e] + } + + function Bi(t) { + var e = {main: "", sub: ""}; + return t && (t = t.split(ob), e.main = t[0] || "", e.sub = t[1] || ""), e + } + + function Vi(t) { + k(/^[a-zA-Z0-9_]+([.][a-zA-Z0-9_]+)?$/.test(t), 'componentType "' + t + '" illegal') + } + + function Gi(t, e) { + t.$constructor = t, t.extend = function (t) { + var e = this, i = function () { + t.$constructor ? t.$constructor.apply(this, arguments) : e.apply(this, arguments) + }; + return a(i.prototype, t), i.extend = this.extend, i.superCall = Fi, i.superApply = Hi, u(i, this), i.superClass = e, i + } + } + + function Wi(t) { + var e = ["__\0is_clz", rb++, Math.random().toFixed(3)].join("_"); + t.prototype[e] = !0, t.isInstance = function (t) { + return !(!t || !t[e]) + } + } + + function Fi(t, e) { + var i = C(arguments, 2); + return this.superClass.prototype[e].apply(t, i) + } + + function Hi(t, e, i) { + return this.superClass.prototype[e].apply(t, i) + } + + function Zi(t, e) { + function i(t) { + var e = n[t.main]; + return e && e[ab] || ((e = n[t.main] = {})[ab] = !0), e + } + + e = e || {}; + var n = {}; + if (t.registerClass = function (t, e) { + return e && (Vi(e), (e = Bi(e)).sub ? e.sub !== ab && (i(e)[e.sub] = t) : n[e.main] = t), t + }, t.getClass = function (t, e, i) { + var o = n[t]; + if (o && o[ab] && (o = e ? o[e] : null), i && !o) throw new Error(e ? "Component " + t + "." + (e || "") + " not exists. Load it first." : t + ".type should be specified."); + return o + }, t.getClassesByMainType = function (t) { + t = Bi(t); + var e = [], i = n[t.main]; + return i && i[ab] ? d(i, function (t, i) { + i !== ab && e.push(t) + }) : e.push(i), e + }, t.hasClass = function (t) { + return t = Bi(t), !!n[t.main] + }, t.getAllClassMainTypes = function () { + var t = []; + return d(n, function (e, i) { + t.push(i) + }), t + }, t.hasSubTypes = function (t) { + t = Bi(t); + var e = n[t.main]; + return e && e[ab] + }, t.parseClassType = Bi, e.registerWhenExtend) { + var o = t.extend; + o && (t.extend = function (e) { + var i = o.call(this, e); + return t.registerClass(i, e.type) + }) + } + return t + } + + function Ui(t) { + return t > -pb && t < pb + } + + function Xi(t) { + return t > pb || t < -pb + } + + function ji(t, e, i, n, o) { + var a = 1 - o; + return a * a * (a * t + 3 * o * e) + o * o * (o * n + 3 * a * i) + } + + function Yi(t, e, i, n, o) { + var a = 1 - o; + return 3 * (((e - t) * a + 2 * (i - e) * o) * a + (n - i) * o * o) + } + + function qi(t, e, i, n, o, a) { + var r = n + 3 * (e - i) - t, s = 3 * (i - 2 * e + t), l = 3 * (e - t), u = t - o, h = s * s - 3 * r * l, + c = s * l - 9 * r * u, d = l * l - 3 * s * u, f = 0; + if (Ui(h) && Ui(c)) Ui(s) ? a[0] = 0 : (M = -l / s) >= 0 && M <= 1 && (a[f++] = M); else { + var p = c * c - 4 * h * d; + if (Ui(p)) { + var g = c / h, m = -g / 2; + (M = -s / r + g) >= 0 && M <= 1 && (a[f++] = M), m >= 0 && m <= 1 && (a[f++] = m) + } else if (p > 0) { + var v = fb(p), y = h * s + 1.5 * r * (-c + v), x = h * s + 1.5 * r * (-c - v); + (M = (-s - ((y = y < 0 ? -db(-y, vb) : db(y, vb)) + (x = x < 0 ? -db(-x, vb) : db(x, vb)))) / (3 * r)) >= 0 && M <= 1 && (a[f++] = M) + } else { + var _ = (2 * h * s - 3 * r * c) / (2 * fb(h * h * h)), w = Math.acos(_) / 3, b = fb(h), S = Math.cos(w), + M = (-s - 2 * b * S) / (3 * r), m = (-s + b * (S + mb * Math.sin(w))) / (3 * r), + I = (-s + b * (S - mb * Math.sin(w))) / (3 * r); + M >= 0 && M <= 1 && (a[f++] = M), m >= 0 && m <= 1 && (a[f++] = m), I >= 0 && I <= 1 && (a[f++] = I) + } + } + return f + } + + function Ki(t, e, i, n, o) { + var a = 6 * i - 12 * e + 6 * t, r = 9 * e + 3 * n - 3 * t - 9 * i, s = 3 * e - 3 * t, l = 0; + if (Ui(r)) Xi(a) && (c = -s / a) >= 0 && c <= 1 && (o[l++] = c); else { + var u = a * a - 4 * r * s; + if (Ui(u)) o[0] = -a / (2 * r); else if (u > 0) { + var h = fb(u), c = (-a + h) / (2 * r), d = (-a - h) / (2 * r); + c >= 0 && c <= 1 && (o[l++] = c), d >= 0 && d <= 1 && (o[l++] = d) + } + } + return l + } + + function $i(t, e, i, n, o, a) { + var r = (e - t) * o + t, s = (i - e) * o + e, l = (n - i) * o + i, u = (s - r) * o + r, h = (l - s) * o + s, + c = (h - u) * o + u; + a[0] = t, a[1] = r, a[2] = u, a[3] = c, a[4] = c, a[5] = h, a[6] = l, a[7] = n + } + + function Ji(t, e, i, n, o, a, r, s, l, u, h) { + var c, d, f, p, g, m = .005, v = 1 / 0; + yb[0] = l, yb[1] = u; + for (var y = 0; y < 1; y += .05) xb[0] = ji(t, i, o, r, y), xb[1] = ji(e, n, a, s, y), (p = M_(yb, xb)) < v && (c = y, v = p); + v = 1 / 0; + for (var x = 0; x < 32 && !(m < gb); x++) d = c - m, f = c + m, xb[0] = ji(t, i, o, r, d), xb[1] = ji(e, n, a, s, d), p = M_(xb, yb), d >= 0 && p < v ? (c = d, v = p) : (_b[0] = ji(t, i, o, r, f), _b[1] = ji(e, n, a, s, f), g = M_(_b, yb), f <= 1 && g < v ? (c = f, v = g) : m *= .5); + return h && (h[0] = ji(t, i, o, r, c), h[1] = ji(e, n, a, s, c)), fb(v) + } + + function Qi(t, e, i, n) { + var o = 1 - n; + return o * (o * t + 2 * n * e) + n * n * i + } + + function tn(t, e, i, n) { + return 2 * ((1 - n) * (e - t) + n * (i - e)) + } + + function en(t, e, i, n, o) { + var a = t - 2 * e + i, r = 2 * (e - t), s = t - n, l = 0; + if (Ui(a)) Xi(r) && (c = -s / r) >= 0 && c <= 1 && (o[l++] = c); else { + var u = r * r - 4 * a * s; + if (Ui(u)) (c = -r / (2 * a)) >= 0 && c <= 1 && (o[l++] = c); else if (u > 0) { + var h = fb(u), c = (-r + h) / (2 * a), d = (-r - h) / (2 * a); + c >= 0 && c <= 1 && (o[l++] = c), d >= 0 && d <= 1 && (o[l++] = d) + } + } + return l + } + + function nn(t, e, i) { + var n = t + i - 2 * e; + return 0 === n ? .5 : (t - e) / n + } + + function on(t, e, i, n, o) { + var a = (e - t) * n + t, r = (i - e) * n + e, s = (r - a) * n + a; + o[0] = t, o[1] = a, o[2] = s, o[3] = s, o[4] = r, o[5] = i + } + + function an(t, e, i, n, o, a, r, s, l) { + var u, h = .005, c = 1 / 0; + yb[0] = r, yb[1] = s; + for (var d = 0; d < 1; d += .05) xb[0] = Qi(t, i, o, d), xb[1] = Qi(e, n, a, d), (m = M_(yb, xb)) < c && (u = d, c = m); + c = 1 / 0; + for (var f = 0; f < 32 && !(h < gb); f++) { + var p = u - h, g = u + h; + xb[0] = Qi(t, i, o, p), xb[1] = Qi(e, n, a, p); + var m = M_(xb, yb); + if (p >= 0 && m < c) u = p, c = m; else { + _b[0] = Qi(t, i, o, g), _b[1] = Qi(e, n, a, g); + var v = M_(_b, yb); + g <= 1 && v < c ? (u = g, c = v) : h *= .5 + } + } + return l && (l[0] = Qi(t, i, o, u), l[1] = Qi(e, n, a, u)), fb(c) + } + + function rn(t, e, i) { + if (0 !== t.length) { + var n, o = t[0], a = o[0], r = o[0], s = o[1], l = o[1]; + for (n = 1; n < t.length; n++) o = t[n], a = wb(a, o[0]), r = bb(r, o[0]), s = wb(s, o[1]), l = bb(l, o[1]); + e[0] = a, e[1] = s, i[0] = r, i[1] = l + } + } + + function sn(t, e, i, n, o, a) { + o[0] = wb(t, i), o[1] = wb(e, n), a[0] = bb(t, i), a[1] = bb(e, n) + } + + function ln(t, e, i, n, o, a, r, s, l, u) { + var h, c = Ki, d = ji, f = c(t, i, o, r, Cb); + for (l[0] = 1 / 0, l[1] = 1 / 0, u[0] = -1 / 0, u[1] = -1 / 0, h = 0; h < f; h++) { + var p = d(t, i, o, r, Cb[h]); + l[0] = wb(p, l[0]), u[0] = bb(p, u[0]) + } + for (f = c(e, n, a, s, Lb), h = 0; h < f; h++) { + var g = d(e, n, a, s, Lb[h]); + l[1] = wb(g, l[1]), u[1] = bb(g, u[1]) + } + l[0] = wb(t, l[0]), u[0] = bb(t, u[0]), l[0] = wb(r, l[0]), u[0] = bb(r, u[0]), l[1] = wb(e, l[1]), u[1] = bb(e, u[1]), l[1] = wb(s, l[1]), u[1] = bb(s, u[1]) + } + + function un(t, e, i, n, o, a, r, s) { + var l = nn, u = Qi, h = bb(wb(l(t, i, o), 1), 0), c = bb(wb(l(e, n, a), 1), 0), d = u(t, i, o, h), + f = u(e, n, a, c); + r[0] = wb(t, o, d), r[1] = wb(e, a, f), s[0] = bb(t, o, d), s[1] = bb(e, a, f) + } + + function hn(t, e, i, n, o, a, r, s, l) { + var u = tt, h = et, c = Math.abs(o - a); + if (c % Ib < 1e-4 && c > 1e-4) return s[0] = t - i, s[1] = e - n, l[0] = t + i, void (l[1] = e + n); + if (Tb[0] = Mb(o) * i + t, Tb[1] = Sb(o) * n + e, Db[0] = Mb(a) * i + t, Db[1] = Sb(a) * n + e, u(s, Tb, Db), h(l, Tb, Db), (o %= Ib) < 0 && (o += Ib), (a %= Ib) < 0 && (a += Ib), o > a && !r ? a += Ib : o < a && r && (o += Ib), r) { + var d = a; + a = o, o = d + } + for (var f = 0; f < a; f += Math.PI / 2) f > o && (Ab[0] = Mb(f) * i + t, Ab[1] = Sb(f) * n + e, u(s, Ab, s), h(l, Ab, l)) + } + + function cn(t, e, i, n, o, a, r) { + if (0 === o) return !1; + var s = o, l = 0, u = t; + if (r > e + s && r > n + s || r < e - s && r < n - s || a > t + s && a > i + s || a < t - s && a < i - s) return !1; + if (t === i) return Math.abs(a - t) <= s / 2; + var h = (l = (e - n) / (t - i)) * a - r + (u = (t * n - i * e) / (t - i)); + return h * h / (l * l + 1) <= s / 2 * s / 2 + } + + function dn(t, e, i, n, o, a, r, s, l, u, h) { + if (0 === l) return !1; + var c = l; + return !(h > e + c && h > n + c && h > a + c && h > s + c || h < e - c && h < n - c && h < a - c && h < s - c || u > t + c && u > i + c && u > o + c && u > r + c || u < t - c && u < i - c && u < o - c && u < r - c) && Ji(t, e, i, n, o, a, r, s, u, h, null) <= c / 2 + } + + function fn(t, e, i, n, o, a, r, s, l) { + if (0 === r) return !1; + var u = r; + return !(l > e + u && l > n + u && l > a + u || l < e - u && l < n - u && l < a - u || s > t + u && s > i + u && s > o + u || s < t - u && s < i - u && s < o - u) && an(t, e, i, n, o, a, s, l, null) <= u / 2 + } + + function pn(t) { + return (t %= Zb) < 0 && (t += Zb), t + } + + function gn(t, e, i, n, o, a, r, s, l) { + if (0 === r) return !1; + var u = r; + s -= t, l -= e; + var h = Math.sqrt(s * s + l * l); + if (h - u > i || h + u < i) return !1; + if (Math.abs(n - o) % Ub < 1e-4) return !0; + if (a) { + var c = n; + n = pn(o), o = pn(c) + } else n = pn(n), o = pn(o); + n > o && (o += Ub); + var d = Math.atan2(l, s); + return d < 0 && (d += Ub), d >= n && d <= o || d + Ub >= n && d + Ub <= o + } + + function mn(t, e, i, n, o, a) { + if (a > e && a > n || a < e && a < n) return 0; + if (n === e) return 0; + var r = n < e ? 1 : -1, s = (a - e) / (n - e); + 1 !== s && 0 !== s || (r = n < e ? .5 : -.5); + var l = s * (i - t) + t; + return l === o ? 1 / 0 : l > o ? r : 0 + } + + function vn(t, e) { + return Math.abs(t - e) < Yb + } + + function yn() { + var t = Kb[0]; + Kb[0] = Kb[1], Kb[1] = t + } + + function xn(t, e, i, n, o, a, r, s, l, u) { + if (u > e && u > n && u > a && u > s || u < e && u < n && u < a && u < s) return 0; + var h = qi(e, n, a, s, u, qb); + if (0 === h) return 0; + for (var c, d, f = 0, p = -1, g = 0; g < h; g++) { + var m = qb[g], v = 0 === m || 1 === m ? .5 : 1; + ji(t, i, o, r, m) < l || (p < 0 && (p = Ki(e, n, a, s, Kb), Kb[1] < Kb[0] && p > 1 && yn(), c = ji(e, n, a, s, Kb[0]), p > 1 && (d = ji(e, n, a, s, Kb[1]))), 2 == p ? m < Kb[0] ? f += c < e ? v : -v : m < Kb[1] ? f += d < c ? v : -v : f += s < d ? v : -v : m < Kb[0] ? f += c < e ? v : -v : f += s < c ? v : -v) + } + return f + } + + function _n(t, e, i, n, o, a, r, s) { + if (s > e && s > n && s > a || s < e && s < n && s < a) return 0; + var l = en(e, n, a, s, qb); + if (0 === l) return 0; + var u = nn(e, n, a); + if (u >= 0 && u <= 1) { + for (var h = 0, c = Qi(e, n, a, u), d = 0; d < l; d++) { + f = 0 === qb[d] || 1 === qb[d] ? .5 : 1; + (p = Qi(t, i, o, qb[d])) < r || (qb[d] < u ? h += c < e ? f : -f : h += a < c ? f : -f) + } + return h + } + var f = 0 === qb[0] || 1 === qb[0] ? .5 : 1, p = Qi(t, i, o, qb[0]); + return p < r ? 0 : a < e ? f : -f + } + + function wn(t, e, i, n, o, a, r, s) { + if ((s -= e) > i || s < -i) return 0; + u = Math.sqrt(i * i - s * s); + qb[0] = -u, qb[1] = u; + var l = Math.abs(n - o); + if (l < 1e-4) return 0; + if (l % jb < 1e-4) { + n = 0, o = jb; + p = a ? 1 : -1; + return r >= qb[0] + t && r <= qb[1] + t ? p : 0 + } + if (a) { + var u = n; + n = pn(o), o = pn(u) + } else n = pn(n), o = pn(o); + n > o && (o += jb); + for (var h = 0, c = 0; c < 2; c++) { + var d = qb[c]; + if (d + t > r) { + var f = Math.atan2(s, d), p = a ? 1 : -1; + f < 0 && (f = jb + f), (f >= n && f <= o || f + jb >= n && f + jb <= o) && (f > Math.PI / 2 && f < 1.5 * Math.PI && (p = -p), h += p) + } + } + return h + } + + function bn(t, e, i, n, o) { + for (var a = 0, r = 0, s = 0, l = 0, u = 0, h = 0; h < t.length;) { + var c = t[h++]; + switch (c === Xb.M && h > 1 && (i || (a += mn(r, s, l, u, n, o))), 1 == h && (l = r = t[h], u = s = t[h + 1]), c) { + case Xb.M: + r = l = t[h++], s = u = t[h++]; + break; + case Xb.L: + if (i) { + if (cn(r, s, t[h], t[h + 1], e, n, o)) return !0 + } else a += mn(r, s, t[h], t[h + 1], n, o) || 0; + r = t[h++], s = t[h++]; + break; + case Xb.C: + if (i) { + if (dn(r, s, t[h++], t[h++], t[h++], t[h++], t[h], t[h + 1], e, n, o)) return !0 + } else a += xn(r, s, t[h++], t[h++], t[h++], t[h++], t[h], t[h + 1], n, o) || 0; + r = t[h++], s = t[h++]; + break; + case Xb.Q: + if (i) { + if (fn(r, s, t[h++], t[h++], t[h], t[h + 1], e, n, o)) return !0 + } else a += _n(r, s, t[h++], t[h++], t[h], t[h + 1], n, o) || 0; + r = t[h++], s = t[h++]; + break; + case Xb.A: + var d = t[h++], f = t[h++], p = t[h++], g = t[h++], m = t[h++], v = t[h++], + y = (t[h++], 1 - t[h++]), x = Math.cos(m) * p + d, _ = Math.sin(m) * g + f; + h > 1 ? a += mn(r, s, x, _, n, o) : (l = x, u = _); + var w = (n - d) * g / p + d; + if (i) { + if (gn(d, f, g, m, m + v, y, e, w, o)) return !0 + } else a += wn(d, f, g, m, m + v, y, w, o); + r = Math.cos(m + v) * p + d, s = Math.sin(m + v) * g + f; + break; + case Xb.R: + l = r = t[h++], u = s = t[h++]; + var x = l + t[h++], _ = u + t[h++]; + if (i) { + if (cn(l, u, x, u, e, n, o) || cn(x, u, x, _, e, n, o) || cn(x, _, l, _, e, n, o) || cn(l, _, l, u, e, n, o)) return !0 + } else a += mn(x, u, x, _, n, o), a += mn(l, _, l, u, n, o); + break; + case Xb.Z: + if (i) { + if (cn(r, s, l, u, e, n, o)) return !0 + } else a += mn(r, s, l, u, n, o); + r = l, s = u + } + } + return i || vn(s, u) || (a += mn(r, s, l, u, n, o) || 0), 0 !== a + } + + function Sn(t, e, i) { + return bn(t, 0, !1, e, i) + } + + function Mn(t, e, i, n) { + return bn(t, e, !0, i, n) + } + + function In(t) { + $e.call(this, t), this.path = null + } + + function Tn(t, e, i, n, o, a, r, s, l, u, h) { + var c = l * (uS / 180), d = lS(c) * (t - i) / 2 + sS(c) * (e - n) / 2, + f = -1 * sS(c) * (t - i) / 2 + lS(c) * (e - n) / 2, p = d * d / (r * r) + f * f / (s * s); + p > 1 && (r *= rS(p), s *= rS(p)); + var g = (o === a ? -1 : 1) * rS((r * r * (s * s) - r * r * (f * f) - s * s * (d * d)) / (r * r * (f * f) + s * s * (d * d))) || 0, + m = g * r * f / s, v = g * -s * d / r, y = (t + i) / 2 + lS(c) * m - sS(c) * v, + x = (e + n) / 2 + sS(c) * m + lS(c) * v, _ = dS([1, 0], [(d - m) / r, (f - v) / s]), + w = [(d - m) / r, (f - v) / s], b = [(-1 * d - m) / r, (-1 * f - v) / s], S = dS(w, b); + cS(w, b) <= -1 && (S = uS), cS(w, b) >= 1 && (S = 0), 0 === a && S > 0 && (S -= 2 * uS), 1 === a && S < 0 && (S += 2 * uS), h.addData(u, y, x, r, s, _, S, c, a) + } + + function Dn(t) { + if (!t) return []; + var e, i = t.replace(/-/g, " -").replace(/ /g, " ").replace(/ /g, ",").replace(/,,/g, ","); + for (e = 0; e < aS.length; e++) i = i.replace(new RegExp(aS[e], "g"), "|" + aS[e]); + var n, o = i.split("|"), a = 0, r = 0, s = new Hb, l = Hb.CMD; + for (e = 1; e < o.length; e++) { + var u, h = o[e], c = h.charAt(0), d = 0, f = h.slice(1).replace(/e,-/g, "e-").split(","); + f.length > 0 && "" === f[0] && f.shift(); + for (var p = 0; p < f.length; p++) f[p] = parseFloat(f[p]); + for (; d < f.length && !isNaN(f[d]) && !isNaN(f[0]);) { + var g, m, v, y, x, _, w, b = a, S = r; + switch (c) { + case"l": + a += f[d++], r += f[d++], u = l.L, s.addData(u, a, r); + break; + case"L": + a = f[d++], r = f[d++], u = l.L, s.addData(u, a, r); + break; + case"m": + a += f[d++], r += f[d++], u = l.M, s.addData(u, a, r), c = "l"; + break; + case"M": + a = f[d++], r = f[d++], u = l.M, s.addData(u, a, r), c = "L"; + break; + case"h": + a += f[d++], u = l.L, s.addData(u, a, r); + break; + case"H": + a = f[d++], u = l.L, s.addData(u, a, r); + break; + case"v": + r += f[d++], u = l.L, s.addData(u, a, r); + break; + case"V": + r = f[d++], u = l.L, s.addData(u, a, r); + break; + case"C": + u = l.C, s.addData(u, f[d++], f[d++], f[d++], f[d++], f[d++], f[d++]), a = f[d - 2], r = f[d - 1]; + break; + case"c": + u = l.C, s.addData(u, f[d++] + a, f[d++] + r, f[d++] + a, f[d++] + r, f[d++] + a, f[d++] + r), a += f[d - 2], r += f[d - 1]; + break; + case"S": + g = a, m = r; + var M = s.len(), I = s.data; + n === l.C && (g += a - I[M - 4], m += r - I[M - 3]), u = l.C, b = f[d++], S = f[d++], a = f[d++], r = f[d++], s.addData(u, g, m, b, S, a, r); + break; + case"s": + g = a, m = r; + var M = s.len(), I = s.data; + n === l.C && (g += a - I[M - 4], m += r - I[M - 3]), u = l.C, b = a + f[d++], S = r + f[d++], a += f[d++], r += f[d++], s.addData(u, g, m, b, S, a, r); + break; + case"Q": + b = f[d++], S = f[d++], a = f[d++], r = f[d++], u = l.Q, s.addData(u, b, S, a, r); + break; + case"q": + b = f[d++] + a, S = f[d++] + r, a += f[d++], r += f[d++], u = l.Q, s.addData(u, b, S, a, r); + break; + case"T": + g = a, m = r; + var M = s.len(), I = s.data; + n === l.Q && (g += a - I[M - 4], m += r - I[M - 3]), a = f[d++], r = f[d++], u = l.Q, s.addData(u, g, m, a, r); + break; + case"t": + g = a, m = r; + var M = s.len(), I = s.data; + n === l.Q && (g += a - I[M - 4], m += r - I[M - 3]), a += f[d++], r += f[d++], u = l.Q, s.addData(u, g, m, a, r); + break; + case"A": + v = f[d++], y = f[d++], x = f[d++], _ = f[d++], w = f[d++], Tn(b = a, S = r, a = f[d++], r = f[d++], _, w, v, y, x, u = l.A, s); + break; + case"a": + v = f[d++], y = f[d++], x = f[d++], _ = f[d++], w = f[d++], Tn(b = a, S = r, a += f[d++], r += f[d++], _, w, v, y, x, u = l.A, s) + } + } + "z" !== c && "Z" !== c || (u = l.Z, s.addData(u)), n = u + } + return s.toStatic(), s + } + + function An(t, e) { + var i = Dn(t); + return e = e || {}, e.buildPath = function (t) { + if (t.setData) t.setData(i.data), (e = t.getContext()) && t.rebuildPath(e); else { + var e = t; + i.rebuildPath(e) + } + }, e.applyTransform = function (t) { + oS(i, t), this.dirty(!0) + }, e + } + + function Cn(t, e) { + return new In(An(t, e)) + } + + function Ln(t, e) { + return In.extend(An(t, e)) + } + + function kn(t, e, i, n, o, a, r) { + var s = .5 * (i - t), l = .5 * (n - e); + return (2 * (e - i) + s + l) * r + (-3 * (e - i) - 2 * s - l) * a + s * o + e + } + + function Pn(t, e, i) { + var n = e.points, o = e.smooth; + if (n && n.length >= 2) { + if (o && "spline" !== o) { + var a = _S(n, o, i, e.smoothConstraint); + t.moveTo(n[0][0], n[0][1]); + for (var r = n.length, s = 0; s < (i ? r : r - 1); s++) { + var l = a[2 * s], u = a[2 * s + 1], h = n[(s + 1) % r]; + t.bezierCurveTo(l[0], l[1], u[0], u[1], h[0], h[1]) + } + } else { + "spline" === o && (n = xS(n, i)), t.moveTo(n[0][0], n[0][1]); + for (var s = 1, c = n.length; s < c; s++) t.lineTo(n[s][0], n[s][1]) + } + i && t.closePath() + } + } + + function Nn(t, e, i) { + var n = t.cpx2, o = t.cpy2; + return null === n || null === o ? [(i ? Yi : ji)(t.x1, t.cpx1, t.cpx2, t.x2, e), (i ? Yi : ji)(t.y1, t.cpy1, t.cpy2, t.y2, e)] : [(i ? tn : Qi)(t.x1, t.cpx1, t.x2, e), (i ? tn : Qi)(t.y1, t.cpy1, t.y2, e)] + } + + function On(t) { + $e.call(this, t), this._displayables = [], this._temporaryDisplayables = [], this._cursor = 0, this.notClear = !0 + } + + function En(t) { + return In.extend(t) + } + + function Rn(t, e, i, n) { + var o = Cn(t, e), a = o.getBoundingRect(); + return i && ("center" === n && (i = Bn(i, a)), Vn(o, i)), o + } + + function zn(t, e, i) { + var n = new Je({ + style: {image: t, x: e.x, y: e.y, width: e.width, height: e.height}, onload: function (t) { + if ("center" === i) { + var o = {width: t.width, height: t.height}; + n.setStyle(Bn(e, o)) + } + } + }); + return n + } + + function Bn(t, e) { + var i, n = e.width / e.height, o = t.height * n; + return i = o <= t.width ? t.height : (o = t.width) / n, { + x: t.x + t.width / 2 - o / 2, + y: t.y + t.height / 2 - i / 2, + width: o, + height: i + } + } + + function Vn(t, e) { + if (t.applyTransform) { + var i = t.getBoundingRect().calculateTransform(e); + t.applyTransform(i) + } + } + + function Gn(t) { + var e = t.shape, i = t.style.lineWidth; + return NS(2 * e.x1) === NS(2 * e.x2) && (e.x1 = e.x2 = Fn(e.x1, i, !0)), NS(2 * e.y1) === NS(2 * e.y2) && (e.y1 = e.y2 = Fn(e.y1, i, !0)), t + } + + function Wn(t) { + var e = t.shape, i = t.style.lineWidth, n = e.x, o = e.y, a = e.width, r = e.height; + return e.x = Fn(e.x, i, !0), e.y = Fn(e.y, i, !0), e.width = Math.max(Fn(n + a, i, !1) - e.x, 0 === a ? 0 : 1), e.height = Math.max(Fn(o + r, i, !1) - e.y, 0 === r ? 0 : 1), t + } + + function Fn(t, e, i) { + var n = NS(2 * t); + return (n + NS(e)) % 2 == 0 ? n / 2 : (n + (i ? 1 : -1)) / 2 + } + + function Hn(t) { + return null != t && "none" != t + } + + function Zn(t) { + return "string" == typeof t ? kt(t, -.1) : t + } + + function Un(t) { + if (t.__hoverStlDirty) { + var e = t.style.stroke, i = t.style.fill, n = t.__hoverStl; + n.fill = n.fill || (Hn(i) ? Zn(i) : null), n.stroke = n.stroke || (Hn(e) ? Zn(e) : null); + var o = {}; + for (var a in n) null != n[a] && (o[a] = t.style[a]); + t.__normalStl = o, t.__hoverStlDirty = !1 + } + } + + function Xn(t) { + if (!t.__isHover) { + if (Un(t), t.useHoverLayer) t.__zr && t.__zr.addHover(t, t.__hoverStl); else { + var e = t.style, i = e.insideRollbackOpt; + i && uo(e), e.extendFrom(t.__hoverStl), i && (lo(e, e.insideOriginalTextPosition, i), null == e.textFill && (e.textFill = i.autoColor)), t.dirty(!1), t.z2 += 1 + } + t.__isHover = !0 + } + } + + function jn(t) { + if (t.__isHover) { + var e = t.__normalStl; + t.useHoverLayer ? t.__zr && t.__zr.removeHover(t) : (e && t.setStyle(e), t.z2 -= 1), t.__isHover = !1 + } + } + + function Yn(t) { + "group" === t.type ? t.traverse(function (t) { + "group" !== t.type && Xn(t) + }) : Xn(t) + } + + function qn(t) { + "group" === t.type ? t.traverse(function (t) { + "group" !== t.type && jn(t) + }) : jn(t) + } + + function Kn(t, e) { + t.__hoverStl = t.hoverStyle || e || {}, t.__hoverStlDirty = !0, t.__isHover && Un(t) + } + + function $n(t) { + this.__hoverSilentOnTouch && t.zrByTouch || !this.__isEmphasis && Yn(this) + } + + function Jn(t) { + this.__hoverSilentOnTouch && t.zrByTouch || !this.__isEmphasis && qn(this) + } + + function Qn() { + this.__isEmphasis = !0, Yn(this) + } + + function to() { + this.__isEmphasis = !1, qn(this) + } + + function eo(t, e, i) { + t.__hoverSilentOnTouch = i && i.hoverSilentOnTouch, "group" === t.type ? t.traverse(function (t) { + "group" !== t.type && Kn(t, e) + }) : Kn(t, e), t.on("mouseover", $n).on("mouseout", Jn), t.on("emphasis", Qn).on("normal", to) + } + + function io(t, e, i, n, o, a, r) { + var s, l = (o = o || RS).labelFetcher, u = o.labelDataIndex, h = o.labelDimIndex, c = i.getShallow("show"), + d = n.getShallow("show"); + (c || d) && (l && (s = l.getFormattedLabel(u, "normal", null, h)), null == s && (s = x(o.defaultText) ? o.defaultText(u, o) : o.defaultText)); + var f = c ? s : null, p = d ? D(l ? l.getFormattedLabel(u, "emphasis", null, h) : null, s) : null; + null == f && null == p || (no(t, i, a, o), no(e, n, r, o, !0)), t.text = f, e.text = p + } + + function no(t, e, i, n, o) { + return oo(t, e, n, o), i && a(t, i), t.host && t.host.dirty && t.host.dirty(!1), t + } + + function oo(t, e, i, n) { + if ((i = i || RS).isRectText) { + var o = e.getShallow("position") || (n ? null : "inside"); + "outside" === o && (o = "top"), t.textPosition = o, t.textOffset = e.getShallow("offset"); + var a = e.getShallow("rotate"); + null != a && (a *= Math.PI / 180), t.textRotation = a, t.textDistance = D(e.getShallow("distance"), n ? null : 5) + } + var r, s = e.ecModel, l = s && s.option.textStyle, u = ao(e); + if (u) { + r = {}; + for (var h in u) if (u.hasOwnProperty(h)) { + var c = e.getModel(["rich", h]); + ro(r[h] = {}, c, l, i, n) + } + } + return t.rich = r, ro(t, e, l, i, n, !0), i.forceRich && !i.textStyle && (i.textStyle = {}), t + } + + function ao(t) { + for (var e; t && t !== t.ecModel;) { + var i = (t.option || RS).rich; + if (i) { + e = e || {}; + for (var n in i) i.hasOwnProperty(n) && (e[n] = 1) + } + t = t.parentModel + } + return e + } + + function ro(t, e, i, n, o, a) { + if (i = !o && i || RS, t.textFill = so(e.getShallow("color"), n) || i.color, t.textStroke = so(e.getShallow("textBorderColor"), n) || i.textBorderColor, t.textStrokeWidth = D(e.getShallow("textBorderWidth"), i.textBorderWidth), !o) { + if (a) { + var r = t.textPosition; + t.insideRollback = lo(t, r, n), t.insideOriginalTextPosition = r, t.insideRollbackOpt = n + } + null == t.textFill && (t.textFill = n.autoColor) + } + t.fontStyle = e.getShallow("fontStyle") || i.fontStyle, t.fontWeight = e.getShallow("fontWeight") || i.fontWeight, t.fontSize = e.getShallow("fontSize") || i.fontSize, t.fontFamily = e.getShallow("fontFamily") || i.fontFamily, t.textAlign = e.getShallow("align"), t.textVerticalAlign = e.getShallow("verticalAlign") || e.getShallow("baseline"), t.textLineHeight = e.getShallow("lineHeight"), t.textWidth = e.getShallow("width"), t.textHeight = e.getShallow("height"), t.textTag = e.getShallow("tag"), a && n.disableBox || (t.textBackgroundColor = so(e.getShallow("backgroundColor"), n), t.textPadding = e.getShallow("padding"), t.textBorderColor = so(e.getShallow("borderColor"), n), t.textBorderWidth = e.getShallow("borderWidth"), t.textBorderRadius = e.getShallow("borderRadius"), t.textBoxShadowColor = e.getShallow("shadowColor"), t.textBoxShadowBlur = e.getShallow("shadowBlur"), t.textBoxShadowOffsetX = e.getShallow("shadowOffsetX"), t.textBoxShadowOffsetY = e.getShallow("shadowOffsetY")), t.textShadowColor = e.getShallow("textShadowColor") || i.textShadowColor, t.textShadowBlur = e.getShallow("textShadowBlur") || i.textShadowBlur, t.textShadowOffsetX = e.getShallow("textShadowOffsetX") || i.textShadowOffsetX, t.textShadowOffsetY = e.getShallow("textShadowOffsetY") || i.textShadowOffsetY + } + + function so(t, e) { + return "auto" !== t ? t : e && e.autoColor ? e.autoColor : null + } + + function lo(t, e, i) { + var n, o = i.useInsideStyle; + return null == t.textFill && !1 !== o && (!0 === o || i.isRectText && e && "string" == typeof e && e.indexOf("inside") >= 0) && (n = { + textFill: null, + textStroke: t.textStroke, + textStrokeWidth: t.textStrokeWidth + }, t.textFill = "#fff", null == t.textStroke && (t.textStroke = i.autoColor, null == t.textStrokeWidth && (t.textStrokeWidth = 2))), n + } + + function uo(t) { + var e = t.insideRollback; + e && (t.textFill = e.textFill, t.textStroke = e.textStroke, t.textStrokeWidth = e.textStrokeWidth) + } + + function ho(t, e) { + var i = e || e.getModel("textStyle"); + return P([t.fontStyle || i && i.getShallow("fontStyle") || "", t.fontWeight || i && i.getShallow("fontWeight") || "", (t.fontSize || i && i.getShallow("fontSize") || 12) + "px", t.fontFamily || i && i.getShallow("fontFamily") || "sans-serif"].join(" ")) + } + + function co(t, e, i, n, o, a) { + if ("function" == typeof o && (a = o, o = null), n && n.isAnimationEnabled()) { + var r = t ? "Update" : "", s = n.getShallow("animationDuration" + r), + l = n.getShallow("animationEasing" + r), u = n.getShallow("animationDelay" + r); + "function" == typeof u && (u = u(o, n.getAnimationDelayParams ? n.getAnimationDelayParams(e, o) : null)), "function" == typeof s && (s = s(o)), s > 0 ? e.animateTo(i, s, u || 0, l, a, !!a) : (e.stopAnimation(), e.attr(i), a && a()) + } else e.stopAnimation(), e.attr(i), a && a() + } + + function fo(t, e, i, n, o) { + co(!0, t, e, i, n, o) + } + + function po(t, e, i, n, o) { + co(!1, t, e, i, n, o) + } + + function go(t, e) { + for (var i = lt([]); t && t !== e;) ht(i, t.getLocalTransform(), i), t = t.parent; + return i + } + + function mo(t, e, i) { + return e && !c(e) && (e = E_.getLocalTransform(e)), i && (e = pt([], e)), Q([], t, e) + } + + function vo(t, e, i) { + var n = 0 === e[4] || 0 === e[5] || 0 === e[0] ? 1 : Math.abs(2 * e[4] / e[0]), + o = 0 === e[4] || 0 === e[5] || 0 === e[2] ? 1 : Math.abs(2 * e[4] / e[2]), + a = ["left" === t ? -n : "right" === t ? n : 0, "top" === t ? -o : "bottom" === t ? o : 0]; + return a = mo(a, e, i), Math.abs(a[0]) > Math.abs(a[1]) ? a[0] > 0 ? "right" : "left" : a[1] > 0 ? "bottom" : "top" + } + + function yo(t, e, i, n) { + function o(t) { + var e = {position: W(t.position), rotation: t.rotation}; + return t.shape && (e.shape = a({}, t.shape)), e + } + + if (t && e) { + var r = function (t) { + var e = {}; + return t.traverse(function (t) { + !t.isGroup && t.anid && (e[t.anid] = t) + }), e + }(t); + e.traverse(function (t) { + if (!t.isGroup && t.anid) { + var e = r[t.anid]; + if (e) { + var n = o(t); + t.attr(o(e)), fo(t, n, i, t.dataIndex) + } + } + }) + } + } + + function xo(t, e) { + return f(t, function (t) { + var i = t[0]; + i = OS(i, e.x), i = ES(i, e.x + e.width); + var n = t[1]; + return n = OS(n, e.y), n = ES(n, e.y + e.height), [i, n] + }) + } + + function _o(t, e, i) { + var n = (e = a({rectHover: !0}, e)).style = {strokeNoScale: !0}; + if (i = i || { + x: -1, + y: -1, + width: 2, + height: 2 + }, t) return 0 === t.indexOf("image://") ? (n.image = t.slice(8), r(n, i), new Je(e)) : Rn(t.replace("path://", ""), e, i, "center") + } + + function wo(t, e, i) { + this.parentModel = e, this.ecModel = i, this.option = t + } + + function bo(t, e, i) { + for (var n = 0; n < e.length && (!e[n] || null != (t = t && "object" == typeof t ? t[e[n]] : null)); n++) ; + return null == t && i && (t = i.get(e)), t + } + + function So(t, e) { + var i = ZS(t).getParent; + return i ? i.call(t, e) : t.parentModel + } + + function Mo(t) { + return [t || "", US++, Math.random().toFixed(5)].join("_") + } + + function Io(t) { + return t.replace(/^\s+/, "").replace(/\s+$/, "") + } + + function To(t, e, i, n) { + var o = e[1] - e[0], a = i[1] - i[0]; + if (0 === o) return 0 === a ? i[0] : (i[0] + i[1]) / 2; + if (n) if (o > 0) { + if (t <= e[0]) return i[0]; + if (t >= e[1]) return i[1] + } else { + if (t >= e[0]) return i[0]; + if (t <= e[1]) return i[1] + } else { + if (t === e[0]) return i[0]; + if (t === e[1]) return i[1] + } + return (t - e[0]) / o * a + i[0] + } + + function Do(t, e) { + switch (t) { + case"center": + case"middle": + t = "50%"; + break; + case"left": + case"top": + t = "0%"; + break; + case"right": + case"bottom": + t = "100%" + } + return "string" == typeof t ? Io(t).match(/%$/) ? parseFloat(t) / 100 * e : parseFloat(t) : null == t ? NaN : +t + } + + function Ao(t, e, i) { + return null == e && (e = 10), e = Math.min(Math.max(0, e), 20), t = (+t).toFixed(e), i ? t : +t + } + + function Co(t) { + return t.sort(function (t, e) { + return t - e + }), t + } + + function Lo(t) { + if (t = +t, isNaN(t)) return 0; + for (var e = 1, i = 0; Math.round(t * e) / e !== t;) e *= 10, i++; + return i + } + + function ko(t) { + var e = t.toString(), i = e.indexOf("e"); + if (i > 0) { + var n = +e.slice(i + 1); + return n < 0 ? -n : 0 + } + var o = e.indexOf("."); + return o < 0 ? 0 : e.length - 1 - o + } + + function Po(t, e) { + var i = Math.log, n = Math.LN10, o = Math.floor(i(t[1] - t[0]) / n), + a = Math.round(i(Math.abs(e[1] - e[0])) / n), r = Math.min(Math.max(-o + a, 0), 20); + return isFinite(r) ? r : 20 + } + + function No(t, e, i) { + if (!t[e]) return 0; + var n = p(t, function (t, e) { + return t + (isNaN(e) ? 0 : e) + }, 0); + if (0 === n) return 0; + for (var o = Math.pow(10, i), a = f(t, function (t) { + return (isNaN(t) ? 0 : t) / n * o * 100 + }), r = 100 * o, s = f(a, function (t) { + return Math.floor(t) + }), l = p(s, function (t, e) { + return t + e + }, 0), u = f(a, function (t, e) { + return t - s[e] + }); l < r;) { + for (var h = Number.NEGATIVE_INFINITY, c = null, d = 0, g = u.length; d < g; ++d) u[d] > h && (h = u[d], c = d); + ++s[c], u[c] = 0, ++l + } + return s[e] / o + } + + function Oo(t) { + var e = 2 * Math.PI; + return (t % e + e) % e + } + + function Eo(t) { + return t > -XS && t < XS + } + + function Ro(t) { + if (t instanceof Date) return t; + if ("string" == typeof t) { + var e = YS.exec(t); + if (!e) return new Date(NaN); + if (e[8]) { + var i = +e[4] || 0; + return "Z" !== e[8].toUpperCase() && (i -= e[8].slice(0, 3)), new Date(Date.UTC(+e[1], +(e[2] || 1) - 1, +e[3] || 1, i, +(e[5] || 0), +e[6] || 0, +e[7] || 0)) + } + return new Date(+e[1], +(e[2] || 1) - 1, +e[3] || 1, +e[4] || 0, +(e[5] || 0), +e[6] || 0, +e[7] || 0) + } + return null == t ? new Date(NaN) : new Date(Math.round(t)) + } + + function zo(t) { + return Math.pow(10, Bo(t)) + } + + function Bo(t) { + return Math.floor(Math.log(t) / Math.LN10) + } + + function Vo(t, e) { + var i, n = Bo(t), o = Math.pow(10, n), a = t / o; + return i = e ? a < 1.5 ? 1 : a < 2.5 ? 2 : a < 4 ? 3 : a < 7 ? 5 : 10 : a < 1 ? 1 : a < 2 ? 2 : a < 3 ? 3 : a < 5 ? 5 : 10, t = i * o, n >= -20 ? +t.toFixed(n < 0 ? -n : 0) : t + } + + function Go(t) { + function e(t, i, n) { + return t.interval[n] < i.interval[n] || t.interval[n] === i.interval[n] && (t.close[n] - i.close[n] == (n ? -1 : 1) || !n && e(t, i, 1)) + } + + t.sort(function (t, i) { + return e(t, i, 0) ? -1 : 1 + }); + for (var i = -1 / 0, n = 1, o = 0; o < t.length;) { + for (var a = t[o].interval, r = t[o].close, s = 0; s < 2; s++) a[s] <= i && (a[s] = i, r[s] = s ? 1 : 1 - n), i = a[s], n = r[s]; + a[0] === a[1] && r[0] * r[1] != 1 ? t.splice(o, 1) : o++ + } + return t + } + + function Wo(t) { + return t - parseFloat(t) >= 0 + } + + function Fo(t) { + return isNaN(t) ? "-" : (t = (t + "").split("."))[0].replace(/(\d{1,3})(?=(?:\d{3})+(?!\d))/g, "$1,") + (t.length > 1 ? "." + t[1] : "") + } + + function Ho(t, e) { + return t = (t || "").toLowerCase().replace(/-(.)/g, function (t, e) { + return e.toUpperCase() + }), e && t && (t = t.charAt(0).toUpperCase() + t.slice(1)), t + } + + function Zo(t) { + return null == t ? "" : (t + "").replace($S, function (t, e) { + return JS[e] + }) + } + + function Uo(t, e, i) { + y(e) || (e = [e]); + var n = e.length; + if (!n) return ""; + for (var o = e[0].$vars || [], a = 0; a < o.length; a++) { + var r = QS[a]; + t = t.replace(tM(r), tM(r, 0)) + } + for (var s = 0; s < n; s++) for (var l = 0; l < o.length; l++) { + var u = e[s][o[l]]; + t = t.replace(tM(QS[l], s), i ? Zo(u) : u) + } + return t + } + + function Xo(t, e, i) { + return d(e, function (e, n) { + t = t.replace("{" + n + "}", i ? Zo(e) : e) + }), t + } + + function jo(t, e) { + var i = (t = _(t) ? {color: t, extraCssText: e} : t || {}).color, n = t.type, e = t.extraCssText; + return i ? "subItem" === n ? '' : '' : "" + } + + function Yo(t, e) { + return t += "", "0000".substr(0, e - t.length) + t + } + + function qo(t, e, i) { + "week" !== t && "month" !== t && "quarter" !== t && "half-year" !== t && "year" !== t || (t = "MM-dd\nyyyy"); + var n = Ro(e), o = i ? "UTC" : "", a = n["get" + o + "FullYear"](), r = n["get" + o + "Month"]() + 1, + s = n["get" + o + "Date"](), l = n["get" + o + "Hours"](), u = n["get" + o + "Minutes"](), + h = n["get" + o + "Seconds"](), c = n["get" + o + "Milliseconds"](); + return t = t.replace("MM", Yo(r, 2)).replace("M", r).replace("yyyy", a).replace("yy", a % 100).replace("dd", Yo(s, 2)).replace("d", s).replace("hh", Yo(l, 2)).replace("h", l).replace("mm", Yo(u, 2)).replace("m", u).replace("ss", Yo(h, 2)).replace("s", h).replace("SSS", Yo(c, 3)) + } + + function Ko(t) { + return t ? t.charAt(0).toUpperCase() + t.substr(1) : t + } + + function $o(t, e, i, n, o) { + var a = 0, r = 0; + null == n && (n = 1 / 0), null == o && (o = 1 / 0); + var s = 0; + e.eachChild(function (l, u) { + var h, c, d = l.position, f = l.getBoundingRect(), p = e.childAt(u + 1), g = p && p.getBoundingRect(); + if ("horizontal" === t) { + var m = f.width + (g ? -g.x + f.x : 0); + (h = a + m) > n || l.newline ? (a = 0, h = m, r += s + i, s = f.height) : s = Math.max(s, f.height) + } else { + var v = f.height + (g ? -g.y + f.y : 0); + (c = r + v) > o || l.newline ? (a += s + i, r = 0, c = v, s = f.width) : s = Math.max(s, f.width) + } + l.newline || (d[0] = a, d[1] = r, "horizontal" === t ? a = h + i : r = c + i) + }) + } + + function Jo(t, e, i) { + var n = e.width, o = e.height, a = Do(t.x, n), r = Do(t.y, o), s = Do(t.x2, n), l = Do(t.y2, o); + return (isNaN(a) || isNaN(parseFloat(t.x))) && (a = 0), (isNaN(s) || isNaN(parseFloat(t.x2))) && (s = n), (isNaN(r) || isNaN(parseFloat(t.y))) && (r = 0), (isNaN(l) || isNaN(parseFloat(t.y2))) && (l = o), i = KS(i || 0), { + width: Math.max(s - a - i[1] - i[3], 0), + height: Math.max(l - r - i[0] - i[2], 0) + } + } + + function Qo(t, e, i) { + i = KS(i || 0); + var n = e.width, o = e.height, a = Do(t.left, n), r = Do(t.top, o), s = Do(t.right, n), l = Do(t.bottom, o), + u = Do(t.width, n), h = Do(t.height, o), c = i[2] + i[0], d = i[1] + i[3], f = t.aspect; + switch (isNaN(u) && (u = n - s - d - a), isNaN(h) && (h = o - l - c - r), null != f && (isNaN(u) && isNaN(h) && (f > n / o ? u = .8 * n : h = .8 * o), isNaN(u) && (u = f * h), isNaN(h) && (h = u / f)), isNaN(a) && (a = n - s - u - d), isNaN(r) && (r = o - l - h - c), t.left || t.right) { + case"center": + a = n / 2 - u / 2 - i[3]; + break; + case"right": + a = n - u - d + } + switch (t.top || t.bottom) { + case"middle": + case"center": + r = o / 2 - h / 2 - i[0]; + break; + case"bottom": + r = o - h - c + } + a = a || 0, r = r || 0, isNaN(u) && (u = n - d - a - (s || 0)), isNaN(h) && (h = o - c - r - (l || 0)); + var p = new $t(a + i[3], r + i[0], u, h); + return p.margin = i, p + } + + function ta(t, e, i, n, o) { + var a = !o || !o.hv || o.hv[0], s = !o || !o.hv || o.hv[1], l = o && o.boundingMode || "all"; + if (a || s) { + var u; + if ("raw" === l) u = "group" === t.type ? new $t(0, 0, +e.width || 0, +e.height || 0) : t.getBoundingRect(); else if (u = t.getBoundingRect(), t.needLocalTransform()) { + var h = t.getLocalTransform(); + (u = u.clone()).applyTransform(h) + } + e = Qo(r({width: u.width, height: u.height}, e), i, n); + var c = t.position, d = a ? e.x - u.x : 0, f = s ? e.y - u.y : 0; + t.attr("position", "raw" === l ? [d, f] : [c[0] + d, c[1] + f]) + } + } + + function ea(t, e) { + return null != t[rM[e][0]] || null != t[rM[e][1]] && null != t[rM[e][2]] + } + + function ia(t, e, i) { + function n(i, n) { + var r = {}, l = 0, u = {}, h = 0; + if (oM(i, function (e) { + u[e] = t[e] + }), oM(i, function (t) { + o(e, t) && (r[t] = u[t] = e[t]), a(r, t) && l++, a(u, t) && h++ + }), s[n]) return a(e, i[1]) ? u[i[2]] = null : a(e, i[2]) && (u[i[1]] = null), u; + if (2 !== h && l) { + if (l >= 2) return r; + for (var c = 0; c < i.length; c++) { + var d = i[c]; + if (!o(r, d) && o(t, d)) { + r[d] = t[d]; + break + } + } + return r + } + return u + } + + function o(t, e) { + return t.hasOwnProperty(e) + } + + function a(t, e) { + return null != t[e] && "auto" !== t[e] + } + + function r(t, e, i) { + oM(t, function (t) { + e[t] = i[t] + }) + } + + !w(i) && (i = {}); + var s = i.ignoreSize; + !y(s) && (s = [s, s]); + var l = n(rM[0], 0), u = n(rM[1], 1); + r(rM[0], t, l), r(rM[1], t, u) + } + + function na(t) { + return oa({}, t) + } + + function oa(t, e) { + return e && t && oM(aM, function (i) { + e.hasOwnProperty(i) && (t[i] = e[i]) + }), t + } + + function aa(t, e) { + for (var i = t.length, n = 0; n < i; n++) if (t[n].length > e) return t[n]; + return t[i - 1] + } + + function ra(t) { + var e = t.get("coordinateSystem"), i = {coordSysName: e, coordSysDims: [], axisMap: R(), categoryAxisMap: R()}, + n = gM[e]; + if (n) return n(t, i, i.axisMap, i.categoryAxisMap), i + } + + function sa(t) { + return "category" === t.get("type") + } + + function la(t) { + this.fromDataset = t.fromDataset, this.data = t.data || (t.sourceFormat === xM ? {} : []), this.sourceFormat = t.sourceFormat || _M, this.seriesLayoutBy = t.seriesLayoutBy || bM, this.dimensionsDefine = t.dimensionsDefine, this.encodeDefine = t.encodeDefine && R(t.encodeDefine), this.startIndex = t.startIndex || 0, this.dimensionsDetectCount = t.dimensionsDetectCount + } + + function ua(t) { + var e = t.option.source, i = _M; + if (S(e)) i = wM; else if (y(e)) for (var n = 0, o = e.length; n < o; n++) { + var a = e[n]; + if (null != a) { + if (y(a)) { + i = vM; + break + } + if (w(a)) { + i = yM; + break + } + } + } else if (w(e)) { + for (var r in e) if (e.hasOwnProperty(r) && c(e[r])) { + i = xM; + break + } + } else if (null != e) throw new Error("Invalid data"); + MM(t).sourceFormat = i + } + + function ha(t) { + return MM(t).source + } + + function ca(t) { + MM(t).datasetMap = R() + } + + function da(t) { + var e = t.option, i = e.data, n = S(i) ? wM : mM, o = !1, a = e.seriesLayoutBy, r = e.sourceHeader, + s = e.dimensions, l = ya(t); + if (l) { + var u = l.option; + i = u.source, n = MM(l).sourceFormat, o = !0, a = a || u.seriesLayoutBy, null == r && (r = u.sourceHeader), s = s || u.dimensions + } + var h = fa(i, n, a, r, s), c = e.encode; + !c && l && (c = va(t, l, i, n, a, h)), MM(t).source = new la({ + data: i, + fromDataset: o, + seriesLayoutBy: a, + sourceFormat: n, + dimensionsDefine: h.dimensionsDefine, + startIndex: h.startIndex, + dimensionsDetectCount: h.dimensionsDetectCount, + encodeDefine: c + }) + } + + function fa(t, e, i, n, o) { + if (!t) return {dimensionsDefine: pa(o)}; + var a, r, s; + if (e === vM) "auto" === n || null == n ? ga(function (t) { + null != t && "-" !== t && (_(t) ? null == r && (r = 1) : r = 0) + }, i, t, 10) : r = n ? 1 : 0, o || 1 !== r || (o = [], ga(function (t, e) { + o[e] = null != t ? t : "" + }, i, t)), a = o ? o.length : i === SM ? t.length : t[0] ? t[0].length : null; else if (e === yM) o || (o = ma(t), s = !0); else if (e === xM) o || (o = [], s = !0, d(t, function (t, e) { + o.push(e) + })); else if (e === mM) { + var l = Ii(t[0]); + a = y(l) && l.length || 1 + } + var u; + return s && d(o, function (t, e) { + "name" === (w(t) ? t.name : t) && (u = e) + }), {startIndex: r, dimensionsDefine: pa(o), dimensionsDetectCount: a, potentialNameDimIndex: u} + } + + function pa(t) { + if (t) { + var e = R(); + return f(t, function (t, i) { + if (null == (t = a({}, w(t) ? t : {name: t})).name) return t; + t.name += "", null == t.displayName && (t.displayName = t.name); + var n = e.get(t.name); + return n ? t.name += "-" + n.count++ : e.set(t.name, {count: 1}), t + }) + } + } + + function ga(t, e, i, n) { + if (null == n && (n = 1 / 0), e === SM) for (a = 0; a < i.length && a < n; a++) t(i[a] ? i[a][0] : null, a); else for (var o = i[0] || [], a = 0; a < o.length && a < n; a++) t(o[a], a) + } + + function ma(t) { + for (var e, i = 0; i < t.length && !(e = t[i++]);) ; + if (e) { + var n = []; + return d(e, function (t, e) { + n.push(e) + }), n + } + } + + function va(t, e, i, n, o, a) { + var r = ra(t), s = {}, l = [], u = [], h = t.subType, c = R(["pie", "map", "funnel"]), + f = R(["line", "bar", "pictorialBar", "scatter", "effectScatter", "candlestick", "boxplot"]); + if (r && null != f.get(h)) { + var p = t.ecModel, g = MM(p).datasetMap, m = e.uid + "_" + o, + v = g.get(m) || g.set(m, {categoryWayDim: 1, valueWayDim: 0}); + d(r.coordSysDims, function (t) { + if (null == r.firstCategoryDimIndex) { + e = v.valueWayDim++; + s[t] = e, u.push(e) + } else if (r.categoryAxisMap.get(t)) s[t] = 0, l.push(0); else { + var e = v.categoryWayDim++; + s[t] = e, u.push(e) + } + }) + } else if (null != c.get(h)) { + for (var y, x = 0; x < 5 && null == y; x++) _a(i, n, o, a.dimensionsDefine, a.startIndex, x) || (y = x); + if (null != y) { + s.value = y; + var _ = a.potentialNameDimIndex || Math.max(y - 1, 0); + u.push(_), l.push(_) + } + } + return l.length && (s.itemName = l), u.length && (s.seriesName = u), s + } + + function ya(t) { + var e = t.option; + if (!e.data) return t.ecModel.getComponent("dataset", e.datasetIndex || 0) + } + + function xa(t, e) { + return _a(t.data, t.sourceFormat, t.seriesLayoutBy, t.dimensionsDefine, t.startIndex, e) + } + + function _a(t, e, i, n, o, a) { + function r(t) { + return (null == t || !isFinite(t) || "" === t) && (!(!_(t) || "-" === t) || void 0) + } + + var s; + if (S(t)) return !1; + var l; + if (n && (l = w(l = n[a]) ? l.name : l), e === vM) if (i === SM) { + for (var u = t[a], h = 0; h < (u || []).length && h < 5; h++) if (null != (s = r(u[o + h]))) return s + } else for (h = 0; h < t.length && h < 5; h++) { + var c = t[o + h]; + if (c && null != (s = r(c[a]))) return s + } else if (e === yM) { + if (!l) return; + for (h = 0; h < t.length && h < 5; h++) if ((d = t[h]) && null != (s = r(d[l]))) return s + } else if (e === xM) { + if (!l) return; + if (!(u = t[l]) || S(u)) return !1; + for (h = 0; h < u.length && h < 5; h++) if (null != (s = r(u[h]))) return s + } else if (e === mM) for (h = 0; h < t.length && h < 5; h++) { + var d = t[h], f = Ii(d); + if (!y(f)) return !1; + if (null != (s = r(f[a]))) return s + } + return !1 + } + + function wa(t, e) { + if (e) { + var i = e.seiresIndex, n = e.seriesId, o = e.seriesName; + return null != i && t.componentIndex !== i || null != n && t.id !== n || null != o && t.name !== o + } + } + + function ba(t, e) { + var o = t.color && !t.colorLayer; + d(e, function (e, a) { + "colorLayer" === a && o || hM.hasClass(a) || ("object" == typeof e ? t[a] = t[a] ? n(t[a], e, !1) : i(e) : null == t[a] && (t[a] = e)) + }) + } + + function Sa(t) { + t = t, this.option = {}, this.option[IM] = 1, this._componentsMap = R({series: []}), this._seriesIndices, this._seriesIndicesMap, ba(t, this._theme.option), n(t, dM, !1), this.mergeOption(t) + } + + function Ma(t, e) { + y(e) || (e = e ? [e] : []); + var i = {}; + return d(e, function (e) { + i[e] = (t.get(e) || []).slice() + }), i + } + + function Ia(t, e, i) { + return e.type ? e.type : i ? i.subType : hM.determineSubType(t, e) + } + + function Ta(t, e) { + t._seriesIndicesMap = R(t._seriesIndices = f(e, function (t) { + return t.componentIndex + }) || []) + } + + function Da(t, e) { + return e.hasOwnProperty("subType") ? g(t, function (t) { + return t.subType === e.subType + }) : t + } + + function Aa(t) { + d(DM, function (e) { + this[e] = m(t[e], t) + }, this) + } + + function Ca() { + this._coordinateSystems = [] + } + + function La(t) { + this._api = t, this._timelineOptions = [], this._mediaList = [], this._mediaDefault, this._currentMediaIndices = [], this._optionBackup, this._newBaseOption + } + + function ka(t, e, i) { + var n, o, a = [], r = [], s = t.timeline; + if (t.baseOption && (o = t.baseOption), (s || t.options) && (o = o || {}, a = (t.options || []).slice()), t.media) { + o = o || {}; + var l = t.media; + CM(l, function (t) { + t && t.option && (t.query ? r.push(t) : n || (n = t)) + }) + } + return o || (o = t), o.timeline || (o.timeline = s), CM([o].concat(a).concat(f(r, function (t) { + return t.option + })), function (t) { + CM(e, function (e) { + e(t, i) + }) + }), {baseOption: o, timelineOptions: a, mediaDefault: n, mediaList: r} + } + + function Pa(t, e, i) { + var n = {width: e, height: i, aspectratio: e / i}, o = !0; + return d(t, function (t, e) { + var i = e.match(NM); + if (i && i[1] && i[2]) { + var a = i[1], r = i[2].toLowerCase(); + Na(n[r], t, a) || (o = !1) + } + }), o + } + + function Na(t, e, i) { + return "min" === i ? t >= e : "max" === i ? t <= e : t === e + } + + function Oa(t, e) { + return t.join(",") === e.join(",") + } + + function Ea(t, e) { + CM(e = e || {}, function (e, i) { + if (null != e) { + var n = t[i]; + if (hM.hasClass(i)) { + e = Si(e); + var o = Di(n = Si(n), e); + t[i] = kM(o, function (t) { + return t.option && t.exist ? PM(t.exist, t.option, !0) : t.exist || t.option + }) + } else t[i] = PM(n, e, !0) + } + }) + } + + function Ra(t) { + var e = t && t.itemStyle; + if (e) for (var i = 0, o = RM.length; i < o; i++) { + var a = RM[i], r = e.normal, s = e.emphasis; + r && r[a] && (t[a] = t[a] || {}, t[a].normal ? n(t[a].normal, r[a]) : t[a].normal = r[a], r[a] = null), s && s[a] && (t[a] = t[a] || {}, t[a].emphasis ? n(t[a].emphasis, s[a]) : t[a].emphasis = s[a], s[a] = null) + } + } + + function za(t, e, i) { + if (t && t[e] && (t[e].normal || t[e].emphasis)) { + var n = t[e].normal, o = t[e].emphasis; + n && (i ? (t[e].normal = t[e].emphasis = null, r(t[e], n)) : t[e] = n), o && (t.emphasis = t.emphasis || {}, t.emphasis[e] = o) + } + } + + function Ba(t) { + za(t, "itemStyle"), za(t, "lineStyle"), za(t, "areaStyle"), za(t, "label"), za(t, "labelLine"), za(t, "upperLabel"), za(t, "edgeLabel") + } + + function Va(t, e) { + var i = EM(t) && t[e], n = EM(i) && i.textStyle; + if (n) for (var o = 0, a = ib.length; o < a; o++) { + var e = ib[o]; + n.hasOwnProperty(e) && (i[e] = n[e]) + } + } + + function Ga(t) { + t && (Ba(t), Va(t, "label"), t.emphasis && Va(t.emphasis, "label")) + } + + function Wa(t) { + if (EM(t)) { + Ra(t), Ba(t), Va(t, "label"), Va(t, "upperLabel"), Va(t, "edgeLabel"), t.emphasis && (Va(t.emphasis, "label"), Va(t.emphasis, "upperLabel"), Va(t.emphasis, "edgeLabel")); + var e = t.markPoint; + e && (Ra(e), Ga(e)); + var i = t.markLine; + i && (Ra(i), Ga(i)); + var n = t.markArea; + n && Ga(n); + var o = t.data; + if ("graph" === t.type) { + o = o || t.nodes; + var a = t.links || t.edges; + if (a && !S(a)) for (s = 0; s < a.length; s++) Ga(a[s]); + d(t.categories, function (t) { + Ba(t) + }) + } + if (o && !S(o)) for (s = 0; s < o.length; s++) Ga(o[s]); + if ((e = t.markPoint) && e.data) for (var r = e.data, s = 0; s < r.length; s++) Ga(r[s]); + if ((i = t.markLine) && i.data) for (var l = i.data, s = 0; s < l.length; s++) y(l[s]) ? (Ga(l[s][0]), Ga(l[s][1])) : Ga(l[s]); + "gauge" === t.type ? (Va(t, "axisLabel"), Va(t, "title"), Va(t, "detail")) : "treemap" === t.type ? (za(t.breadcrumb, "itemStyle"), d(t.levels, function (t) { + Ba(t) + })) : "tree" === t.type && Ba(t.leaves) + } + } + + function Fa(t) { + return y(t) ? t : t ? [t] : [] + } + + function Ha(t) { + return (y(t) ? t[0] : t) || {} + } + + function Za(t, e) { + e = e.split(","); + for (var i = t, n = 0; n < e.length && null != (i = i && i[e[n]]); n++) ; + return i + } + + function Ua(t, e, i, n) { + e = e.split(","); + for (var o, a = t, r = 0; r < e.length - 1; r++) null == a[o = e[r]] && (a[o] = {}), a = a[o]; + (n || null == a[e[r]]) && (a[e[r]] = i) + } + + function Xa(t) { + d(BM, function (e) { + e[0] in t && !(e[1] in t) && (t[e[1]] = t[e[0]]) + }) + } + + function ja(t) { + d(t, function (e, i) { + var n = [], o = [NaN, NaN], a = [e.stackResultDimension, e.stackedOverDimension], r = e.data, + s = e.isStackedByIndex, l = r.map(a, function (a, l, u) { + var h = r.get(e.stackedDimension, u); + if (isNaN(h)) return o; + var c, d; + s ? d = r.getRawIndex(u) : c = r.get(e.stackedByDimension, u); + for (var f = NaN, p = i - 1; p >= 0; p--) { + var g = t[p]; + if (s || (d = g.data.rawIndexOf(g.stackedByDimension, c)), d >= 0) { + var m = g.data.getByRawIndex(g.stackResultDimension, d); + if (h >= 0 && m > 0 || h <= 0 && m < 0) { + h += m, f = m; + break + } + } + } + return n[0] = h, n[1] = f, n + }); + r.hostModel.setData(l), e.data = l + }) + } + + function Ya(t, e) { + la.isInstance(t) || (t = la.seriesDataToSource(t)), this._source = t; + var i = this._data = t.data, n = t.sourceFormat; + n === wM && (this._offset = 0, this._dimSize = e, this._data = i), a(this, FM[n === vM ? n + "_" + t.seriesLayoutBy : n]) + } + + function qa() { + return this._data.length + } + + function Ka(t) { + return this._data[t] + } + + function $a(t) { + for (var e = 0; e < t.length; e++) this._data.push(t[e]) + } + + function Ja(t, e, i, n) { + return null != i ? t[i] : t + } + + function Qa(t, e, i, n) { + return tr(t[n], this._dimensionInfos[e]) + } + + function tr(t, e) { + var i = e && e.type; + if ("ordinal" === i) { + var n = e && e.ordinalMeta; + return n ? n.parseAndCollect(t) : t + } + return "time" === i && "number" != typeof t && null != t && "-" !== t && (t = +Ro(t)), null == t || "" === t ? NaN : +t + } + + function er(t, e, i) { + if (t) { + var n = t.getRawDataItem(e); + if (null != n) { + var o, a, r = t.getProvider().getSource().sourceFormat, s = t.getDimensionInfo(i); + return s && (o = s.name, a = s.index), HM[r](n, e, a, o) + } + } + } + + function ir(t, e, i) { + if (t) { + var n = t.getProvider().getSource().sourceFormat; + if (n === mM || n === yM) { + var o = t.getRawDataItem(e); + return n !== mM || w(o) || (o = null), o ? o[i] : void 0 + } + } + } + + function nr(t) { + return new or(t) + } + + function or(t) { + t = t || {}, this._reset = t.reset, this._plan = t.plan, this._count = t.count, this._onDirty = t.onDirty, this._dirty = !0, this.context + } + + function ar(t, e, i, n, o, a) { + YM.reset(i, n, o, a), t._callingProgress = e, t._callingProgress({ + start: i, + end: n, + count: n - i, + next: YM.next + }, t.context) + } + + function rr(t, e) { + t._dueIndex = t._outputDueEnd = t._dueEnd = 0, t._settedOutputEnd = null; + var i, n; + !e && t._reset && ((i = t._reset(t.context)) && i.progress && (n = i.forceFirstProgress, i = i.progress), y(i) && !i.length && (i = null)), t._progress = i, t._modBy = t._modDataCount = null; + var o = t._downstream; + return o && o.dirty(), n + } + + function sr(t) { + var e = t.name; + Ci(t) || (t.name = lr(t) || e) + } + + function lr(t) { + var e = t.getRawData(), i = []; + return d(e.mapDimension("seriesName", !0), function (t) { + var n = e.getDimensionInfo(t); + n.displayName && i.push(n.displayName) + }), i.join(" ") + } + + function ur(t) { + return t.model.getRawData().count() + } + + function hr(t) { + var e = t.model; + return e.setData(e.getRawData().cloneShallow()), cr + } + + function cr(t, e) { + t.end > e.outputData.count() && e.model.getRawData().cloneShallow(e.outputData) + } + + function dr(t, e) { + d(t.CHANGABLE_METHODS, function (i) { + t.wrapMethod(i, v(fr, e)) + }) + } + + function fr(t) { + var e = pr(t); + e && e.setOutputEnd(this.count()) + } + + function pr(t) { + var e = (t.ecModel || {}).scheduler, i = e && e.getPipeline(t.uid); + if (i) { + var n = i.currentTask; + if (n) { + var o = n.agentStubMap; + o && (n = o.get(t.uid)) + } + return n + } + } + + function gr() { + this.group = new sw, this.uid = Mo("viewChart"), this.renderTask = nr({ + plan: yr, + reset: xr + }), this.renderTask.context = {view: this} + } + + function mr(t, e) { + if (t && (t.trigger(e), "group" === t.type)) for (var i = 0; i < t.childCount(); i++) mr(t.childAt(i), e) + } + + function vr(t, e, i) { + var n = Pi(t, e); + null != n ? d(Si(n), function (e) { + mr(t.getItemGraphicEl(e), i) + }) : t.eachItemGraphicEl(function (t) { + mr(t, i) + }) + } + + function yr(t) { + return eI(t.model) + } + + function xr(t) { + var e = t.model, i = t.ecModel, n = t.api, o = t.payload, a = e.pipelineContext.progressiveRender, r = t.view, + s = o && tI(o).updateMethod, l = a ? "incrementalPrepareRender" : s && r[s] ? s : "render"; + return "render" !== l && r[l](e, i, n, o), nI[l] + } + + function _r(t, e, i) { + function n() { + h = (new Date).getTime(), c = null, t.apply(r, s || []) + } + + var o, a, r, s, l, u = 0, h = 0, c = null; + e = e || 0; + var d = function () { + o = (new Date).getTime(), r = this, s = arguments; + var t = l || e, d = l || i; + l = null, a = o - (d ? u : h) - t, clearTimeout(c), d ? c = setTimeout(n, t) : a >= 0 ? n() : c = setTimeout(n, -a), u = o + }; + return d.clear = function () { + c && (clearTimeout(c), c = null) + }, d.debounceNextCall = function (t) { + l = t + }, d + } + + function wr(t, e, i, n) { + var o = t[e]; + if (o) { + var a = o[oI] || o, r = o[rI]; + if (o[aI] !== i || r !== n) { + if (null == i || !n) return t[e] = a; + (o = t[e] = _r(a, i, "debounce" === n))[oI] = a, o[rI] = n, o[aI] = i + } + return o + } + } + + function br(t, e) { + var i = t[e]; + i && i[oI] && (t[e] = i[oI]) + } + + function Sr(t, e, i, n) { + this.ecInstance = t, this.api = e, this.unfinished; + var i = this._dataProcessorHandlers = i.slice(), n = this._visualHandlers = n.slice(); + this._allHandlers = i.concat(n), this._stageTaskMap = R() + } + + function Mr(t, e, i, n, o) { + function a(t, e) { + return t.setDirty && (!t.dirtyMap || t.dirtyMap.get(e.__pipeline.id)) + } + + o = o || {}; + var r; + d(e, function (e, s) { + if (!o.visualType || o.visualType === e.visualType) { + var l = t._stageTaskMap.get(e.uid), u = l.seriesTaskMap, h = l.overallTask; + if (h) { + var c, d = h.agentStubMap; + d.each(function (t) { + a(o, t) && (t.dirty(), c = !0) + }), c && h.dirty(), dI(h, n); + var f = t.getPerformArgs(h, o.block); + d.each(function (t) { + t.perform(f) + }), r |= h.perform(f) + } else u && u.each(function (s, l) { + a(o, s) && s.dirty(); + var u = t.getPerformArgs(s, o.block); + u.skip = !e.performRawSeries && i.isSeriesFiltered(s.context.model), dI(s, n), r |= s.perform(u) + }) + } + }), t.unfinished |= r + } + + function Ir(t, e, i, n, o) { + function a(i) { + var a = i.uid, s = r.get(a) || r.set(a, nr({plan: kr, reset: Pr, count: Or})); + s.context = { + model: i, + ecModel: n, + api: o, + useClearVisual: e.isVisual && !e.isLayout, + plan: e.plan, + reset: e.reset, + scheduler: t + }, Er(t, i, s) + } + + var r = i.seriesTaskMap || (i.seriesTaskMap = R()), s = e.seriesType, l = e.getTargetSeries; + e.createOnAllSeries ? n.eachRawSeries(a) : s ? n.eachRawSeriesByType(s, a) : l && l(n, o).each(a); + var u = t._pipelineMap; + r.each(function (t, e) { + u.get(e) || (t.dispose(), r.removeKey(e)) + }) + } + + function Tr(t, e, i, n, o) { + function a(e) { + var i = e.uid, n = s.get(i); + n || (n = s.set(i, nr({reset: Ar, onDirty: Lr})), r.dirty()), n.context = { + model: e, + overallProgress: h, + modifyOutputEnd: c + }, n.agent = r, n.__block = h, Er(t, e, n) + } + + var r = i.overallTask = i.overallTask || nr({reset: Dr}); + r.context = {ecModel: n, api: o, overallReset: e.overallReset, scheduler: t}; + var s = r.agentStubMap = r.agentStubMap || R(), l = e.seriesType, u = e.getTargetSeries, h = !0, + c = e.modifyOutputEnd; + l ? n.eachRawSeriesByType(l, a) : u ? u(n, o).each(a) : (h = !1, d(n.getSeries(), a)); + var f = t._pipelineMap; + s.each(function (t, e) { + f.get(e) || (t.dispose(), r.dirty(), s.removeKey(e)) + }) + } + + function Dr(t) { + t.overallReset(t.ecModel, t.api, t.payload) + } + + function Ar(t, e) { + return t.overallProgress && Cr + } + + function Cr() { + this.agent.dirty(), this.getDownstream().dirty() + } + + function Lr() { + this.agent && this.agent.dirty() + } + + function kr(t) { + return t.plan && t.plan(t.model, t.ecModel, t.api, t.payload) + } + + function Pr(t) { + t.useClearVisual && t.data.clearAllVisual(); + var e = t.resetDefines = Si(t.reset(t.model, t.ecModel, t.api, t.payload)); + return e.length > 1 ? f(e, function (t, e) { + return Nr(e) + }) : fI + } + + function Nr(t) { + return function (e, i) { + var n = i.data, o = i.resetDefines[t]; + if (o && o.dataEach) for (var a = e.start; a < e.end; a++) o.dataEach(n, a); else o && o.progress && o.progress(e, n) + } + } + + function Or(t) { + return t.data.count() + } + + function Er(t, e, i) { + var n = e.uid, o = t._pipelineMap.get(n); + !o.head && (o.head = i), o.tail && o.tail.pipe(i), o.tail = i, i.__idxInPipeline = o.count++, i.__pipeline = o + } + + function Rr(t) { + pI = null; + try { + t(gI, mI) + } catch (t) { + } + return pI + } + + function zr(t, e) { + for (var i in e.prototype) t[i] = B + } + + function Br(t) { + return function (e, i, n) { + e = e && e.toLowerCase(), D_.prototype[t].call(this, e, i, n) + } + } + + function Vr() { + D_.call(this) + } + + function Gr(t, e, n) { + function o(t, e) { + return t.__prio - e.__prio + } + + n = n || {}, "string" == typeof e && (e = HI[e]), this.id, this.group, this._dom = t; + var a = this._zr = _i(t, { + renderer: n.renderer || "canvas", + devicePixelRatio: n.devicePixelRatio, + width: n.width, + height: n.height + }); + this._throttledZrFlush = _r(m(a.flush, a), 17), (e = i(e)) && GM(e, !0), this._theme = e, this._chartsViews = [], this._chartsMap = {}, this._componentsViews = [], this._componentsMap = {}, this._coordSysMgr = new Ca; + var r = this._api = os(this); + ae(FI, o), ae(VI, o), this._scheduler = new Sr(this, r, VI, FI), D_.call(this), this._messageCenter = new Vr, this._initEvents(), this.resize = m(this.resize, this), this._pendingActions = [], a.animation.on("frame", this._onframe, this), Yr(a, this), N(this) + } + + function Wr(t, e, i) { + var n, o = this._model, a = this._coordSysMgr.getCoordinateSystems(); + e = Oi(o, e); + for (var r = 0; r < a.length; r++) { + var s = a[r]; + if (s[t] && null != (n = s[t](o, e, i))) return n + } + } + + function Fr(t) { + var e = t._model, i = t._scheduler; + i.restorePipelines(e), i.prepareStageTasks(), qr(t, "component", e, i), qr(t, "chart", e, i), i.plan() + } + + function Hr(t, e, i, n, o) { + function a(n) { + n && n.__alive && n[e] && n[e](n.__model, r, t._api, i) + } + + var r = t._model; + if (n) { + var s = {}; + s[n + "Id"] = i[n + "Id"], s[n + "Index"] = i[n + "Index"], s[n + "Name"] = i[n + "Name"]; + var l = {mainType: n, query: s}; + o && (l.subType = o); + var u = i.excludeSeriesId; + null != u && (u = R(Si(u))), r && r.eachComponent(l, function (e) { + u && null != u.get(e.id) || a(t["series" === n ? "_chartsMap" : "_componentsMap"][e.__viewId]) + }, t) + } else bI(t._componentsViews.concat(t._chartsViews), a) + } + + function Zr(t, e) { + var i = t._chartsMap, n = t._scheduler; + e.eachSeries(function (t) { + n.updateStreamModes(t, i[t.__viewId]) + }) + } + + function Ur(t, e) { + var i = t.type, n = t.escapeConnect, o = zI[i], s = o.actionInfo, l = (s.update || "update").split(":"), + u = l.pop(); + l = null != l[0] && II(l[0]), this[kI] = !0; + var h = [t], c = !1; + t.batch && (c = !0, h = f(t.batch, function (e) { + return e = r(a({}, e), t), e.batch = null, e + })); + var d, p = [], g = "highlight" === i || "downplay" === i; + bI(h, function (t) { + d = o.action(t, this._model, this._api), (d = d || a({}, t)).type = s.event || d.type, p.push(d), g ? Hr(this, u, t, "series") : l && Hr(this, u, t, l.main, l.sub) + }, this), "none" === u || g || l || (this[PI] ? (Fr(this), EI.update.call(this, t), this[PI] = !1) : EI[u].call(this, t)), d = c ? { + type: s.event || i, + escapeConnect: n, + batch: p + } : p[0], this[kI] = !1, !e && this._messageCenter.trigger(d.type, d) + } + + function Xr(t) { + for (var e = this._pendingActions; e.length;) { + var i = e.shift(); + Ur.call(this, i, t) + } + } + + function jr(t) { + !t && this.trigger("updated") + } + + function Yr(t, e) { + t.on("rendered", function () { + e.trigger("rendered"), !t.animation.isFinished() || e[PI] || e._scheduler.unfinished || e._pendingActions.length || e.trigger("finished") + }) + } + + function qr(t, e, i, n) { + function o(t) { + var e = "_ec_" + t.id + "_" + t.type, o = s[e]; + if (!o) { + var h = II(t.type); + (o = new (a ? $M.getClass(h.main, h.sub) : gr.getClass(h.sub))).init(i, u), s[e] = o, r.push(o), l.add(o.group) + } + t.__viewId = o.__id = e, o.__alive = !0, o.__model = t, o.group.__ecComponentInfo = { + mainType: t.mainType, + index: t.componentIndex + }, !a && n.prepareView(o, t, i, u) + } + + for (var a = "component" === e, r = a ? t._componentsViews : t._chartsViews, s = a ? t._componentsMap : t._chartsMap, l = t._zr, u = t._api, h = 0; h < r.length; h++) r[h].__alive = !1; + a ? i.eachComponent(function (t, e) { + "series" !== t && o(e) + }) : i.eachSeries(o); + for (h = 0; h < r.length;) { + var c = r[h]; + c.__alive ? h++ : (!a && c.renderTask.dispose(), l.remove(c.group), c.dispose(i, u), r.splice(h, 1), delete s[c.__id], c.__id = c.group.__ecComponentInfo = null) + } + } + + function Kr(t) { + t.clearColorPalette(), t.eachSeries(function (t) { + t.clearColorPalette() + }) + } + + function $r(t, e, i, n) { + Jr(t, e, i, n), bI(t._chartsViews, function (t) { + t.__alive = !1 + }), Qr(t, e, i, n), bI(t._chartsViews, function (t) { + t.__alive || t.remove(e, i) + }) + } + + function Jr(t, e, i, n, o) { + bI(o || t._componentsViews, function (t) { + var o = t.__model; + t.render(o, e, i, n), ns(o, t) + }) + } + + function Qr(t, e, i, n, o) { + var a, r = t._scheduler; + e.eachSeries(function (e) { + var i = t._chartsMap[e.__viewId]; + i.__alive = !0; + var s = i.renderTask; + r.updatePayload(s, n), o && o.get(e.uid) && s.dirty(), a |= s.perform(r.getPerformArgs(s)), i.group.silent = !!e.get("silent"), ns(e, i), is(e, i) + }), r.unfinished |= a, es(t._zr, e), uI(t._zr.dom, e) + } + + function ts(t, e) { + bI(WI, function (i) { + i(t, e) + }) + } + + function es(t, e) { + var i = t.storage, n = 0; + i.traverse(function (t) { + t.isGroup || n++ + }), n > e.get("hoverLayerThreshold") && !a_.node && i.traverse(function (t) { + t.isGroup || (t.useHoverLayer = !0) + }) + } + + function is(t, e) { + var i = t.get("blendMode") || null; + e.group.traverse(function (t) { + t.isGroup || t.style.blend !== i && t.setStyle("blend", i), t.eachPendingDisplayable && t.eachPendingDisplayable(function (t) { + t.setStyle("blend", i) + }) + }) + } + + function ns(t, e) { + var i = t.get("z"), n = t.get("zlevel"); + e.group.traverse(function (t) { + "group" !== t.type && (null != i && (t.z = i), null != n && (t.zlevel = n)) + }) + } + + function os(t) { + var e = t._coordSysMgr; + return a(new Aa(t), { + getCoordinateSystems: m(e.getCoordinateSystems, e), getComponentByElement: function (e) { + for (; e;) { + var i = e.__ecComponentInfo; + if (null != i) return t._model.getComponent(i.mainType, i.index); + e = e.parent + } + } + }) + } + + function as(t) { + function e(t, e) { + for (var n = 0; n < t.length; n++) t[n][i] = e + } + + var i = "__connectUpdateStatus"; + bI(BI, function (n, o) { + t._messageCenter.on(o, function (n) { + if (XI[t.group] && 0 !== t[i]) { + if (n && n.escapeConnect) return; + var o = t.makeActionFromEvent(n), a = []; + bI(UI, function (e) { + e !== t && e.group === t.group && a.push(e) + }), e(a, 0), bI(a, function (t) { + 1 !== t[i] && t.dispatchAction(o) + }), e(a, 2) + } + }) + }) + } + + function rs(t) { + XI[t] = !1 + } + + function ss(t) { + return UI[zi(t, qI)] + } + + function ls(t, e) { + HI[t] = e + } + + function us(t) { + GI.push(t) + } + + function hs(t, e) { + gs(VI, t, e, DI) + } + + function cs(t, e, i) { + "function" == typeof e && (i = e, e = ""); + var n = MI(t) ? t.type : [t, t = {event: e}][0]; + t.event = (t.event || n).toLowerCase(), e = t.event, wI(NI.test(n) && NI.test(e)), zI[n] || (zI[n] = { + action: i, + actionInfo: t + }), BI[e] = n + } + + function ds(t, e) { + Ca.register(t, e) + } + + function fs(t, e) { + gs(FI, t, e, AI, "layout") + } + + function ps(t, e) { + gs(FI, t, e, CI, "visual") + } + + function gs(t, e, i, n, o) { + (SI(e) || MI(e)) && (i = e, e = n); + var a = Sr.wrapStageHandler(i, o); + return a.__prio = e, a.__raw = i, t.push(a), a + } + + function ms(t, e) { + ZI[t] = e + } + + function vs(t) { + return hM.extend(t) + } + + function ys(t) { + return $M.extend(t) + } + + function xs(t) { + return KM.extend(t) + } + + function _s(t) { + return gr.extend(t) + } + + function ws(t) { + return KI[t] + } + + function bs(t) { + return t + } + + function Ss(t, e, i, n, o) { + this._old = t, this._new = e, this._oldKeyGetter = i || bs, this._newKeyGetter = n || bs, this.context = o + } + + function Ms(t, e, i, n, o) { + for (var a = 0; a < t.length; a++) { + var r = "_ec_" + o[n](t[a], a), s = e[r]; + null == s ? (i.push(r), e[r] = a) : (s.length || (e[r] = s = [s]), s.push(a)) + } + } + + function Is(t) { + var e = {}, i = e.encode = {}, n = R(), o = [], a = []; + d(t.dimensions, function (e) { + var r = t.getDimensionInfo(e), s = r.coordDim; + if (s) { + var l = i[s]; + i.hasOwnProperty(s) || (l = i[s] = []), l[r.coordDimIndex] = e, r.isExtraCoord || (n.set(s, 1), Ds(r.type) && (o[0] = e)), r.defaultTooltip && a.push(e) + } + QI.each(function (t, e) { + var n = i[e]; + i.hasOwnProperty(e) || (n = i[e] = []); + var o = r.otherDims[e]; + null != o && !1 !== o && (n[o] = r.name) + }) + }); + var r = [], s = {}; + n.each(function (t, e) { + var n = i[e]; + s[e] = n[0], r = r.concat(n) + }), e.dataDimsOnCoord = r, e.encodeFirstDimNotExtra = s; + var l = i.label; + l && l.length && (o = l.slice()); + var u = i.tooltip; + return u && u.length ? a = u.slice() : a.length || (a = o.slice()), i.defaultedLabel = o, i.defaultedTooltip = a, e + } + + function Ts(t) { + return "category" === t ? "ordinal" : "time" === t ? "time" : "float" + } + + function Ds(t) { + return !("ordinal" === t || "time" === t) + } + + function As(t) { + return t._rawCount > 65535 ? nT : oT + } + + function Cs(t) { + var e = t.constructor; + return e === Array ? t.slice() : new e(t) + } + + function Ls(t, e) { + d(aT.concat(e.__wrappedMethods || []), function (i) { + e.hasOwnProperty(i) && (t[i] = e[i]) + }), t.__wrappedMethods = e.__wrappedMethods, d(rT, function (n) { + t[n] = i(e[n]) + }), t._calculationInfo = a(e._calculationInfo) + } + + function ks(t) { + var e = t._invertedIndicesMap; + d(e, function (i, n) { + var o = t._dimensionInfos[n].ordinalMeta; + if (o) { + i = e[n] = new nT(o.categories.length); + for (a = 0; a < i.length; a++) i[a] = NaN; + for (var a = 0; a < t._count; a++) i[t.get(n, a)] = a + } + }) + } + + function Ps(t, e, i) { + var n; + if (null != e) { + var o = t._chunkSize, a = Math.floor(i / o), r = i % o, s = t.dimensions[e], l = t._storage[s][a]; + if (l) { + n = l[r]; + var u = t._dimensionInfos[s].ordinalMeta; + u && u.categories.length && (n = u.categories[n]) + } + } + return n + } + + function Ns(t) { + return t + } + + function Os(t) { + return t < this._count && t >= 0 ? this._indices[t] : -1 + } + + function Es(t, e) { + var i = t._idList[e]; + return null == i && (i = Ps(t, t._idDimIdx, e)), null == i && (i = eT + e), i + } + + function Rs(t) { + return y(t) || (t = [t]), t + } + + function zs(t, e) { + var i = t.dimensions, n = new sT(f(i, t.getDimensionInfo, t), t.hostModel); + Ls(n, t); + for (var o = n._storage = {}, a = t._storage, r = 0; r < i.length; r++) { + var s = i[r]; + a[s] && (l(e, s) >= 0 ? (o[s] = Bs(a[s]), n._rawExtent[s] = Vs(), n._extent[s] = null) : o[s] = a[s]) + } + return n + } + + function Bs(t) { + for (var e = new Array(t.length), i = 0; i < t.length; i++) e[i] = Cs(t[i]); + return e + } + + function Vs() { + return [1 / 0, -1 / 0] + } + + function Gs(t, e, n) { + function o(t, e, i) { + null != QI.get(e) ? t.otherDims[e] = i : (t.coordDim = e, t.coordDimIndex = i, h.set(e, !0)) + } + + la.isInstance(e) || (e = la.seriesDataToSource(e)), n = n || {}, t = (t || []).slice(); + for (var s = (n.dimsDef || []).slice(), l = R(n.encodeDef), u = R(), h = R(), c = [], f = Ws(e, t, s, n.dimCount), p = 0; p < f; p++) { + var g = s[p] = a({}, w(s[p]) ? s[p] : {name: s[p]}), m = g.name, v = c[p] = {otherDims: {}}; + null != m && null == u.get(m) && (v.name = v.displayName = m, u.set(m, p)), null != g.type && (v.type = g.type), null != g.displayName && (v.displayName = g.displayName) + } + l.each(function (t, e) { + t = Si(t).slice(); + var i = l.set(e, []); + d(t, function (t, n) { + _(t) && (t = u.get(t)), null != t && t < f && (i[n] = t, o(c[t], e, n)) + }) + }); + var y = 0; + d(t, function (t, e) { + var n, t, a, s; + if (_(t)) n = t, t = {}; else { + n = t.name; + var u = t.ordinalMeta; + t.ordinalMeta = null, (t = i(t)).ordinalMeta = u, a = t.dimsDef, s = t.otherDims, t.name = t.coordDim = t.coordDimIndex = t.dimsDef = t.otherDims = null + } + var h = Si(l.get(n)); + if (!h.length) for (var f = 0; f < (a && a.length || 1); f++) { + for (; y < c.length && null != c[y].coordDim;) y++; + y < c.length && h.push(y++) + } + d(h, function (e, i) { + var l = c[e]; + if (o(r(l, t), n, i), null == l.name && a) { + var u = a[i]; + !w(u) && (u = {name: u}), l.name = l.displayName = u.name, l.defaultTooltip = u.defaultTooltip + } + s && r(l.otherDims, s) + }) + }); + var x = n.generateCoord, b = n.generateCoordCount, S = null != b; + b = x ? b || 1 : 0; + for (var M = x || "value", I = 0; I < f; I++) null == (v = c[I] = c[I] || {}).coordDim && (v.coordDim = Fs(M, h, S), v.coordDimIndex = 0, (!x || b <= 0) && (v.isExtraCoord = !0), b--), null == v.name && (v.name = Fs(v.coordDim, u)), null == v.type && xa(e, I, v.name) && (v.type = "ordinal"); + return c + } + + function Ws(t, e, i, n) { + var o = Math.max(t.dimensionsDetectCount || 1, e.length, i.length, n || 0); + return d(e, function (t) { + var e = t.dimsDef; + e && (o = Math.max(o, e.length)) + }), o + } + + function Fs(t, e, i) { + if (i || null != e.get(t)) { + for (var n = 0; null != e.get(t + n);) n++; + t += n + } + return e.set(t, !0), t + } + + function Hs(t, e, i) { + var n, o, a, r, s = (i = i || {}).byIndex, l = i.stackedCoordDimension, u = !(!t || !t.get("stack")); + if (d(e, function (t, i) { + _(t) && (e[i] = t = {name: t}), u && !t.isExtraCoord && (s || n || !t.ordinalMeta || (n = t), o || "ordinal" === t.type || "time" === t.type || l && l !== t.coordDim || (o = t)) + }), !o || s || n || (s = !0), o) { + a = "__\0ecstackresult", r = "__\0ecstackedover", n && (n.createInvertedIndices = !0); + var h = o.coordDim, c = o.type, f = 0; + d(e, function (t) { + t.coordDim === h && f++ + }), e.push({ + name: a, + coordDim: h, + coordDimIndex: f, + type: c, + isExtraCoord: !0, + isCalculationCoord: !0 + }), f++, e.push({name: r, coordDim: r, coordDimIndex: f, type: c, isExtraCoord: !0, isCalculationCoord: !0}) + } + return { + stackedDimension: o && o.name, + stackedByDimension: n && n.name, + isStackedByIndex: s, + stackedOverDimension: r, + stackResultDimension: a + } + } + + function Zs(t, e) { + return !!e && e === t.getCalculationInfo("stackedDimension") + } + + function Us(t, e) { + return Zs(t, e) ? t.getCalculationInfo("stackResultDimension") : e + } + + function Xs(t, e, i) { + i = i || {}, la.isInstance(t) || (t = la.seriesDataToSource(t)); + var n, o = e.get("coordinateSystem"), a = Ca.get(o), r = ra(e); + r && (n = f(r.coordSysDims, function (t) { + var e = {name: t}, i = r.axisMap.get(t); + if (i) { + var n = i.get("type"); + e.type = Ts(n) + } + return e + })), n || (n = a && (a.getDimensionsInfo ? a.getDimensionsInfo() : a.dimensions.slice()) || ["x", "y"]); + var s, l, u = hT(t, {coordDimensions: n, generateCoord: i.generateCoord}); + r && d(u, function (t, e) { + var i = t.coordDim, n = r.categoryAxisMap.get(i); + n && (null == s && (s = e), t.ordinalMeta = n.getOrdinalMeta()), null != t.otherDims.itemName && (l = !0) + }), l || null == s || (u[s].otherDims.itemName = 0); + var h = Hs(e, u), c = new sT(u, e); + c.setCalculationInfo(h); + var p = null != s && js(t) ? function (t, e, i, n) { + return n === s ? i : this.defaultDimValueGetter(t, e, i, n) + } : null; + return c.hasItemOption = !1, c.initData(t, null, p), c + } + + function js(t) { + if (t.sourceFormat === mM) { + var e = Ys(t.data || []); + return null != e && !y(Ii(e)) + } + } + + function Ys(t) { + for (var e = 0; e < t.length && null == t[e];) e++; + return t[e] + } + + function qs(t) { + this._setting = t || {}, this._extent = [1 / 0, -1 / 0], this._interval = 0, this.init && this.init.apply(this, arguments) + } + + function Ks(t) { + this.categories = t.categories || [], this._needCollect = t.needCollect, this._deduplication = t.deduplication, this._map + } + + function $s(t) { + return t._map || (t._map = R(t.categories)) + } + + function Js(t) { + return w(t) && null != t.value ? t.value : t + "" + } + + function Qs(t, e, i, n) { + var o = {}, a = t[1] - t[0], r = o.interval = Vo(a / e, !0); + null != i && r < i && (r = o.interval = i), null != n && r > n && (r = o.interval = n); + var s = o.intervalPrecision = tl(r); + return il(o.niceTickExtent = [pT(Math.ceil(t[0] / r) * r, s), pT(Math.floor(t[1] / r) * r, s)], t), o + } + + function tl(t) { + return ko(t) + 2 + } + + function el(t, e, i) { + t[e] = Math.max(Math.min(t[e], i[1]), i[0]) + } + + function il(t, e) { + !isFinite(t[0]) && (t[0] = e[0]), !isFinite(t[1]) && (t[1] = e[1]), el(t, 0, e), el(t, 1, e), t[0] > t[1] && (t[0] = t[1]) + } + + function nl(t, e, i, n) { + var o = []; + if (!t) return o; + e[0] < i[0] && o.push(e[0]); + for (var a = i[0]; a <= i[1] && (o.push(a), (a = pT(a + t, n)) !== o[o.length - 1]);) if (o.length > 1e4) return []; + return e[1] > (o.length ? o[o.length - 1] : i[1]) && o.push(e[1]), o + } + + function ol(t) { + return t.get("stack") || vT + t.seriesIndex + } + + function al(t) { + return t.dim + t.index + } + + function rl(t) { + var e = [], i = t.axis; + if ("category" === i.type) { + for (var n = i.getBandWidth(), o = 0; o < t.count; o++) e.push(r({ + bandWidth: n, + axisKey: "axis0", + stackId: vT + o + }, t)); + for (var a = ul(e), s = [], o = 0; o < t.count; o++) { + var l = a.axis0[vT + o]; + l.offsetCenter = l.offset + l.width / 2, s.push(l) + } + return s + } + } + + function sl(t, e) { + var i = []; + return e.eachSeriesByType(t, function (t) { + dl(t) && !fl(t) && i.push(t) + }), i + } + + function ll(t) { + var e = []; + return d(t, function (t) { + var i = t.getData(), n = t.coordinateSystem.getBaseAxis(), o = n.getExtent(), + a = "category" === n.type ? n.getBandWidth() : Math.abs(o[1] - o[0]) / i.count(), + r = Do(t.get("barWidth"), a), s = Do(t.get("barMaxWidth"), a), l = t.get("barGap"), + u = t.get("barCategoryGap"); + e.push({ + bandWidth: a, + barWidth: r, + barMaxWidth: s, + barGap: l, + barCategoryGap: u, + axisKey: al(n), + stackId: ol(t) + }) + }), ul(e) + } + + function ul(t) { + var e = {}; + d(t, function (t, i) { + var n = t.axisKey, o = t.bandWidth, a = e[n] || { + bandWidth: o, + remainedWidth: o, + autoWidthCount: 0, + categoryGap: "20%", + gap: "30%", + stacks: {} + }, r = a.stacks; + e[n] = a; + var s = t.stackId; + r[s] || a.autoWidthCount++, r[s] = r[s] || {width: 0, maxWidth: 0}; + var l = t.barWidth; + l && !r[s].width && (r[s].width = l, l = Math.min(a.remainedWidth, l), a.remainedWidth -= l); + var u = t.barMaxWidth; + u && (r[s].maxWidth = u); + var h = t.barGap; + null != h && (a.gap = h); + var c = t.barCategoryGap; + null != c && (a.categoryGap = c) + }); + var i = {}; + return d(e, function (t, e) { + i[e] = {}; + var n = t.stacks, o = t.bandWidth, a = Do(t.categoryGap, o), r = Do(t.gap, 1), s = t.remainedWidth, + l = t.autoWidthCount, u = (s - a) / (l + (l - 1) * r); + u = Math.max(u, 0), d(n, function (t, e) { + var i = t.maxWidth; + i && i < u && (i = Math.min(i, s), t.width && (i = Math.min(i, t.width)), s -= i, t.width = i, l--) + }), u = (s - a) / (l + (l - 1) * r), u = Math.max(u, 0); + var h, c = 0; + d(n, function (t, e) { + t.width || (t.width = u), h = t, c += t.width * (1 + r) + }), h && (c -= h.width * r); + var f = -c / 2; + d(n, function (t, n) { + i[e][n] = i[e][n] || {offset: f, width: t.width}, f += t.width * (1 + r) + }) + }), i + } + + function hl(t, e, i) { + if (t && e) { + var n = t[al(e)]; + return null != n && null != i && (n = n[ol(i)]), n + } + } + + function cl(t, e) { + var i = sl(t, e), n = ll(i), o = {}; + d(i, function (t) { + var e = t.getData(), i = t.coordinateSystem, a = i.getBaseAxis(), r = ol(t), s = n[al(a)][r], l = s.offset, + u = s.width, h = i.getOtherAxis(a), c = t.get("barMinHeight") || 0; + o[r] = o[r] || [], e.setLayout({offset: l, size: u}); + for (var d = e.mapDimension(h.dim), f = e.mapDimension(a.dim), p = Zs(e, d), g = h.isHorizontal(), m = pl(a, h, p), v = 0, y = e.count(); v < y; v++) { + var x = e.get(d, v), _ = e.get(f, v); + if (!isNaN(x)) { + var w = x >= 0 ? "p" : "n", b = m; + p && (o[r][_] || (o[r][_] = {p: m, n: m}), b = o[r][_][w]); + var S, M, I, T; + if (g) S = b, M = (D = i.dataToPoint([x, _]))[1] + l, I = D[0] - m, T = u, Math.abs(I) < c && (I = (I < 0 ? -1 : 1) * c), p && (o[r][_][w] += I); else { + var D = i.dataToPoint([_, x]); + S = D[0] + l, M = b, I = u, T = D[1] - m, Math.abs(T) < c && (T = (T <= 0 ? -1 : 1) * c), p && (o[r][_][w] += T) + } + e.setItemLayout(v, {x: S, y: M, width: I, height: T}) + } + } + }, this) + } + + function dl(t) { + return t.coordinateSystem && "cartesian2d" === t.coordinateSystem.type + } + + function fl(t) { + return t.pipelineContext && t.pipelineContext.large + } + + function pl(t, e, i) { + return l(t.getAxesOnZeroOf(), e) >= 0 || i ? e.toGlobalCoord(e.dataToCoord(0)) : e.getGlobalExtent()[0] + } + + function gl(t, e) { + return CT(t, AT(e)) + } + + function ml(t, e) { + var i, n, o, a = t.type, r = e.getMin(), s = e.getMax(), l = null != r, u = null != s, h = t.getExtent(); + "ordinal" === a ? i = e.getCategories().length : (y(n = e.get("boundaryGap")) || (n = [n || 0, n || 0]), "boolean" == typeof n[0] && (n = [0, 0]), n[0] = Do(n[0], 1), n[1] = Do(n[1], 1), o = h[1] - h[0] || Math.abs(h[0])), null == r && (r = "ordinal" === a ? i ? 0 : NaN : h[0] - n[0] * o), null == s && (s = "ordinal" === a ? i ? i - 1 : NaN : h[1] + n[1] * o), "dataMin" === r ? r = h[0] : "function" == typeof r && (r = r({ + min: h[0], + max: h[1] + })), "dataMax" === s ? s = h[1] : "function" == typeof s && (s = s({ + min: h[0], + max: h[1] + })), (null == r || !isFinite(r)) && (r = NaN), (null == s || !isFinite(s)) && (s = NaN), t.setBlank(I(r) || I(s) || "ordinal" === a && !t.getOrdinalMeta().categories.length), e.getNeedCrossZero() && (r > 0 && s > 0 && !l && (r = 0), r < 0 && s < 0 && !u && (s = 0)); + var c = e.ecModel; + if (c && "time" === a) { + var f, p = sl("bar", c); + if (d(p, function (t) { + f |= t.getBaseAxis() === e.axis + }), f) { + var g = ll(p), m = vl(r, s, e, g); + r = m.min, s = m.max + } + } + return [r, s] + } + + function vl(t, e, i, n) { + var o = i.axis.getExtent(), a = o[1] - o[0], r = hl(n, i.axis); + if (void 0 === r) return {min: t, max: e}; + var s = 1 / 0; + d(r, function (t) { + s = Math.min(t.offset, s) + }); + var l = -1 / 0; + d(r, function (t) { + l = Math.max(t.offset + t.width, l) + }), s = Math.abs(s), l = Math.abs(l); + var u = s + l, h = e - t, c = h / (1 - (s + l) / a) - h; + return e += c * (l / u), t -= c * (s / u), {min: t, max: e} + } + + function yl(t, e) { + var i = ml(t, e), n = null != e.getMin(), o = null != e.getMax(), a = e.get("splitNumber"); + "log" === t.type && (t.base = e.get("logBase")); + var r = t.type; + t.setExtent(i[0], i[1]), t.niceExtent({ + splitNumber: a, + fixMin: n, + fixMax: o, + minInterval: "interval" === r || "time" === r ? e.get("minInterval") : null, + maxInterval: "interval" === r || "time" === r ? e.get("maxInterval") : null + }); + var s = e.get("interval"); + null != s && t.setInterval && t.setInterval(s) + } + + function xl(t, e) { + if (e = e || t.get("type")) switch (e) { + case"category": + return new fT(t.getOrdinalMeta ? t.getOrdinalMeta() : t.getCategories(), [1 / 0, -1 / 0]); + case"value": + return new mT; + default: + return (qs.getClass(e) || mT).create(t) + } + } + + function _l(t) { + var e = t.scale.getExtent(), i = e[0], n = e[1]; + return !(i > 0 && n > 0 || i < 0 && n < 0) + } + + function wl(t) { + var e = t.getLabelModel().get("formatter"), i = "category" === t.type ? t.scale.getExtent()[0] : null; + return "string" == typeof e ? e = function (t) { + return function (e) { + return t.replace("{value}", null != e ? e : "") + } + }(e) : "function" == typeof e ? function (n, o) { + return null != i && (o = n - i), e(bl(t, n), o) + } : function (e) { + return t.scale.getLabel(e) + } + } + + function bl(t, e) { + return "category" === t.type ? t.scale.getLabel(e) : e + } + + function Sl(t) { + var e = t.model, i = t.scale; + if (e.get("axisLabel.show") && !i.isBlank()) { + var n, o, a = "category" === t.type, r = i.getExtent(); + o = a ? i.count() : (n = i.getTicks()).length; + var s, l = t.getLabelModel(), u = wl(t), h = 1; + o > 40 && (h = Math.ceil(o / 40)); + for (var c = 0; c < o; c += h) { + var d = u(n ? n[c] : r[0] + c), f = Ml(l.getTextRect(d), l.get("rotate") || 0); + s ? s.union(f) : s = f + } + return s + } + } + + function Ml(t, e) { + var i = e * Math.PI / 180, n = t.plain(), o = n.width, a = n.height, r = o * Math.cos(i) + a * Math.sin(i), + s = o * Math.sin(i) + a * Math.cos(i); + return new $t(n.x, n.y, r, s) + } + + function Il(t, e) { + if ("image" !== this.type) { + var i = this.style, n = this.shape; + n && "line" === n.symbolType ? i.stroke = t : this.__isEmptyBrush ? (i.stroke = t, i.fill = e || "#fff") : (i.fill && (i.fill = t), i.stroke && (i.stroke = t)), this.dirty(!1) + } + } + + function Tl(t, e, i, n, o, a, r) { + var s = 0 === t.indexOf("empty"); + s && (t = t.substr(5, 1).toLowerCase() + t.substr(6)); + var l; + return l = 0 === t.indexOf("image://") ? zn(t.slice(8), new $t(e, i, n, o), r ? "center" : "cover") : 0 === t.indexOf("path://") ? Rn(t.slice(7), {}, new $t(e, i, n, o), r ? "center" : "cover") : new FT({ + shape: { + symbolType: t, + x: e, + y: i, + width: n, + height: o + } + }), l.__isEmptyBrush = s, l.setColor = Il, l.setColor(a), l + } + + function Dl(t, e) { + return Math.abs(t - e) < UT + } + + function Al(t, e, i) { + var n = 0, o = t[0]; + if (!o) return !1; + for (var a = 1; a < t.length; a++) { + var r = t[a]; + n += mn(o[0], o[1], r[0], r[1], e, i), o = r + } + var s = t[0]; + return Dl(o[0], s[0]) && Dl(o[1], s[1]) || (n += mn(o[0], o[1], s[0], s[1], e, i)), 0 !== n + } + + function Cl(t, e, i) { + if (this.name = t, this.geometries = e, i) i = [i[0], i[1]]; else { + var n = this.getBoundingRect(); + i = [n.x + n.width / 2, n.y + n.height / 2] + } + this.center = i + } + + function Ll(t) { + if (!t.UTF8Encoding) return t; + var e = t.UTF8Scale; + null == e && (e = 1024); + for (var i = t.features, n = 0; n < i.length; n++) for (var o = i[n].geometry, a = o.coordinates, r = o.encodeOffsets, s = 0; s < a.length; s++) { + var l = a[s]; + if ("Polygon" === o.type) a[s] = kl(l, r[s], e); else if ("MultiPolygon" === o.type) for (var u = 0; u < l.length; u++) { + var h = l[u]; + l[u] = kl(h, r[s][u], e) + } + } + return t.UTF8Encoding = !1, t + } + + function kl(t, e, i) { + for (var n = [], o = e[0], a = e[1], r = 0; r < t.length; r += 2) { + var s = t.charCodeAt(r) - 64, l = t.charCodeAt(r + 1) - 64; + s = s >> 1 ^ -(1 & s), l = l >> 1 ^ -(1 & l), o = s += o, a = l += a, n.push([s / i, l / i]) + } + return n + } + + function Pl(t) { + return "category" === t.type ? Ol(t) : zl(t) + } + + function Nl(t, e) { + return "category" === t.type ? Rl(t, e) : {ticks: t.scale.getTicks()} + } + + function Ol(t) { + var e = t.getLabelModel(), i = El(t, e); + return !e.get("show") || t.scale.isBlank() ? {labels: [], labelCategoryInterval: i.labelCategoryInterval} : i + } + + function El(t, e) { + var i = Bl(t, "labels"), n = Xl(e), o = Vl(i, n); + if (o) return o; + var a, r; + return a = x(n) ? Ul(t, n) : Zl(t, r = "auto" === n ? Wl(t) : n), Gl(i, n, { + labels: a, + labelCategoryInterval: r + }) + } + + function Rl(t, e) { + var i = Bl(t, "ticks"), n = Xl(e), o = Vl(i, n); + if (o) return o; + var a, r; + if (e.get("show") && !t.scale.isBlank() || (a = []), x(n)) a = Ul(t, n, !0); else if ("auto" === n) { + var s = El(t, t.getLabelModel()); + r = s.labelCategoryInterval, a = f(s.labels, function (t) { + return t.tickValue + }) + } else a = Zl(t, r = n, !0); + return Gl(i, n, {ticks: a, tickCategoryInterval: r}) + } + + function zl(t) { + var e = t.scale.getTicks(), i = wl(t); + return { + labels: f(e, function (e, n) { + return {formattedLabel: i(e, n), rawLabel: t.scale.getLabel(e), tickValue: e} + }) + } + } + + function Bl(t, e) { + return jT(t)[e] || (jT(t)[e] = []) + } + + function Vl(t, e) { + for (var i = 0; i < t.length; i++) if (t[i].key === e) return t[i].value + } + + function Gl(t, e, i) { + return t.push({key: e, value: i}), i + } + + function Wl(t) { + var e = jT(t).autoInterval; + return null != e ? e : jT(t).autoInterval = t.calculateCategoryInterval() + } + + function Fl(t) { + var e = Hl(t), i = wl(t), n = (e.axisRotate - e.labelRotate) / 180 * Math.PI, o = t.scale, a = o.getExtent(), + r = o.count(); + if (a[1] - a[0] < 1) return 0; + var s = 1; + r > 40 && (s = Math.max(1, Math.floor(r / 40))); + for (var l = a[0], u = t.dataToCoord(l + 1) - t.dataToCoord(l), h = Math.abs(u * Math.cos(n)), c = Math.abs(u * Math.sin(n)), d = 0, f = 0; l <= a[1]; l += s) { + var p = 0, g = 0, m = me(i(l), e.font, "center", "top"); + p = 1.3 * m.width, g = 1.3 * m.height, d = Math.max(d, p, 7), f = Math.max(f, g, 7) + } + var v = d / h, y = f / c; + isNaN(v) && (v = 1 / 0), isNaN(y) && (y = 1 / 0); + var x = Math.max(0, Math.floor(Math.min(v, y))), _ = jT(t.model), w = _.lastAutoInterval, b = _.lastTickCount; + return null != w && null != b && Math.abs(w - x) <= 1 && Math.abs(b - r) <= 1 && w > x ? x = w : (_.lastTickCount = r, _.lastAutoInterval = x), x + } + + function Hl(t) { + var e = t.getLabelModel(); + return { + axisRotate: t.getRotate ? t.getRotate() : t.isHorizontal && !t.isHorizontal() ? 90 : 0, + labelRotate: e.get("rotate") || 0, + font: e.getFont() + } + } + + function Zl(t, e, i) { + function n(t) { + l.push(i ? t : {formattedLabel: o(t), rawLabel: a.getLabel(t), tickValue: t}) + } + + var o = wl(t), a = t.scale, r = a.getExtent(), s = t.getLabelModel(), l = [], u = Math.max((e || 0) + 1, 1), + h = r[0], c = a.count(); + 0 !== h && u > 1 && c / u > 2 && (h = Math.round(Math.ceil(h / u) * u)); + var d = {min: s.get("showMinLabel"), max: s.get("showMaxLabel")}; + d.min && h !== r[0] && n(r[0]); + for (var f = h; f <= r[1]; f += u) n(f); + return d.max && f !== r[1] && n(r[1]), l + } + + function Ul(t, e, i) { + var n = t.scale, o = wl(t), a = []; + return d(n.getTicks(), function (t) { + var r = n.getLabel(t); + e(t, r) && a.push(i ? t : {formattedLabel: o(t), rawLabel: r, tickValue: t}) + }), a + } + + function Xl(t) { + var e = t.get("interval"); + return null == e ? "auto" : e + } + + function jl(t, e) { + var i = (t[1] - t[0]) / e / 2; + t[0] += i, t[1] -= i + } + + function Yl(t, e, i, n, o) { + function a(t, e) { + return h ? t > e : t < e + } + + var r = e.length; + if (t.onBand && !n && r) { + var s, l = t.getExtent(); + if (1 === r) e[0].coord = l[0], s = e[1] = {coord: l[0]}; else { + var u = e[1].coord - e[0].coord; + d(e, function (t) { + t.coord -= u / 2; + var e = e || 0; + e % 2 > 0 && (t.coord -= u / (2 * (e + 1))) + }), s = {coord: e[r - 1].coord + u}, e.push(s) + } + var h = l[0] > l[1]; + a(e[0].coord, l[0]) && (o ? e[0].coord = l[0] : e.shift()), o && a(l[0], e[0].coord) && e.unshift({coord: l[0]}), a(l[1], s.coord) && (o ? s.coord = l[1] : e.pop()), o && a(s.coord, l[1]) && e.push({coord: l[1]}) + } + } + + function ql(t, e) { + var i = t.mapDimension("defaultedLabel", !0), n = i.length; + if (1 === n) return er(t, e, i[0]); + if (n) { + for (var o = [], a = 0; a < i.length; a++) { + var r = er(t, e, i[a]); + o.push(r) + } + return o.join(" ") + } + } + + function Kl(t, e, i) { + sw.call(this), this.updateData(t, e, i) + } + + function $l(t) { + return [t[0] / 2, t[1] / 2] + } + + function Jl(t, e) { + this.parent.drift(t, e) + } + + function Ql(t) { + this.group = new sw, this._symbolCtor = t || Kl + } + + function tu(t, e, i, n) { + return e && !isNaN(e[0]) && !isNaN(e[1]) && !(n.isIgnore && n.isIgnore(i)) && !(n.clipShape && !n.clipShape.contain(e[0], e[1])) && "none" !== t.getItemVisual(i, "symbol") + } + + function eu(t) { + return null == t || w(t) || (t = {isIgnore: t}), t || {} + } + + function iu(t) { + var e = t.hostModel; + return { + itemStyle: e.getModel("itemStyle").getItemStyle(["color"]), + hoverItemStyle: e.getModel("emphasis.itemStyle").getItemStyle(), + symbolRotate: e.get("symbolRotate"), + symbolOffset: e.get("symbolOffset"), + hoverAnimation: e.get("hoverAnimation"), + labelModel: e.getModel("label"), + hoverLabelModel: e.getModel("emphasis.label"), + cursorStyle: e.get("cursor") + } + } + + function nu(t, e, i) { + var n, o = t.getBaseAxis(), a = t.getOtherAxis(o), r = ou(a, i), s = o.dim, l = a.dim, u = e.mapDimension(l), + h = e.mapDimension(s), c = "x" === l || "radius" === l ? 1 : 0, d = f(t.dimensions, function (t) { + return e.mapDimension(t) + }), p = e.getCalculationInfo("stackResultDimension"); + return (n |= Zs(e, d[0])) && (d[0] = p), (n |= Zs(e, d[1])) && (d[1] = p), { + dataDimsForPoint: d, + valueStart: r, + valueAxisDim: l, + baseAxisDim: s, + stacked: !!n, + valueDim: u, + baseDim: h, + baseDataOffset: c, + stackedOverDimension: e.getCalculationInfo("stackedOverDimension") + } + } + + function ou(t, e) { + var i = 0, n = t.scale.getExtent(); + return "start" === e ? i = n[0] : "end" === e ? i = n[1] : n[0] > 0 ? i = n[0] : n[1] < 0 && (i = n[1]), i + } + + function au(t, e, i, n) { + var o = NaN; + t.stacked && (o = i.get(i.getCalculationInfo("stackedOverDimension"), n)), isNaN(o) && (o = t.valueStart); + var a = t.baseDataOffset, r = []; + return r[a] = i.get(t.baseDim, n), r[1 - a] = o, e.dataToPoint(r) + } + + function ru(t, e) { + var i = []; + return e.diff(t).add(function (t) { + i.push({cmd: "+", idx: t}) + }).update(function (t, e) { + i.push({cmd: "=", idx: e, idx1: t}) + }).remove(function (t) { + i.push({cmd: "-", idx: t}) + }).execute(), i + } + + function su(t) { + return isNaN(t[0]) || isNaN(t[1]) + } + + function lu(t, e, i, n, o, a, r, s, l, u, h) { + return "none" !== u && u ? uu.apply(this, arguments) : hu.apply(this, arguments) + } + + function uu(t, e, i, n, o, a, r, s, l, u, h) { + for (var c = 0, d = i, f = 0; f < n; f++) { + var p = e[d]; + if (d >= o || d < 0) break; + if (su(p)) { + if (h) { + d += a; + continue + } + break + } + if (d === i) t[a > 0 ? "moveTo" : "lineTo"](p[0], p[1]); else if (l > 0) { + var g = e[c], m = "y" === u ? 1 : 0, v = (p[m] - g[m]) * l; + uD(cD, g), cD[m] = g[m] + v, uD(dD, p), dD[m] = p[m] - v, t.bezierCurveTo(cD[0], cD[1], dD[0], dD[1], p[0], p[1]) + } else t.lineTo(p[0], p[1]); + c = d, d += a + } + return f + } + + function hu(t, e, i, n, o, a, r, s, l, u, h) { + for (var c = 0, d = i, f = 0; f < n; f++) { + var p = e[d]; + if (d >= o || d < 0) break; + if (su(p)) { + if (h) { + d += a; + continue + } + break + } + if (d === i) t[a > 0 ? "moveTo" : "lineTo"](p[0], p[1]), uD(cD, p); else if (l > 0) { + var g = d + a, m = e[g]; + if (h) for (; m && su(e[g]);) m = e[g += a]; + var v = .5, y = e[c]; + if (!(m = e[g]) || su(m)) uD(dD, p); else { + su(m) && !h && (m = p), U(hD, m, y); + var x, _; + if ("x" === u || "y" === u) { + var w = "x" === u ? 0 : 1; + x = Math.abs(p[w] - y[w]), _ = Math.abs(p[w] - m[w]) + } else x = S_(p, y), _ = S_(p, m); + lD(dD, p, hD, -l * (1 - (v = _ / (_ + x)))) + } + rD(cD, cD, s), sD(cD, cD, r), rD(dD, dD, s), sD(dD, dD, r), t.bezierCurveTo(cD[0], cD[1], dD[0], dD[1], p[0], p[1]), lD(cD, p, hD, l * v) + } else t.lineTo(p[0], p[1]); + c = d, d += a + } + return f + } + + function cu(t, e) { + var i = [1 / 0, 1 / 0], n = [-1 / 0, -1 / 0]; + if (e) for (var o = 0; o < t.length; o++) { + var a = t[o]; + a[0] < i[0] && (i[0] = a[0]), a[1] < i[1] && (i[1] = a[1]), a[0] > n[0] && (n[0] = a[0]), a[1] > n[1] && (n[1] = a[1]) + } + return {min: e ? i : n, max: e ? n : i} + } + + function du(t, e) { + if (t.length === e.length) { + for (var i = 0; i < t.length; i++) { + var n = t[i], o = e[i]; + if (n[0] !== o[0] || n[1] !== o[1]) return + } + return !0 + } + } + + function fu(t) { + return "number" == typeof t ? t : t ? .5 : 0 + } + + function pu(t) { + var e = t.getGlobalExtent(); + if (t.onBand) { + var i = t.getBandWidth() / 2 - 1, n = e[1] > e[0] ? 1 : -1; + e[0] += n * i, e[1] -= n * i + } + return e + } + + function gu(t, e, i) { + if (!i.valueDim) return []; + for (var n = [], o = 0, a = e.count(); o < a; o++) n.push(au(i, t, e, o)); + return n + } + + function mu(t, e, i, n) { + var o = pu(t.getAxis("x")), a = pu(t.getAxis("y")), r = t.getBaseAxis().isHorizontal(), + s = Math.min(o[0], o[1]), l = Math.min(a[0], a[1]), u = Math.max(o[0], o[1]) - s, + h = Math.max(a[0], a[1]) - l; + if (i) s -= .5, u += .5, l -= .5, h += .5; else { + var c = n.get("lineStyle.width") || 2, d = n.get("clipOverflow") ? c / 2 : Math.max(u, h); + r ? (l -= d, h += 2 * d) : (s -= d, u += 2 * d) + } + var f = new SS({shape: {x: s, y: l, width: u, height: h}}); + return e && (f.shape[r ? "width" : "height"] = 0, po(f, {shape: {width: u, height: h}}, n)), f + } + + function vu(t, e, i, n) { + var o = t.getAngleAxis(), a = t.getRadiusAxis().getExtent().slice(); + a[0] > a[1] && a.reverse(); + var r = o.getExtent(), s = Math.PI / 180; + i && (a[0] -= .5, a[1] += .5); + var l = new vS({ + shape: { + cx: Ao(t.cx, 1), + cy: Ao(t.cy, 1), + r0: Ao(a[0], 1), + r: Ao(a[1], 1), + startAngle: -r[0] * s, + endAngle: -r[1] * s, + clockwise: o.inverse + } + }); + return e && (l.shape.endAngle = -r[0] * s, po(l, {shape: {endAngle: -r[1] * s}}, n)), l + } + + function yu(t, e, i, n) { + return "polar" === t.type ? vu(t, e, i, n) : mu(t, e, i, n) + } + + function xu(t, e, i) { + for (var n = e.getBaseAxis(), o = "x" === n.dim || "radius" === n.dim ? 0 : 1, a = [], r = 0; r < t.length - 1; r++) { + var s = t[r + 1], l = t[r]; + a.push(l); + var u = []; + switch (i) { + case"end": + u[o] = s[o], u[1 - o] = l[1 - o], a.push(u); + break; + case"middle": + var h = (l[o] + s[o]) / 2, c = []; + u[o] = c[o] = h, u[1 - o] = l[1 - o], c[1 - o] = s[1 - o], a.push(u), a.push(c); + break; + default: + u[o] = l[o], u[1 - o] = s[1 - o], a.push(u) + } + } + return t[r] && a.push(t[r]), a + } + + function _u(t, e) { + var i = t.getVisual("visualMeta"); + if (i && i.length && t.count() && "cartesian2d" === e.type) { + for (var n, o, a = i.length - 1; a >= 0; a--) { + var r = i[a].dimension, s = t.dimensions[r], l = t.getDimensionInfo(s); + if ("x" === (n = l && l.coordDim) || "y" === n) { + o = i[a]; + break + } + } + if (o) { + var u = e.getAxis(n), h = f(o.stops, function (t) { + return {coord: u.toGlobalCoord(u.dataToCoord(t.value)), color: t.color} + }), c = h.length, p = o.outerColors.slice(); + c && h[0].coord > h[c - 1].coord && (h.reverse(), p.reverse()); + var g = h[0].coord - 10, m = h[c - 1].coord + 10, v = m - g; + if (v < .001) return "transparent"; + d(h, function (t) { + t.offset = (t.coord - g) / v + }), h.push({ + offset: c ? h[c - 1].offset : .5, + color: p[1] || "transparent" + }), h.unshift({offset: c ? h[0].offset : .5, color: p[0] || "transparent"}); + var y = new LS(0, 0, 0, 0, h, !0); + return y[n] = g, y[n + "2"] = m, y + } + } + } + + function wu(t, e, i) { + var n = t.get("showAllSymbol"), o = "auto" === n; + if (!n || o) { + var a = i.getAxesByScale("ordinal")[0]; + if (a && (!o || !bu(a, e))) { + var r = e.mapDimension(a.dim), s = {}; + return d(a.getViewLabels(), function (t) { + s[t.tickValue] = 1 + }), function (t) { + return !s.hasOwnProperty(e.get(r, t)) + } + } + } + } + + function bu(t, e) { + var i = t.getExtent(), n = Math.abs(i[1] - i[0]) / t.scale.count(); + isNaN(n) && (n = 0); + for (var o = e.count(), a = Math.max(1, Math.round(o / 5)), r = 0; r < o; r += a) if (1.5 * Kl.getSymbolSize(e, r)[t.isHorizontal() ? 1 : 0] > n) return !1; + return !0 + } + + function Su(t) { + return this._axes[t] + } + + function Mu(t) { + xD.call(this, t) + } + + function Iu(t, e) { + return e.type || (e.data ? "category" : "value") + } + + function Tu(t, e, i) { + return t.getCoordSysModel() === e + } + + function Du(t, e, i) { + this._coordsMap = {}, this._coordsList = [], this._axesMap = {}, this._axesList = [], this._initCartesian(t, e, i), this.model = t + } + + function Au(t, e, i) { + i.getAxesOnZeroOf = function () { + return n ? [n] : [] + }; + var n, o = t[e], a = i.model, r = a.get("axisLine.onZero"), s = a.get("axisLine.onZeroAxisIndex"); + if (r) if (null == s) { + for (var l in o) if (o.hasOwnProperty(l) && Cu(o[l])) { + n = o[l]; + break + } + } else Cu(o[s]) && (n = o[s]) + } + + function Cu(t) { + return t && "category" !== t.type && "time" !== t.type && _l(t) + } + + function Lu(t, e) { + var i = t.getExtent(), n = i[0] + i[1]; + t.toGlobalCoord = "x" === t.dim ? function (t) { + return t + e + } : function (t) { + return n - t + e + }, t.toLocalCoord = "x" === t.dim ? function (t) { + return t - e + } : function (t) { + return n - t + e + } + } + + function ku(t, e) { + return f(AD, function (e) { + return t.getReferringComponents(e)[0] + }) + } + + function Pu(t) { + return "cartesian2d" === t.get("coordinateSystem") + } + + function Nu(t) { + var e = {componentType: t.mainType}; + return e[t.mainType + "Index"] = t.componentIndex, e + } + + function Ou(t, e, i, n) { + var o, a, r = Oo(i - t.rotation), s = n[0] > n[1], l = "start" === e && !s || "start" !== e && s; + return Eo(r - CD / 2) ? (a = l ? "bottom" : "top", o = "center") : Eo(r - 1.5 * CD) ? (a = l ? "top" : "bottom", o = "center") : (a = "middle", o = r < 1.5 * CD && r > CD / 2 ? l ? "left" : "right" : l ? "right" : "left"), { + rotation: r, + textAlign: o, + textVerticalAlign: a + } + } + + function Eu(t) { + var e = t.get("tooltip"); + return t.get("silent") || !(t.get("triggerEvent") || e && e.show) + } + + function Ru(t, e, i) { + var n = t.get("axisLabel.showMinLabel"), o = t.get("axisLabel.showMaxLabel"); + e = e || [], i = i || []; + var a = e[0], r = e[1], s = e[e.length - 1], l = e[e.length - 2], u = i[0], h = i[1], c = i[i.length - 1], + d = i[i.length - 2]; + !1 === n ? (zu(a), zu(u)) : Bu(a, r) && (n ? (zu(r), zu(h)) : (zu(a), zu(u))), !1 === o ? (zu(s), zu(c)) : Bu(l, s) && (o ? (zu(l), zu(d)) : (zu(s), zu(c))) + } + + function zu(t) { + t && (t.ignore = !0) + } + + function Bu(t, e, i) { + var n = t && t.getBoundingRect().clone(), o = e && e.getBoundingRect().clone(); + if (n && o) { + var a = lt([]); + return dt(a, a, -t.rotation), n.applyTransform(ht([], a, t.getLocalTransform())), o.applyTransform(ht([], a, e.getLocalTransform())), n.intersect(o) + } + } + + function Vu(t) { + return "middle" === t || "center" === t + } + + function Gu(t, e, i) { + var n = e.axis; + if (e.get("axisTick.show") && !n.scale.isBlank()) { + for (var o = e.getModel("axisTick"), a = o.getModel("lineStyle"), s = o.get("length"), l = n.getTicksCoords(), u = [], h = [], c = t._transform, d = [], f = 0; f < l.length; f++) { + var p = l[f].coord; + u[0] = p, u[1] = 0, h[0] = p, h[1] = i.tickDirection * s, c && (Q(u, u, c), Q(h, h, c)); + var g = new MS(Gn({ + anid: "tick_" + l[f].tickValue, + shape: {x1: u[0], y1: u[1], x2: h[0], y2: h[1]}, + style: r(a.getLineStyle(), {stroke: e.get("axisLine.lineStyle.color")}), + z2: 2, + silent: !0 + })); + t.group.add(g), d.push(g) + } + return d + } + } + + function Wu(t, e, i) { + var n = e.axis; + if (T(i.axisLabelShow, e.get("axisLabel.show")) && !n.scale.isBlank()) { + var o = e.getModel("axisLabel"), a = o.get("margin"), r = n.getViewLabels(), + s = (T(i.labelRotate, o.get("rotate")) || 0) * CD / 180, l = PD(i.rotation, s, i.labelDirection), + u = e.getCategories(!0), h = [], c = Eu(e), f = e.get("triggerEvent"); + return d(r, function (r, s) { + var d = r.tickValue, p = r.formattedLabel, g = r.rawLabel, m = o; + u && u[d] && u[d].textStyle && (m = new wo(u[d].textStyle, o, e.ecModel)); + var v = m.getTextColor() || e.get("axisLine.lineStyle.color"), + y = [n.dataToCoord(d), i.labelOffset + i.labelDirection * a], + x = new fS({anid: "label_" + d, position: y, rotation: l.rotation, silent: c, z2: 10}); + no(x.style, m, { + text: p, + textAlign: m.getShallow("align", !0) || l.textAlign, + textVerticalAlign: m.getShallow("verticalAlign", !0) || m.getShallow("baseline", !0) || l.textVerticalAlign, + textFill: "function" == typeof v ? v("category" === n.type ? g : "value" === n.type ? d + "" : d, s) : v + }), f && (x.eventData = Nu(e), x.eventData.targetType = "axisLabel", x.eventData.value = g), t._dumbGroup.add(x), x.updateTransform(), h.push(x), t.group.add(x), x.decomposeTransform() + }), h + } + } + + function Fu(t, e) { + var i = {axesInfo: {}, seriesInvolved: !1, coordSysAxesInfo: {}, coordSysMap: {}}; + return Hu(i, t, e), i.seriesInvolved && Uu(i, t), i + } + + function Hu(t, e, i) { + var n = e.getComponent("tooltip"), o = e.getComponent("axisPointer"), a = o.get("link", !0) || [], r = []; + ND(i.getCoordinateSystems(), function (i) { + function s(n, s, l) { + var c = l.model.getModel("axisPointer", o), d = c.get("show"); + if (d && ("auto" !== d || n || $u(c))) { + null == s && (s = c.get("triggerTooltip")); + var f = (c = n ? Zu(l, h, o, e, n, s) : c).get("snap"), p = Ju(l.model), + g = s || f || "category" === l.type, m = t.axesInfo[p] = { + key: p, + axis: l, + coordSys: i, + axisPointerModel: c, + triggerTooltip: s, + involveSeries: g, + snap: f, + useHandle: $u(c), + seriesModels: [] + }; + u[p] = m, t.seriesInvolved |= g; + var v = Xu(a, l); + if (null != v) { + var y = r[v] || (r[v] = {axesInfo: {}}); + y.axesInfo[p] = m, y.mapper = a[v].mapper, m.linkGroup = y + } + } + } + + if (i.axisPointerEnabled) { + var l = Ju(i.model), u = t.coordSysAxesInfo[l] = {}; + t.coordSysMap[l] = i; + var h = i.model.getModel("tooltip", n); + if (ND(i.getAxes(), OD(s, !1, null)), i.getTooltipAxes && n && h.get("show")) { + var c = "axis" === h.get("trigger"), d = "cross" === h.get("axisPointer.type"), + f = i.getTooltipAxes(h.get("axisPointer.axis")); + (c || d) && ND(f.baseAxes, OD(s, !d || "cross", c)), d && ND(f.otherAxes, OD(s, "cross", !1)) + } + } + }) + } + + function Zu(t, e, n, o, a, s) { + var l = e.getModel("axisPointer"), u = {}; + ND(["type", "snap", "lineStyle", "shadowStyle", "label", "animation", "animationDurationUpdate", "animationEasingUpdate", "z"], function (t) { + u[t] = i(l.get(t)) + }), u.snap = "category" !== t.type && !!s, "cross" === l.get("type") && (u.type = "line"); + var h = u.label || (u.label = {}); + if (null == h.show && (h.show = !1), "cross" === a) { + var c = l.get("label.show"); + if (h.show = null == c || c, !s) { + var d = u.lineStyle = l.get("crossStyle"); + d && r(h, d.textStyle) + } + } + return t.model.getModel("axisPointer", new wo(u, n, o)) + } + + function Uu(t, e) { + e.eachSeries(function (e) { + var i = e.coordinateSystem, n = e.get("tooltip.trigger", !0), o = e.get("tooltip.show", !0); + i && "none" !== n && !1 !== n && "item" !== n && !1 !== o && !1 !== e.get("axisPointer.show", !0) && ND(t.coordSysAxesInfo[Ju(i.model)], function (t) { + var n = t.axis; + i.getAxis(n.dim) === n && (t.seriesModels.push(e), null == t.seriesDataCount && (t.seriesDataCount = 0), t.seriesDataCount += e.getData().count()) + }) + }, this) + } + + function Xu(t, e) { + for (var i = e.model, n = e.dim, o = 0; o < t.length; o++) { + var a = t[o] || {}; + if (ju(a[n + "AxisId"], i.id) || ju(a[n + "AxisIndex"], i.componentIndex) || ju(a[n + "AxisName"], i.name)) return o + } + } + + function ju(t, e) { + return "all" === t || y(t) && l(t, e) >= 0 || t === e + } + + function Yu(t) { + var e = qu(t); + if (e) { + var i = e.axisPointerModel, n = e.axis.scale, o = i.option, a = i.get("status"), r = i.get("value"); + null != r && (r = n.parse(r)); + var s = $u(i); + null == a && (o.status = s ? "show" : "hide"); + var l = n.getExtent().slice(); + l[0] > l[1] && l.reverse(), (null == r || r > l[1]) && (r = l[1]), r < l[0] && (r = l[0]), o.value = r, s && (o.status = e.axis.scale.isBlank() ? "hide" : "show") + } + } + + function qu(t) { + var e = (t.ecModel.getComponent("axisPointer") || {}).coordSysAxesInfo; + return e && e.axesInfo[Ju(t)] + } + + function Ku(t) { + var e = qu(t); + return e && e.axisPointerModel + } + + function $u(t) { + return !!t.get("handle.show") + } + + function Ju(t) { + return t.type + "||" + t.id + } + + function Qu(t, e, i, n, o, a) { + var r = ED.getAxisPointerClass(t.axisPointerClass); + if (r) { + var s = Ku(e); + s ? (t._axisPointer || (t._axisPointer = new r)).render(e, s, n, a) : th(t, n) + } + } + + function th(t, e, i) { + var n = t._axisPointer; + n && n.dispose(e, i), t._axisPointer = null + } + + function eh(t, e, i) { + i = i || {}; + var n = t.coordinateSystem, o = e.axis, a = {}, r = o.getAxesOnZeroOf()[0], s = o.position, + l = r ? "onZero" : s, u = o.dim, h = n.getRect(), c = [h.x, h.x + h.width, h.y, h.y + h.height], + d = {left: 0, right: 1, top: 0, bottom: 1, onZero: 2}, f = e.get("offset") || 0, + p = "x" === u ? [c[2] - f, c[3] + f] : [c[0] - f, c[1] + f]; + if (r) { + var g = r.toGlobalCoord(r.dataToCoord(0)); + p[d.onZero] = Math.max(Math.min(g, p[1]), p[0]) + } + a.position = ["y" === u ? p[d[l]] : c[0], "x" === u ? p[d[l]] : c[3]], a.rotation = Math.PI / 2 * ("x" === u ? 0 : 1); + var m = {top: -1, bottom: 1, left: -1, right: 1}; + a.labelDirection = a.tickDirection = a.nameDirection = m[s], a.labelOffset = r ? p[d[s]] - p[d.onZero] : 0, e.get("axisTick.inside") && (a.tickDirection = -a.tickDirection), T(i.labelInside, e.get("axisLabel.inside")) && (a.labelDirection = -a.labelDirection); + var v = e.get("axisLabel.rotate"); + return a.labelRotate = "top" === l ? -v : v, a.z2 = 1, a + } + + function ih(t, e, i, n, o, a, r) { + io(t, e, i.getModel("label"), i.getModel("emphasis.label"), { + labelFetcher: o, + labelDataIndex: a, + defaultText: ql(o.getData(), a), + isRectText: !0, + autoColor: n + }), nh(t), nh(e) + } + + function nh(t, e) { + "outside" === t.textPosition && (t.textPosition = e) + } + + function oh(t, e, i) { + i.style.text = null, fo(i, {shape: {width: 0}}, e, t, function () { + i.parent && i.parent.remove(i) + }) + } + + function ah(t, e, i) { + i.style.text = null, fo(i, {shape: {r: i.shape.r0}}, e, t, function () { + i.parent && i.parent.remove(i) + }) + } + + function rh(t, e, i, n, o, a, s, l) { + var u = e.getItemVisual(i, "color"), h = e.getItemVisual(i, "opacity"), c = n.getModel("itemStyle"), + d = n.getModel("emphasis.itemStyle").getBarItemStyle(); + l || t.setShape("r", c.get("barBorderRadius") || 0), t.useStyle(r({fill: u, opacity: h}, c.getBarItemStyle())); + var f = n.getShallow("cursor"); + f && t.attr("cursor", f); + var p = s ? o.height > 0 ? "bottom" : "top" : o.width > 0 ? "left" : "right"; + l || ih(t.style, d, n, u, a, i, p), eo(t, d) + } + + function sh(t, e) { + var i = t.get(HD) || 0; + return Math.min(i, Math.abs(e.width), Math.abs(e.height)) + } + + function lh(t, e, i) { + var n = t.getData(), o = [], a = n.getLayout("valueAxisHorizontal") ? 1 : 0; + o[1 - a] = n.getLayout("valueAxisStart"); + var r = new XD({shape: {points: n.getLayout("largePoints")}, incremental: !!i, __startPoint: o, __valueIdx: a}); + e.add(r), uh(r, t, n) + } + + function uh(t, e, i) { + var n = i.getVisual("borderColor") || i.getVisual("color"), + o = e.getModel("itemStyle").getItemStyle(["color", "borderColor"]); + t.useStyle(o), t.style.fill = null, t.style.stroke = n, t.style.lineWidth = i.getLayout("barWidth") + } + + function hh(t, e, i, n) { + var o = e.getData(), a = this.dataIndex, r = o.getName(a), s = e.get("selectedOffset"); + n.dispatchAction({type: "pieToggleSelect", from: t, name: r, seriesId: e.id}), o.each(function (t) { + ch(o.getItemGraphicEl(t), o.getItemLayout(t), e.isSelected(o.getName(t)), s, i) + }) + } + + function ch(t, e, i, n, o) { + var a = (e.startAngle + e.endAngle) / 2, r = Math.cos(a), s = Math.sin(a), l = i ? n : 0, u = [r * l, s * l]; + o ? t.animate().when(200, {position: u}).start("bounceOut") : t.attr("position", u) + } + + function dh(t, e) { + function i() { + a.ignore = a.hoverIgnore, r.ignore = r.hoverIgnore + } + + function n() { + a.ignore = a.normalIgnore, r.ignore = r.normalIgnore + } + + sw.call(this); + var o = new vS({z2: 2}), a = new bS, r = new fS; + this.add(o), this.add(a), this.add(r), this.updateData(t, e, !0), this.on("emphasis", i).on("normal", n).on("mouseover", i).on("mouseout", n) + } + + function fh(t, e, i, n, o, a, r) { + function s(e, i) { + for (var n = e; n >= 0 && (t[n].y -= i, !(n > 0 && t[n].y > t[n - 1].y + t[n - 1].height)); n--) ; + } + + function l(t, e, i, n, o, a) { + for (var r = e ? Number.MAX_VALUE : 0, s = 0, l = t.length; s < l; s++) if ("center" !== t[s].position) { + var u = Math.abs(t[s].y - n), h = t[s].len, c = t[s].len2, + d = u < o + h ? Math.sqrt((o + h + c) * (o + h + c) - u * u) : Math.abs(t[s].x - i); + e && d >= r && (d = r - 10), !e && d <= r && (d = r + 10), t[s].x = i + d * a, r = d + } + } + + t.sort(function (t, e) { + return t.y - e.y + }); + for (var u, h = 0, c = t.length, d = [], f = [], p = 0; p < c; p++) (u = t[p].y - h) < 0 && function (e, i, n, o) { + for (var a = e; a < i; a++) if (t[a].y += n, a > e && a + 1 < i && t[a + 1].y > t[a].y + t[a].height) return void s(a, n / 2); + s(i - 1, n / 2) + }(p, c, -u), h = t[p].y + t[p].height; + r - h < 0 && s(c - 1, h - r); + for (p = 0; p < c; p++) t[p].y >= i ? f.push(t[p]) : d.push(t[p]); + l(d, !1, e, i, n, o), l(f, !0, e, i, n, o) + } + + function ph(t, e, i, n, o, a) { + for (var r = [], s = [], l = 0; l < t.length; l++) t[l].x < e ? r.push(t[l]) : s.push(t[l]); + fh(s, e, i, n, 1, o, a), fh(r, e, i, n, -1, o, a); + for (l = 0; l < t.length; l++) { + var u = t[l].linePoints; + if (u) { + var h = u[1][0] - u[2][0]; + t[l].x < e ? u[2][0] = t[l].x + 3 : u[2][0] = t[l].x - 3, u[1][1] = u[2][1] = t[l].y, u[1][0] = u[2][0] + h + } + } + } + + function gh() { + this.group = new sw + } + + function mh(t, e, i) { + qT.call(this, t, e, i), this.type = "value", this.angle = 0, this.name = "", this.model + } + + function vh(t, e, i) { + this._model = t, this.dimensions = [], this._indicatorAxes = f(t.getIndicatorModels(), function (t, e) { + var i = "indicator_" + e, n = new mh(i, new mT); + return n.name = t.get("name"), n.model = t, t.axis = n, this.dimensions.push(i), n + }, this), this.resize(t, i), this.cx, this.cy, this.r, this.startAngle + } + + function yh(t, e) { + return r({show: e}, t) + } + + function xh(t) { + return y(t) || (t = [+t, +t]), t + } + + function _h() { + E_.call(this) + } + + function wh(t) { + this.name = t, this.zoomLimit, E_.call(this), this._roamTransformable = new _h, this._rawTransformable = new _h, this._center, this._zoom + } + + function bh(t, e, i, n) { + var o = i.seriesModel, a = o ? o.coordinateSystem : null; + return a === this ? a[t](n) : null + } + + function Sh(t, e, i, n, o) { + wh.call(this, t), this.map = e, this._nameCoordMap = R(), this.loadGeoJson(i, n, o) + } + + function Mh(t, e, i, n) { + var o = i.geoModel, a = i.seriesModel, + r = o ? o.coordinateSystem : a ? a.coordinateSystem || (a.getReferringComponents("geo")[0] || {}).coordinateSystem : null; + return r === this ? r[t](n) : null + } + + function Ih(t, e) { + var i = t.get("boundingCoords"); + if (null != i) { + var n = i[0], o = i[1]; + isNaN(n[0]) || isNaN(n[1]) || isNaN(o[0]) || isNaN(o[1]) || this.setBoundingRect(n[0], n[1], o[0] - n[0], o[1] - n[1]) + } + var a, r = this.getBoundingRect(), s = t.get("layoutCenter"), l = t.get("layoutSize"), u = e.getWidth(), + h = e.getHeight(), c = t.get("aspectScale") || .75, d = r.width / r.height * c, f = !1; + s && l && (s = [Do(s[0], u), Do(s[1], h)], l = Do(l, Math.min(u, h)), isNaN(s[0]) || isNaN(s[1]) || isNaN(l) || (f = !0)); + if (f) { + var p = {}; + d > 1 ? (p.width = l, p.height = l / d) : (p.height = l, p.width = l * d), p.y = s[1] - p.height / 2, p.x = s[0] - p.width / 2 + } else (a = t.getBoxLayoutParams()).aspect = d, p = Qo(a, {width: u, height: h}); + this.setViewRect(p.x, p.y, p.width, p.height), this.setCenter(t.get("center")), this.setZoom(t.get("zoom")) + } + + function Th(t, e) { + d(e.get("geoCoord"), function (e, i) { + t.addGeoCoord(i, e) + }) + } + + function Dh(t, e, i) { + Lh(t)[e] = i + } + + function Ah(t, e, i) { + var n = Lh(t); + n[e] === i && (n[e] = null) + } + + function Ch(t, e) { + return !!Lh(t)[e] + } + + function Lh(t) { + return t[xA] || (t[xA] = {}) + } + + function kh(t) { + this.pointerChecker, this._zr = t, this._opt = {}; + var e = m, n = e(Ph, this), o = e(Nh, this), a = e(Oh, this), s = e(Eh, this), l = e(Rh, this); + D_.call(this), this.setPointerChecker = function (t) { + this.pointerChecker = t + }, this.enable = function (e, u) { + this.disable(), this._opt = r(i(u) || {}, { + zoomOnMouseWheel: !0, + moveOnMouseMove: !0, + preventDefaultMouseMove: !0 + }), null == e && (e = !0), !0 !== e && "move" !== e && "pan" !== e || (t.on("mousedown", n), t.on("mousemove", o), t.on("mouseup", a)), !0 !== e && "scale" !== e && "zoom" !== e || (t.on("mousewheel", s), t.on("pinch", l)) + }, this.disable = function () { + t.off("mousedown", n), t.off("mousemove", o), t.off("mouseup", a), t.off("mousewheel", s), t.off("pinch", l) + }, this.dispose = this.disable, this.isDragging = function () { + return this._dragging + }, this.isPinching = function () { + return this._pinching + } + } + + function Ph(t) { + if (!(ci(t) || t.target && t.target.draggable)) { + var e = t.offsetX, i = t.offsetY; + this.pointerChecker && this.pointerChecker(t, e, i) && (this._x = e, this._y = i, this._dragging = !0) + } + } + + function Nh(t) { + if (!ci(t) && Bh(this, "moveOnMouseMove", t) && this._dragging && "pinch" !== t.gestureEvent && !Ch(this._zr, "globalPan")) { + var e = t.offsetX, i = t.offsetY, n = this._x, o = this._y, a = e - n, r = i - o; + this._x = e, this._y = i, this._opt.preventDefaultMouseMove && zw(t.event), this.trigger("pan", a, r, n, o, e, i) + } + } + + function Oh(t) { + ci(t) || (this._dragging = !1) + } + + function Eh(t) { + if (Bh(this, "zoomOnMouseWheel", t) && 0 !== t.wheelDelta) { + var e = t.wheelDelta > 0 ? 1.1 : 1 / 1.1; + zh.call(this, t, e, t.offsetX, t.offsetY) + } + } + + function Rh(t) { + if (!Ch(this._zr, "globalPan")) { + var e = t.pinchScale > 1 ? 1.1 : 1 / 1.1; + zh.call(this, t, e, t.pinchX, t.pinchY) + } + } + + function zh(t, e, i, n) { + this.pointerChecker && this.pointerChecker(t, i, n) && (zw(t.event), this.trigger("zoom", e, i, n)) + } + + function Bh(t, e, i) { + var n = t._opt[e]; + return n && (!_(n) || i.event[n + "Key"]) + } + + function Vh(t, e, i) { + var n = t.target, o = n.position; + o[0] += e, o[1] += i, n.dirty() + } + + function Gh(t, e, i, n) { + var o = t.target, a = t.zoomLimit, r = o.position, s = o.scale, l = t.zoom = t.zoom || 1; + if (l *= e, a) { + var u = a.min || 0, h = a.max || 1 / 0; + l = Math.max(Math.min(h, l), u) + } + var c = l / t.zoom; + t.zoom = l, r[0] -= (i - r[0]) * (c - 1), r[1] -= (n - r[1]) * (c - 1), s[0] *= c, s[1] *= c, o.dirty() + } + + function Wh(t, e, i) { + var n = e.getComponentByElement(t.topTarget), o = n && n.coordinateSystem; + return n && n !== i && !_A[n.mainType] && o && o.model !== i + } + + function Fh(t, e) { + var i = t.getItemStyle(), n = t.get("areaColor"); + return null != n && (i.fill = n), i + } + + function Hh(t, e, i, n, o) { + i.off("click"), i.off("mousedown"), e.get("selectedMode") && (i.on("mousedown", function () { + t._mouseDownFlag = !0 + }), i.on("click", function (a) { + if (t._mouseDownFlag) { + t._mouseDownFlag = !1; + for (var r = a.target; !r.__regions;) r = r.parent; + if (r) { + var s = { + type: ("geo" === e.mainType ? "geo" : "map") + "ToggleSelect", + batch: f(r.__regions, function (t) { + return {name: t.name, from: o.uid} + }) + }; + s[e.mainType + "Id"] = e.id, n.dispatchAction(s), Zh(e, i) + } + } + })) + } + + function Zh(t, e) { + e.eachChild(function (e) { + d(e.__regions, function (i) { + e.trigger(t.isSelected(i.name) ? "emphasis" : "normal") + }) + }) + } + + function Uh(t, e) { + var i = new sw; + this._controller = new kh(t.getZr()), this._controllerHost = {target: e ? i : null}, this.group = i, this._updateGroup = e, this._mouseDownFlag + } + + function Xh(t, e, i) { + var n = t.getZoom(), o = t.getCenter(), a = e.zoom, r = t.dataToPoint(o); + if (null != e.dx && null != e.dy) { + r[0] -= e.dx, r[1] -= e.dy; + o = t.pointToData(r); + t.setCenter(o) + } + if (null != a) { + if (i) { + var s = i.min || 0, l = i.max || 1 / 0; + a = Math.max(Math.min(n * a, l), s) / n + } + t.scale[0] *= a, t.scale[1] *= a; + var u = t.position, h = (e.originX - u[0]) * (a - 1), c = (e.originY - u[1]) * (a - 1); + u[0] -= h, u[1] -= c, t.updateTransform(); + o = t.pointToData(r); + t.setCenter(o), t.setZoom(a * n) + } + return {center: t.getCenter(), zoom: t.getZoom()} + } + + function jh(t, e) { + var i = {}; + return d(t, function (t) { + t.each(t.mapDimension("value"), function (e, n) { + var o = "ec-" + t.getName(n); + i[o] = i[o] || [], isNaN(e) || i[o].push(e) + }) + }), t[0].map(t[0].mapDimension("value"), function (n, o) { + for (var a = "ec-" + t[0].getName(o), r = 0, s = 1 / 0, l = -1 / 0, u = i[a].length, h = 0; h < u; h++) s = Math.min(s, i[a][h]), l = Math.max(l, i[a][h]), r += i[a][h]; + var c; + return c = "min" === e ? s : "max" === e ? l : "average" === e ? r / u : r, 0 === u ? NaN : c + }) + } + + function Yh(t) { + var e = t.mainData, i = t.datas; + i || (i = {main: e}, t.datasAttr = {main: "data"}), t.datas = t.mainData = null, tc(e, i, t), wA(i, function (i) { + wA(e.TRANSFERABLE_METHODS, function (e) { + i.wrapMethod(e, v(qh, t)) + }) + }), e.wrapMethod("cloneShallow", v($h, t)), wA(e.CHANGABLE_METHODS, function (i) { + e.wrapMethod(i, v(Kh, t)) + }), k(i[e.dataType] === e) + } + + function qh(t, e) { + if (Qh(this)) { + var i = a({}, this[bA]); + i[this.dataType] = e, tc(e, i, t) + } else ec(e, this.dataType, this[SA], t); + return e + } + + function Kh(t, e) { + return t.struct && t.struct.update(this), e + } + + function $h(t, e) { + return wA(e[bA], function (i, n) { + i !== e && ec(i.cloneShallow(), n, e, t) + }), e + } + + function Jh(t) { + var e = this[SA]; + return null == t || null == e ? e : e[bA][t] + } + + function Qh(t) { + return t[SA] === t + } + + function tc(t, e, i) { + t[bA] = {}, wA(e, function (e, n) { + ec(e, n, t, i) + }) + } + + function ec(t, e, i, n) { + i[bA][e] = t, t[SA] = i, t.dataType = e, n.struct && (t[n.structAttr] = n.struct, n.struct[n.datasAttr[e]] = t), t.getLinkedData = Jh + } + + function ic(t, e, i) { + this.root, this.data, this._nodes = [], this.hostModel = t, this.levelModels = f(e || [], function (e) { + return new wo(e, t, t.ecModel) + }), this.leavesModel = new wo(i || {}, t, t.ecModel) + } + + function nc(t, e) { + var i = e.children; + t.parentNode !== e && (i.push(t), t.parentNode = e) + } + + function oc(t) { + t.hierNode = { + defaultAncestor: null, + ancestor: t, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: 0, + thread: null + }; + for (var e, i, n = [t]; e = n.pop();) if (i = e.children, e.isExpand && i.length) for (var o = i.length - 1; o >= 0; o--) { + var a = i[o]; + a.hierNode = { + defaultAncestor: null, + ancestor: a, + prelim: 0, + modifier: 0, + change: 0, + shift: 0, + i: o, + thread: null + }, n.push(a) + } + } + + function ac(t, e) { + var i = t.isExpand ? t.children : [], n = t.parentNode.children, o = t.hierNode.i ? n[t.hierNode.i - 1] : null; + if (i.length) { + hc(t); + var a = (i[0].hierNode.prelim + i[i.length - 1].hierNode.prelim) / 2; + o ? (t.hierNode.prelim = o.hierNode.prelim + e(t, o), t.hierNode.modifier = t.hierNode.prelim - a) : t.hierNode.prelim = a + } else o && (t.hierNode.prelim = o.hierNode.prelim + e(t, o)); + t.parentNode.hierNode.defaultAncestor = cc(t, o, t.parentNode.hierNode.defaultAncestor || n[0], e) + } + + function rc(t) { + var e = t.hierNode.prelim + t.parentNode.hierNode.modifier; + t.setLayout({x: e}, !0), t.hierNode.modifier += t.parentNode.hierNode.modifier + } + + function sc(t) { + return arguments.length ? t : mc + } + + function lc(t, e) { + var i = {}; + return t -= Math.PI / 2, i.x = e * Math.cos(t), i.y = e * Math.sin(t), i + } + + function uc(t, e) { + return Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}) + } + + function hc(t) { + for (var e = t.children, i = e.length, n = 0, o = 0; --i >= 0;) { + var a = e[i]; + a.hierNode.prelim += n, a.hierNode.modifier += n, o += a.hierNode.change, n += a.hierNode.shift + o + } + } + + function cc(t, e, i, n) { + if (e) { + for (var o = t, a = t, r = a.parentNode.children[0], s = e, l = o.hierNode.modifier, u = a.hierNode.modifier, h = r.hierNode.modifier, c = s.hierNode.modifier; s = dc(s), a = fc(a), s && a;) { + o = dc(o), r = fc(r), o.hierNode.ancestor = t; + var d = s.hierNode.prelim + c - a.hierNode.prelim - u + n(s, a); + d > 0 && (gc(pc(s, t, i), t, d), u += d, l += d), c += s.hierNode.modifier, u += a.hierNode.modifier, l += o.hierNode.modifier, h += r.hierNode.modifier + } + s && !dc(o) && (o.hierNode.thread = s, o.hierNode.modifier += c - l), a && !fc(r) && (r.hierNode.thread = a, r.hierNode.modifier += u - h, i = t) + } + return i + } + + function dc(t) { + var e = t.children; + return e.length && t.isExpand ? e[e.length - 1] : t.hierNode.thread + } + + function fc(t) { + var e = t.children; + return e.length && t.isExpand ? e[0] : t.hierNode.thread + } + + function pc(t, e, i) { + return t.hierNode.ancestor.parentNode === e.parentNode ? t.hierNode.ancestor : i + } + + function gc(t, e, i) { + var n = i / (e.hierNode.i - t.hierNode.i); + e.hierNode.change -= n, e.hierNode.shift += i, e.hierNode.modifier += i, e.hierNode.prelim += i, t.hierNode.change += n + } + + function mc(t, e) { + return t.parentNode === e.parentNode ? 1 : 2 + } + + function vc(t, e) { + var i = t.getItemLayout(e); + return i && !isNaN(i.x) && !isNaN(i.y) && "none" !== t.getItemVisual(e, "symbol") + } + + function yc(t, e, i) { + return i.itemModel = e, i.itemStyle = e.getModel("itemStyle").getItemStyle(), i.hoverItemStyle = e.getModel("emphasis.itemStyle").getItemStyle(), i.lineStyle = e.getModel("lineStyle").getLineStyle(), i.labelModel = e.getModel("label"), i.hoverLabelModel = e.getModel("emphasis.label"), !1 === t.isExpand && 0 !== t.children.length ? i.symbolInnerColor = i.itemStyle.fill : i.symbolInnerColor = "#fff", i + } + + function xc(t, e, i, n, o, a) { + var s = !i, l = t.tree.getNodeByDataIndex(e), a = yc(l, l.getModel(), a), u = t.tree.root, + h = l.parentNode === u ? l : l.parentNode || l, c = t.getItemGraphicEl(h.dataIndex), d = h.getLayout(), + f = c ? {x: c.position[0], y: c.position[1], rawX: c.__radialOldRawX, rawY: c.__radialOldRawY} : d, + p = l.getLayout(); + s ? (i = new Kl(t, e, a)).attr("position", [f.x, f.y]) : i.updateData(t, e, a), i.__radialOldRawX = i.__radialRawX, i.__radialOldRawY = i.__radialRawY, i.__radialRawX = p.rawX, i.__radialRawY = p.rawY, n.add(i), t.setItemGraphicEl(e, i), fo(i, {position: [p.x, p.y]}, o); + var g = i.getSymbolPath(); + if ("radial" === a.layout) { + var m, v, y = u.children[0], x = y.getLayout(), _ = y.children.length; + if (p.x === x.x && !0 === l.isExpand) { + var w = {}; + w.x = (y.children[0].getLayout().x + y.children[_ - 1].getLayout().x) / 2, w.y = (y.children[0].getLayout().y + y.children[_ - 1].getLayout().y) / 2, (m = Math.atan2(w.y - x.y, w.x - x.x)) < 0 && (m = 2 * Math.PI + m), (v = w.x < x.x) && (m -= Math.PI) + } else (m = Math.atan2(p.y - x.y, p.x - x.x)) < 0 && (m = 2 * Math.PI + m), 0 === l.children.length || 0 !== l.children.length && !1 === l.isExpand ? (v = p.x < x.x) && (m -= Math.PI) : (v = p.x > x.x) || (m -= Math.PI); + var b = v ? "left" : "right"; + g.setStyle({textPosition: b, textRotation: -m, textOrigin: "center", verticalAlign: "middle"}) + } + if (l.parentNode && l.parentNode !== u) { + var S = i.__edge; + S || (S = i.__edge = new TS({ + shape: wc(a, f, f), + style: r({opacity: 0}, a.lineStyle) + })), fo(S, {shape: wc(a, d, p), style: {opacity: 1}}, o), n.add(S) + } + } + + function _c(t, e, i, n, o, a) { + for (var r, s = t.tree.getNodeByDataIndex(e), l = t.tree.root, a = yc(s, s.getModel(), a), u = s.parentNode === l ? s : s.parentNode || s; null == (r = u.getLayout());) u = u.parentNode === l ? u : u.parentNode || u; + fo(i, {position: [r.x + 1, r.y + 1]}, o, function () { + n.remove(i), t.setItemGraphicEl(e, null) + }), i.fadeOut(null, {keepLabel: !0}); + var h = i.__edge; + h && fo(h, {shape: wc(a, r, r), style: {opacity: 0}}, o, function () { + n.remove(h) + }) + } + + function wc(t, e, i) { + var n, o, a, r, s = t.orient; + if ("radial" === t.layout) { + var l = e.rawX, u = e.rawY, h = i.rawX, c = i.rawY, d = lc(l, u), f = lc(l, u + (c - u) * t.curvature), + p = lc(h, c + (u - c) * t.curvature), g = lc(h, c); + return {x1: d.x, y1: d.y, x2: g.x, y2: g.y, cpx1: f.x, cpy1: f.y, cpx2: p.x, cpy2: p.y} + } + var l = e.x, u = e.y, h = i.x, c = i.y; + return "LR" !== s && "RL" !== s || (n = l + (h - l) * t.curvature, o = u, a = h + (l - h) * t.curvature, r = c), "TB" !== s && "BT" !== s || (n = l, o = u + (c - u) * t.curvature, a = h, r = c + (u - c) * t.curvature), { + x1: l, + y1: u, + x2: h, + y2: c, + cpx1: n, + cpy1: o, + cpx2: a, + cpy2: r + } + } + + function bc(t, e, i) { + for (var n, o = [t], a = []; n = o.pop();) if (a.push(n), n.isExpand) { + var r = n.children; + if (r.length) for (var s = 0; s < r.length; s++) o.push(r[s]) + } + for (; n = a.pop();) e(n, i) + } + + function Sc(t, e) { + for (var i, n = [t]; i = n.pop();) if (e(i), i.isExpand) { + var o = i.children; + if (o.length) for (var a = o.length - 1; a >= 0; a--) n.push(o[a]) + } + } + + function Mc(t, e) { + var i = uc(t, e); + t.layoutInfo = i; + var n = t.get("layout"), o = 0, a = 0, r = null; + "radial" === n ? (o = 2 * Math.PI, a = Math.min(i.height, i.width) / 2, r = sc(function (t, e) { + return (t.parentNode === e.parentNode ? 1 : 2) / t.depth + })) : (o = i.width, a = i.height, r = sc()); + var s = t.getData().tree.root, l = s.children[0]; + if (l) { + oc(s), bc(l, ac, r), s.hierNode.modifier = -l.hierNode.prelim, Sc(l, rc); + var u = l, h = l, c = l; + Sc(l, function (t) { + var e = t.getLayout().x; + e < u.getLayout().x && (u = t), e > h.getLayout().x && (h = t), t.depth > c.depth && (c = t) + }); + var d = u === h ? 1 : r(u, h) / 2, f = d - u.getLayout().x, p = 0, g = 0, m = 0, v = 0; + if ("radial" === n) p = o / (h.getLayout().x + d + f), g = a / (c.depth - 1 || 1), Sc(l, function (t) { + m = (t.getLayout().x + f) * p, v = (t.depth - 1) * g; + var e = lc(m, v); + t.setLayout({x: e.x, y: e.y, rawX: m, rawY: v}, !0) + }); else { + var y = t.getOrient(); + "RL" === y || "LR" === y ? (g = a / (h.getLayout().x + d + f), p = o / (c.depth - 1 || 1), Sc(l, function (t) { + v = (t.getLayout().x + f) * g, m = "LR" === y ? (t.depth - 1) * p : o - (t.depth - 1) * p, t.setLayout({ + x: m, + y: v + }, !0) + })) : "TB" !== y && "BT" !== y || (p = o / (h.getLayout().x + d + f), g = a / (c.depth - 1 || 1), Sc(l, function (t) { + m = (t.getLayout().x + f) * p, v = "TB" === y ? (t.depth - 1) * g : a - (t.depth - 1) * g, t.setLayout({ + x: m, + y: v + }, !0) + })) + } + } + } + + function Ic(t, e, i) { + if (t && l(e, t.type) >= 0) { + var n = i.getData().tree.root, o = t.targetNode; + if (o && n.contains(o)) return {node: o}; + var a = t.targetNodeId; + if (null != a && (o = n.getNodeById(a))) return {node: o} + } + } + + function Tc(t) { + for (var e = []; t;) (t = t.parentNode) && e.push(t); + return e.reverse() + } + + function Dc(t, e) { + return l(Tc(t), e) >= 0 + } + + function Ac(t, e) { + for (var i = []; t;) { + var n = t.dataIndex; + i.push({name: t.name, dataIndex: n, value: e.getRawValue(n)}), t = t.parentNode + } + return i.reverse(), i + } + + function Cc(t) { + var e = 0; + d(t.children, function (t) { + Cc(t); + var i = t.value; + y(i) && (i = i[0]), e += i + }); + var i = t.value; + y(i) && (i = i[0]), (null == i || isNaN(i)) && (i = e), i < 0 && (i = 0), y(t.value) ? t.value[0] = i : t.value = i + } + + function Lc(t, e) { + var i = e.get("color"); + if (i) { + var n; + return d(t = t || [], function (t) { + var e = new wo(t), i = e.get("color"); + (e.get("itemStyle.color") || i && "none" !== i) && (n = !0) + }), n || ((t[0] || (t[0] = {})).color = i.slice()), t + } + } + + function kc(t) { + this.group = new sw, t.add(this.group) + } + + function Pc(t, e, i, n, o, a) { + var r = [[o ? t : t - IA, e], [t + i, e], [t + i, e + n], [o ? t : t - IA, e + n]]; + return !a && r.splice(2, 0, [t + i + IA, e + n / 2]), !o && r.push([t, e + n / 2]), r + } + + function Nc(t, e, i) { + t.eventData = { + componentType: "series", + componentSubType: "treemap", + seriesIndex: e.componentIndex, + seriesName: e.name, + seriesType: "treemap", + selfType: "breadcrumb", + nodeData: {dataIndex: i && i.dataIndex, name: i && i.name}, + treePathInfo: i && Ac(i, e) + } + } + + function Oc() { + var t, e = [], i = {}; + return { + add: function (t, n, o, a, r) { + return _(a) && (r = a, a = 0), !i[t.id] && (i[t.id] = 1, e.push({ + el: t, + target: n, + time: o, + delay: a, + easing: r + }), !0) + }, done: function (e) { + return t = e, this + }, start: function () { + for (var n = e.length, o = 0, a = e.length; o < a; o++) { + var r = e[o]; + r.el.animateTo(r.target, r.time, r.delay, r.easing, function () { + --n || (e.length = 0, i = {}, t && t()) + }) + } + return this + } + } + } + + function Ec(t, e, n, o, r, s, l, u, h, c) { + function d(t, e) { + w ? !t.invisible && s.push(t) : (e(), t.__tmWillVisible || (t.invisible = !1)) + } + + function f(e, n, o, a, r, s) { + var u = l.getModel(), + h = T(t.getFormattedLabel(l.dataIndex, "normal", null, null, s ? "upperLabel" : "label"), u.get("name")); + if (!s && v.isLeafRoot) { + var c = t.get("drillDownIcon", !0); + h = c ? c + " " + h : h + } + var d = u.getModel(s ? PA : LA), f = u.getModel(s ? NA : kA), p = d.getShallow("show"); + io(e, n, d, f, { + defaultText: p ? h : null, + autoColor: o, + isRectText: !0 + }), s && (e.textRect = i(s)), e.truncate = p && d.get("ellipsis") ? { + outerWidth: a, + outerHeight: r, + minChar: 2 + } : null + } + + function p(t, i, o, a) { + var s = null != S && n[t][S], l = r[t]; + return s ? (n[t][S] = null, g(l, s, t)) : w || ((s = new i({z: Rc(o, a)})).__tmDepth = o, s.__tmStorageName = t, m(l, s, t)), e[t][b] = s + } + + function g(t, e, i) { + (t[b] = {}).old = "nodeGroup" === i ? e.position.slice() : a({}, e.shape) + } + + function m(t, e, i) { + var n = t[b] = {}, a = l.parentNode; + if (a && (!o || "drillDown" === o.direction)) { + var s = 0, u = 0, h = r.background[a.getRawIndex()]; + !o && h && h.old && (s = h.old.width, u = h.old.height), n.old = "nodeGroup" === i ? [0, u] : { + x: s, + y: u, + width: 0, + height: 0 + } + } + n.fadein = "nodeGroup" !== i + } + + if (l) { + var v = l.getLayout(); + if (v && v.isInView) { + var y = v.width, x = v.height, _ = v.borderWidth, w = v.invisible, b = l.getRawIndex(), + S = u && u.getRawIndex(), M = l.viewChildren, I = v.upperHeight, D = M && M.length, + A = l.getModel("itemStyle"), C = l.getModel("emphasis.itemStyle"), L = p("nodeGroup", DA); + if (L) { + if (h.add(L), L.attr("position", [v.x || 0, v.y || 0]), L.__tmNodeWidth = y, L.__tmNodeHeight = x, v.isAboveViewRoot) return L; + var k = p("background", AA, c, EA); + if (k && function (e, i, n) { + i.dataIndex = l.dataIndex, i.seriesIndex = t.seriesIndex, i.setShape({ + x: 0, + y: 0, + width: y, + height: x + }); + var o = l.getVisual("borderColor", !0), a = C.get("borderColor"); + d(i, function () { + var t = BA(A); + t.fill = o; + var e = zA(C); + if (e.fill = a, n) { + var r = y - 2 * _; + f(t, e, o, r, I, {x: _, y: 0, width: r, height: I}) + } else t.text = e.text = null; + i.setStyle(t), eo(i, e) + }), e.add(i) + }(L, k, D && v.upperHeight), !D) { + var P = p("content", AA, c, RA); + P && function (e, i) { + i.dataIndex = l.dataIndex, i.seriesIndex = t.seriesIndex; + var n = Math.max(y - 2 * _, 0), o = Math.max(x - 2 * _, 0); + i.culling = !0, i.setShape({x: _, y: _, width: n, height: o}); + var a = l.getVisual("color", !0); + d(i, function () { + var t = BA(A); + t.fill = a; + var e = zA(C); + f(t, e, a, n, o), i.setStyle(t), eo(i, e) + }), e.add(i) + }(L, P) + } + return L + } + } + } + } + + function Rc(t, e) { + var i = t * OA + e; + return (i - 1) / i + } + + function zc(t) { + var e = t.pieceList; + t.hasSpecialVisual = !1, d(e, function (e, i) { + e.originIndex = i, null != e.visual && (t.hasSpecialVisual = !0) + }) + } + + function Bc(t) { + var e = t.categories, i = t.visual, n = t.categoryMap = {}; + if (WA(e, function (t, e) { + n[t] = e + }), !y(i)) { + var o = []; + w(i) ? WA(i, function (t, e) { + var i = n[e]; + o[null != i ? i : HA] = t + }) : o[HA] = i, i = jc(t, o) + } + for (var a = e.length - 1; a >= 0; a--) null == i[a] && (delete n[e[a]], e.pop()) + } + + function Vc(t, e) { + var i = t.visual, n = []; + w(i) ? WA(i, function (t) { + n.push(t) + }) : null != i && n.push(i); + var o = {color: 1, symbol: 1}; + e || 1 !== n.length || o.hasOwnProperty(t.type) || (n[1] = n[0]), jc(t, n) + } + + function Gc(t) { + return { + applyVisual: function (e, i, n) { + e = this.mapValueToVisual(e), n("color", t(i("color"), e)) + }, _doMap: Uc([0, 1]) + } + } + + function Wc(t) { + var e = this.option.visual; + return e[Math.round(To(t, [0, 1], [0, e.length - 1], !0))] || {} + } + + function Fc(t) { + return function (e, i, n) { + n(t, this.mapValueToVisual(e)) + } + } + + function Hc(t) { + var e = this.option.visual; + return e[this.option.loop && t !== HA ? t % e.length : t] + } + + function Zc() { + return this.option.visual[0] + } + + function Uc(t) { + return { + linear: function (e) { + return To(e, t, this.option.visual, !0) + }, category: Hc, piecewise: function (e, i) { + var n = Xc.call(this, i); + return null == n && (n = To(e, t, this.option.visual, !0)), n + }, fixed: Zc + } + } + + function Xc(t) { + var e = this.option, i = e.pieceList; + if (e.hasSpecialVisual) { + var n = i[ZA.findPieceIndex(t, i)]; + if (n && n.visual) return n.visual[this.type] + } + } + + function jc(t, e) { + return t.visual = e, "color" === t.type && (t.parsedVisual = f(e, function (t) { + return At(t) + })), e + } + + function Yc(t, e, i) { + return t ? e <= i : e < i + } + + function qc(t, e, i, n, o, a) { + var r = t.getModel(), s = t.getLayout(); + if (s && !s.invisible && s.isInView) { + var l, u = t.getModel(YA), h = Kc(u, e, i[t.depth], n), c = u.get("borderColor"), + f = u.get("borderColorSaturation"); + null != f && (c = Jc(f, l = $c(h))), t.setVisual("borderColor", c); + var p = t.viewChildren; + if (p && p.length) { + var g = td(t, r, s, u, h, p); + d(p, function (t, e) { + (t.depth >= o.length || t === o[t.depth]) && qc(t, id(r, h, t, e, g, a), i, n, o, a) + }) + } else l = $c(h), t.setVisual("color", l) + } + } + + function Kc(t, e, i, n) { + var o = a({}, e); + return d(["color", "colorAlpha", "colorSaturation"], function (a) { + var r = t.get(a, !0); + null == r && i && (r = i[a]), null == r && (r = e[a]), null == r && (r = n.get(a)), null != r && (o[a] = r) + }), o + } + + function $c(t) { + var e = Qc(t, "color"); + if (e) { + var i = Qc(t, "colorAlpha"), n = Qc(t, "colorSaturation"); + return n && (e = Et(e, null, null, n)), i && (e = Rt(e, i)), e + } + } + + function Jc(t, e) { + return null != e ? Et(e, null, null, t) : null + } + + function Qc(t, e) { + var i = t[e]; + if (null != i && "none" !== i) return i + } + + function td(t, e, i, n, o, a) { + if (a && a.length) { + var r = ed(e, "color") || null != o.color && "none" !== o.color && (ed(e, "colorAlpha") || ed(e, "colorSaturation")); + if (r) { + var s = e.get("visualMin"), l = e.get("visualMax"), u = i.dataExtent.slice(); + null != s && s < u[0] && (u[0] = s), null != l && l > u[1] && (u[1] = l); + var h = e.get("colorMappingBy"), c = {type: r.name, dataExtent: u, visual: r.range}; + "color" !== c.type || "index" !== h && "id" !== h ? c.mappingMethod = "linear" : (c.mappingMethod = "category", c.loop = !0); + var d = new ZA(c); + return d.__drColorMappingBy = h, d + } + } + } + + function ed(t, e) { + var i = t.get(e); + return jA(i) && i.length ? {name: e, range: i} : null + } + + function id(t, e, i, n, o, r) { + var s = a({}, e); + if (o) { + var l = o.type, u = "color" === l && o.__drColorMappingBy, + h = "index" === u ? n : "id" === u ? r.mapIdToIndex(i.getId()) : i.getValue(t.get("visualDimension")); + s[l] = o.mapValueToVisual(h) + } + return s + } + + function nd(t, e, i, n) { + var o, a; + if (!t.isRemoved()) { + var r = t.getLayout(); + o = r.width, a = r.height; + var s = (f = t.getModel()).get(tC), l = f.get(eC) / 2, u = fd(f), h = Math.max(s, u), c = s - l, d = h - l, + f = t.getModel(); + t.setLayout({borderWidth: s, upperHeight: h, upperLabelHeight: u}, !0); + var p = (o = KA(o - 2 * c, 0)) * (a = KA(a - c - d, 0)), g = od(t, f, p, e, i, n); + if (g.length) { + var m = {x: c, y: d, width: o, height: a}, v = $A(o, a), y = 1 / 0, x = []; + x.area = 0; + for (var _ = 0, w = g.length; _ < w;) { + var b = g[_]; + x.push(b), x.area += b.getLayout().area; + var S = ld(x, v, e.squareRatio); + S <= y ? (_++, y = S) : (x.area -= x.pop().getLayout().area, ud(x, v, m, l, !1), v = $A(m.width, m.height), x.length = x.area = 0, y = 1 / 0) + } + if (x.length && ud(x, v, m, l, !0), !i) { + var M = f.get("childrenVisibleMin"); + null != M && p < M && (i = !0) + } + for (var _ = 0, w = g.length; _ < w; _++) nd(g[_], e, i, n + 1) + } + } + } + + function od(t, e, i, n, o, a) { + var r = t.children || [], s = n.sort; + "asc" !== s && "desc" !== s && (s = null); + var l = null != n.leafDepth && n.leafDepth <= a; + if (o && !l) return t.viewChildren = []; + rd(r = g(r, function (t) { + return !t.isRemoved() + }), s); + var u = sd(e, r, s); + if (0 === u.sum) return t.viewChildren = []; + if (u.sum = ad(e, i, u.sum, s, r), 0 === u.sum) return t.viewChildren = []; + for (var h = 0, c = r.length; h < c; h++) { + var d = r[h].getValue() / u.sum * i; + r[h].setLayout({area: d}) + } + return l && (r.length && t.setLayout({isLeafRoot: !0}, !0), r.length = 0), t.viewChildren = r, t.setLayout({dataExtent: u.dataExtent}, !0), r + } + + function ad(t, e, i, n, o) { + if (!n) return i; + for (var a = t.get("visibleMin"), r = o.length, s = r, l = r - 1; l >= 0; l--) { + var u = o["asc" === n ? r - l - 1 : l].getValue(); + u / i * e < a && (s = l, i -= u) + } + return "asc" === n ? o.splice(0, r - s) : o.splice(s, r - s), i + } + + function rd(t, e) { + return e && t.sort(function (t, i) { + var n = "asc" === e ? t.getValue() - i.getValue() : i.getValue() - t.getValue(); + return 0 === n ? "asc" === e ? t.dataIndex - i.dataIndex : i.dataIndex - t.dataIndex : n + }), t + } + + function sd(t, e, i) { + for (var n = 0, o = 0, a = e.length; o < a; o++) n += e[o].getValue(); + var r = t.get("visualDimension"); + if (e && e.length) if ("value" === r && i) s = [e[e.length - 1].getValue(), e[0].getValue()], "asc" === i && s.reverse(); else { + var s = [1 / 0, -1 / 0]; + QA(e, function (t) { + var e = t.getValue(r); + e < s[0] && (s[0] = e), e > s[1] && (s[1] = e) + }) + } else s = [NaN, NaN]; + return {sum: n, dataExtent: s} + } + + function ld(t, e, i) { + for (var n, o = 0, a = 1 / 0, r = 0, s = t.length; r < s; r++) (n = t[r].getLayout().area) && (n < a && (a = n), n > o && (o = n)); + var l = t.area * t.area, u = e * e * i; + return l ? KA(u * o / l, l / (u * a)) : 1 / 0 + } + + function ud(t, e, i, n, o) { + var a = e === i.width ? 0 : 1, r = 1 - a, s = ["x", "y"], l = ["width", "height"], u = i[s[a]], + h = e ? t.area / e : 0; + (o || h > i[l[r]]) && (h = i[l[r]]); + for (var c = 0, d = t.length; c < d; c++) { + var f = t[c], p = {}, g = h ? f.getLayout().area / h : 0, m = p[l[r]] = KA(h - 2 * n, 0), + v = i[s[a]] + i[l[a]] - u, y = c === d - 1 || v < g ? v : g, x = p[l[a]] = KA(y - 2 * n, 0); + p[s[r]] = i[s[r]] + $A(n, m / 2), p[s[a]] = u + $A(n, x / 2), u += y, f.setLayout(p, !0) + } + i[s[r]] += h, i[l[r]] -= h + } + + function hd(t, e, i, n, o) { + var a = (e || {}).node, r = [n, o]; + if (!a || a === i) return r; + for (var s, l = n * o, u = l * t.option.zoomToNodeRatio; s = a.parentNode;) { + for (var h = 0, c = s.children, d = 0, f = c.length; d < f; d++) h += c[d].getValue(); + var p = a.getValue(); + if (0 === p) return r; + u *= h / p; + var g = s.getModel(), m = g.get(tC); + (u += 4 * m * m + (3 * m + Math.max(m, fd(g))) * Math.pow(u, .5)) > jS && (u = jS), a = s + } + u < l && (u = l); + var v = Math.pow(u / l, .5); + return [n * v, o * v] + } + + function cd(t, e, i) { + if (e) return {x: e.x, y: e.y}; + var n = {x: 0, y: 0}; + if (!i) return n; + var o = i.node, a = o.getLayout(); + if (!a) return n; + for (var r = [a.width / 2, a.height / 2], s = o; s;) { + var l = s.getLayout(); + r[0] += l.x, r[1] += l.y, s = s.parentNode + } + return {x: t.width / 2 - r[0], y: t.height / 2 - r[1]} + } + + function dd(t, e, i, n, o) { + var a = t.getLayout(), r = i[o], s = r && r === t; + if (!(r && !s || o === i.length && t !== n)) { + t.setLayout({isInView: !0, invisible: !s && !e.intersect(a), isAboveViewRoot: s}, !0); + var l = new $t(e.x - a.x, e.y - a.y, e.width, e.height); + QA(t.viewChildren || [], function (t) { + dd(t, l, i, n, o + 1) + }) + } + } + + function fd(t) { + return t.get(iC) ? t.get(nC) : 0 + } + + function pd(t) { + return "_EC_" + t + } + + function gd(t, e) { + this.id = null == t ? "" : t, this.inEdges = [], this.outEdges = [], this.edges = [], this.hostGraph, this.dataIndex = null == e ? -1 : e + } + + function md(t, e, i) { + this.node1 = t, this.node2 = e, this.dataIndex = null == i ? -1 : i + } + + function vd(t) { + return isNaN(+t.cpx1) || isNaN(+t.cpy1) + } + + function yd(t) { + return "_" + t + "Type" + } + + function xd(t, e, i) { + var n = e.getItemVisual(i, "color"), o = e.getItemVisual(i, t), a = e.getItemVisual(i, t + "Size"); + if (o && "none" !== o) { + y(a) || (a = [a, a]); + var r = Tl(o, -a[0] / 2, -a[1] / 2, a[0], a[1], n); + return r.name = t, r + } + } + + function _d(t) { + var e = new dC({name: "line"}); + return wd(e.shape, t), e + } + + function wd(t, e) { + var i = e[0], n = e[1], o = e[2]; + t.x1 = i[0], t.y1 = i[1], t.x2 = n[0], t.y2 = n[1], t.percent = 1, o ? (t.cpx1 = o[0], t.cpy1 = o[1]) : (t.cpx1 = NaN, t.cpy1 = NaN) + } + + function bd(t, e, i) { + sw.call(this), this._createLine(t, e, i) + } + + function Sd(t) { + this._ctor = t || bd, this.group = new sw + } + + function Md(t, e, i, n) { + if (Ad(e.getItemLayout(i))) { + var o = new t._ctor(e, i, n); + e.setItemGraphicEl(i, o), t.group.add(o) + } + } + + function Id(t, e, i, n, o, a) { + var r = e.getItemGraphicEl(n); + Ad(i.getItemLayout(o)) ? (r ? r.updateData(i, o, a) : r = new t._ctor(i, o, a), i.setItemGraphicEl(o, r), t.group.add(r)) : t.group.remove(r) + } + + function Td(t) { + var e = t.hostModel; + return { + lineStyle: e.getModel("lineStyle").getLineStyle(), + hoverLineStyle: e.getModel("emphasis.lineStyle").getLineStyle(), + labelModel: e.getModel("label"), + hoverLabelModel: e.getModel("emphasis.label") + } + } + + function Dd(t) { + return isNaN(t[0]) || isNaN(t[1]) + } + + function Ad(t) { + return !Dd(t[0]) && !Dd(t[1]) + } + + function Cd(t, e, i) { + for (var n, o = t[0], a = t[1], r = t[2], s = 1 / 0, l = i * i, u = .1, h = .1; h <= .9; h += .1) mC[0] = xC(o[0], a[0], r[0], h), mC[1] = xC(o[1], a[1], r[1], h), (f = wC(_C(mC, e) - l)) < s && (s = f, n = h); + for (var c = 0; c < 32; c++) { + var d = n + u; + vC[0] = xC(o[0], a[0], r[0], n), vC[1] = xC(o[1], a[1], r[1], n), yC[0] = xC(o[0], a[0], r[0], d), yC[1] = xC(o[1], a[1], r[1], d); + var f = _C(vC, e) - l; + if (wC(f) < .01) break; + var p = _C(yC, e) - l; + u /= 2, f < 0 ? p >= 0 ? n += u : n -= u : p >= 0 ? n -= u : n += u + } + return n + } + + function Ld(t, e) { + return t.getVisual("opacity") || t.getModel().get(e) + } + + function kd(t, e, i) { + var n = t.getGraphicEl(), o = Ld(t, e); + null != i && (null == o && (o = 1), o *= i), n.downplay && n.downplay(), n.traverse(function (t) { + "group" !== t.type && t.setStyle("opacity", o) + }) + } + + function Pd(t, e) { + var i = Ld(t, e), n = t.getGraphicEl(); + n.highlight && n.highlight(), n.traverse(function (t) { + "group" !== t.type && t.setStyle("opacity", i) + }) + } + + function Nd(t) { + return t instanceof Array || (t = [t, t]), t + } + + function Od(t) { + var e = t.coordinateSystem; + if (!e || "view" === e.type) { + var i = t.getGraph(); + i.eachNode(function (t) { + var e = t.getModel(); + t.setLayout([+e.get("x"), +e.get("y")]) + }), Ed(i) + } + } + + function Ed(t) { + t.eachEdge(function (t) { + var e = t.getModel().get("lineStyle.curveness") || 0, i = W(t.node1.getLayout()), + n = W(t.node2.getLayout()), o = [i, n]; + +e && o.push([(i[0] + n[0]) / 2 - (i[1] - n[1]) * e, (i[1] + n[1]) / 2 - (n[0] - i[0]) * e]), t.setLayout(o) + }) + } + + function Rd(t) { + var e = t.coordinateSystem; + if (!e || "view" === e.type) { + var i = e.getBoundingRect(), n = t.getData(), o = n.graph, a = 0, r = n.getSum("value"), + s = 2 * Math.PI / (r || n.count()), l = i.width / 2 + i.x, u = i.height / 2 + i.y, + h = Math.min(i.width, i.height) / 2; + o.eachNode(function (t) { + var e = t.getValue("value"); + a += s * (r ? e : 1) / 2, t.setLayout([h * Math.cos(a) + l, h * Math.sin(a) + u]), a += s * (r ? e : 1) / 2 + }), n.setLayout({cx: l, cy: u}), o.eachEdge(function (t) { + var e, i = t.getModel().get("lineStyle.curveness") || 0, n = W(t.node1.getLayout()), + o = W(t.node2.getLayout()), a = (n[0] + o[0]) / 2, r = (n[1] + o[1]) / 2; + +i && (e = [l * (i *= 3) + a * (1 - i), u * i + r * (1 - i)]), t.setLayout([n, o, e]) + }) + } + } + + function zd(t, e, i) { + for (var n = i.rect, o = n.width, a = n.height, r = [n.x + o / 2, n.y + a / 2], s = null == i.gravity ? .1 : i.gravity, l = 0; l < t.length; l++) { + var u = t[l]; + u.p || (u.p = V(o * (Math.random() - .5) + r[0], a * (Math.random() - .5) + r[1])), u.pp = W(u.p), u.edges = null + } + var h = .6; + return { + warmUp: function () { + h = .5 + }, setFixed: function (e) { + t[e].fixed = !0 + }, setUnfixed: function (e) { + t[e].fixed = !1 + }, step: function (i) { + for (var n = [], o = t.length, a = 0; a < e.length; a++) { + var l = e[a], u = l.n1; + U(n, (p = l.n2).p, u.p); + var c = X(n) - l.d, d = p.w / (u.w + p.w); + isNaN(d) && (d = 0), q(n, n), !u.fixed && IC(u.p, u.p, n, d * c * h), !p.fixed && IC(p.p, p.p, n, -(1 - d) * c * h) + } + for (a = 0; a < o; a++) (v = t[a]).fixed || (U(n, r, v.p), IC(v.p, v.p, n, s * h)); + for (a = 0; a < o; a++) for (var u = t[a], f = a + 1; f < o; f++) { + var p = t[f]; + U(n, p.p, u.p), 0 === (c = X(n)) && (F(n, Math.random() - .5, Math.random() - .5), c = 1); + var g = (u.rep + p.rep) / c / c; + !u.fixed && IC(u.pp, u.pp, n, g), !p.fixed && IC(p.pp, p.pp, n, -g) + } + for (var m = [], a = 0; a < o; a++) { + var v = t[a]; + v.fixed || (U(m, v.p, v.pp), IC(v.p, v.p, m, h), G(v.pp, v.p)) + } + h *= .992, i && i(t, e, h < .01) + } + } + } + + function Bd(t, e, i) { + var n = t.getBoxLayoutParams(); + return n.aspect = i, Qo(n, {width: e.getWidth(), height: e.getHeight()}) + } + + function Vd(t, e) { + var i = t.get("center"), n = e.getWidth(), o = e.getHeight(), a = Math.min(n, o); + return {cx: Do(i[0], e.getWidth()), cy: Do(i[1], e.getHeight()), r: Do(t.get("radius"), a / 2)} + } + + function Gd(t, e) { + return e && ("string" == typeof e ? t = e.replace("{value}", null != t ? t : "") : "function" == typeof e && (t = e(t))), t + } + + function Wd(t, e) { + function i() { + a.ignore = a.hoverIgnore, r.ignore = r.hoverIgnore + } + + function n() { + a.ignore = a.normalIgnore, r.ignore = r.normalIgnore + } + + sw.call(this); + var o = new wS, a = new bS, r = new fS; + this.add(o), this.add(a), this.add(r), this.updateData(t, e, !0), this.on("emphasis", i).on("normal", n).on("mouseover", i).on("mouseout", n) + } + + function Fd(t, e) { + return Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}) + } + + function Hd(t, e) { + for (var i = t.mapDimension("value"), n = t.mapArray(i, function (t) { + return t + }), o = [], a = "ascending" === e, r = 0, s = t.count(); r < s; r++) o[r] = r; + return "function" == typeof e ? o.sort(e) : "none" !== e && o.sort(function (t, e) { + return a ? n[t] - n[e] : n[e] - n[t] + }), o + } + + function Zd(t) { + t.each(function (e) { + var i, n, o, a, r = t.getItemModel(e), s = r.getModel("label").get("position"), l = r.getModel("labelLine"), + u = t.getItemLayout(e), h = u.points, c = "inner" === s || "inside" === s || "center" === s; + if (c) i = "center", a = [[n = (h[0][0] + h[1][0] + h[2][0] + h[3][0]) / 4, o = (h[0][1] + h[1][1] + h[2][1] + h[3][1]) / 4], [n, o]]; else { + var d, f, p, g = l.get("length"); + "left" === s ? (d = (h[3][0] + h[0][0]) / 2, f = (h[3][1] + h[0][1]) / 2, n = (p = d - g) - 5, i = "right") : (d = (h[1][0] + h[2][0]) / 2, f = (h[1][1] + h[2][1]) / 2, n = (p = d + g) + 5, i = "left"); + var m = f; + a = [[d, f], [p, m]], o = m + } + u.label = {linePoints: a, x: n, y: o, verticalAlign: "middle", textAlign: i, inside: c} + }) + } + + function Ud(t) { + if (!t.parallel) { + var e = !1; + d(t.series, function (t) { + t && "parallel" === t.type && (e = !0) + }), e && (t.parallel = [{}]) + } + } + + function Xd(t) { + d(Si(t.parallelAxis), function (e) { + if (w(e)) { + var i = e.parallelIndex || 0, o = Si(t.parallel)[i]; + o && o.parallelAxisDefault && n(e, o.parallelAxisDefault, !1) + } + }) + } + + function jd(t, e) { + var i = t[e] - t[1 - e]; + return {span: Math.abs(i), sign: i > 0 ? -1 : i < 0 ? 1 : e ? -1 : 1} + } + + function Yd(t, e) { + return Math.min(e[1], Math.max(e[0], t)) + } + + function qd(t, e, i) { + this._axesMap = R(), this._axesLayout = {}, this.dimensions = t.dimensions, this._rect, this._model = t, this._init(t, e, i) + } + + function Kd(t, e) { + return OC(EC(t, e[0]), e[1]) + } + + function $d(t, e) { + var i = e.layoutLength / (e.axisCount - 1); + return {position: i * t, axisNameAvailableWidth: i, axisLabelShow: !0} + } + + function Jd(t, e) { + var i, n, o = e.layoutLength, a = e.axisExpandWidth, r = e.axisCount, s = e.axisCollapseWidth, + l = e.winInnerIndices, u = s, h = !1; + return t < l[0] ? (i = t * s, n = s) : t <= l[1] ? (i = e.axisExpandWindow0Pos + t * a - e.axisExpandWindow[0], u = a, h = !0) : (i = o - (r - 1 - t) * s, n = s), { + position: i, + axisNameAvailableWidth: u, + axisLabelShow: h, + nameTruncateMaxWidth: n + } + } + + function Qd(t) { + D_.call(this), this._zr = t, this.group = new sw, this._brushType, this._brushOption, this._panels, this._track = [], this._dragging, this._covers = [], this._creatingCover, this._creatingPanel, this._enableGlobalPan, this._uid = "brushController_" + eL++, this._handlers = {}, HC(iL, function (t, e) { + this._handlers[e] = m(t, this) + }, this) + } + + function tf(t, e) { + var o = t._zr; + t._enableGlobalPan || Dh(o, $C, t._uid), HC(t._handlers, function (t, e) { + o.on(e, t) + }), t._brushType = e.brushType, t._brushOption = n(i(tL), e, !0) + } + + function ef(t) { + var e = t._zr; + Ah(e, $C, t._uid), HC(t._handlers, function (t, i) { + e.off(i, t) + }), t._brushType = t._brushOption = null + } + + function nf(t, e) { + var i = nL[e.brushType].createCover(t, e); + return i.__brushOption = e, rf(i, e), t.group.add(i), i + } + + function of(t, e) { + var i = lf(e); + return i.endCreating && (i.endCreating(t, e), rf(e, e.__brushOption)), e + } + + function af(t, e) { + var i = e.__brushOption; + lf(e).updateCoverShape(t, e, i.range, i) + } + + function rf(t, e) { + var i = e.z; + null == i && (i = YC), t.traverse(function (t) { + t.z = i, t.z2 = i + }) + } + + function sf(t, e) { + lf(e).updateCommon(t, e), af(t, e) + } + + function lf(t) { + return nL[t.__brushOption.brushType] + } + + function uf(t, e, i) { + var n = t._panels; + if (!n) return !0; + var o, a = t._transform; + return HC(n, function (t) { + t.isTargetByCursor(e, i, a) && (o = t) + }), o + } + + function hf(t, e) { + var i = t._panels; + if (!i) return !0; + var n = e.__brushOption.panelId; + return null == n || i[n] + } + + function cf(t) { + var e = t._covers, i = e.length; + return HC(e, function (e) { + t.group.remove(e) + }, t), e.length = 0, !!i + } + + function df(t, e) { + var n = ZC(t._covers, function (t) { + var e = t.__brushOption, n = i(e.range); + return {brushType: e.brushType, panelId: e.panelId, range: n} + }); + t.trigger("brush", n, {isEnd: !!e.isEnd, removeOnClick: !!e.removeOnClick}) + } + + function ff(t) { + var e = t._track; + if (!e.length) return !1; + var i = e[e.length - 1], n = e[0], o = i[0] - n[0], a = i[1] - n[1]; + return jC(o * o + a * a, .5) > qC + } + + function pf(t) { + var e = t.length - 1; + return e < 0 && (e = 0), [t[0], t[e]] + } + + function gf(t, e, i, n) { + var o = new sw; + return o.add(new SS({ + name: "main", + style: xf(i), + silent: !0, + draggable: !0, + cursor: "move", + drift: FC(t, e, o, "nswe"), + ondragend: FC(df, e, {isEnd: !0}) + })), HC(n, function (i) { + o.add(new SS({ + name: i, + style: {opacity: 0}, + draggable: !0, + silent: !0, + invisible: !0, + drift: FC(t, e, o, i), + ondragend: FC(df, e, {isEnd: !0}) + })) + }), o + } + + function mf(t, e, i, n) { + var o = n.brushStyle.lineWidth || 0, a = XC(o, KC), r = i[0][0], s = i[1][0], l = r - o / 2, u = s - o / 2, + h = i[0][1], c = i[1][1], d = h - a + o / 2, f = c - a + o / 2, p = h - r, g = c - s, m = p + o, v = g + o; + yf(t, e, "main", r, s, p, g), n.transformable && (yf(t, e, "w", l, u, a, v), yf(t, e, "e", d, u, a, v), yf(t, e, "n", l, u, m, a), yf(t, e, "s", l, f, m, a), yf(t, e, "nw", l, u, a, a), yf(t, e, "ne", d, u, a, a), yf(t, e, "sw", l, f, a, a), yf(t, e, "se", d, f, a, a)) + } + + function vf(t, e) { + var i = e.__brushOption, n = i.transformable, o = e.childAt(0); + o.useStyle(xf(i)), o.attr({ + silent: !n, + cursor: n ? "move" : "default" + }), HC(["w", "e", "n", "s", "se", "sw", "ne", "nw"], function (i) { + var o = e.childOfName(i), a = bf(t, i); + o && o.attr({silent: !n, invisible: !n, cursor: n ? QC[a] + "-resize" : null}) + }) + } + + function yf(t, e, i, n, o, a, r) { + var s = e.childOfName(i); + s && s.setShape(Df(Tf(t, e, [[n, o], [n + a, o + r]]))) + } + + function xf(t) { + return r({strokeNoScale: !0}, t.brushStyle) + } + + function _f(t, e, i, n) { + var o = [UC(t, i), UC(e, n)], a = [XC(t, i), XC(e, n)]; + return [[o[0], a[0]], [o[1], a[1]]] + } + + function wf(t) { + return go(t.group) + } + + function bf(t, e) { + if (e.length > 1) return ("e" === (n = [bf(t, (e = e.split(""))[0]), bf(t, e[1])])[0] || "w" === n[0]) && n.reverse(), n.join(""); + var i = {left: "w", right: "e", top: "n", bottom: "s"}, + n = vo({w: "left", e: "right", n: "top", s: "bottom"}[e], wf(t)); + return i[n] + } + + function Sf(t, e, i, n, o, a, r, s) { + var l = n.__brushOption, u = t(l.range), h = If(i, a, r); + HC(o.split(""), function (t) { + var e = JC[t]; + u[e[0]][e[1]] += h[e[0]] + }), l.range = e(_f(u[0][0], u[1][0], u[0][1], u[1][1])), sf(i, n), df(i, {isEnd: !1}) + } + + function Mf(t, e, i, n, o) { + var a = e.__brushOption.range, r = If(t, i, n); + HC(a, function (t) { + t[0] += r[0], t[1] += r[1] + }), sf(t, e), df(t, {isEnd: !1}) + } + + function If(t, e, i) { + var n = t.group, o = n.transformCoordToLocal(e, i), a = n.transformCoordToLocal(0, 0); + return [o[0] - a[0], o[1] - a[1]] + } + + function Tf(t, e, n) { + var o = hf(t, e); + return o && !0 !== o ? o.clipPath(n, t._transform) : i(n) + } + + function Df(t) { + var e = UC(t[0][0], t[1][0]), i = UC(t[0][1], t[1][1]); + return {x: e, y: i, width: XC(t[0][0], t[1][0]) - e, height: XC(t[0][1], t[1][1]) - i} + } + + function Af(t, e, i) { + if (t._brushType) { + var n = t._zr, o = t._covers, a = uf(t, e, i); + if (!t._dragging) for (var r = 0; r < o.length; r++) { + var s = o[r].__brushOption; + if (a && (!0 === a || s.panelId === a.panelId) && nL[s.brushType].contain(o[r], i[0], i[1])) return + } + a && n.setCursorStyle("crosshair") + } + } + + function Cf(t) { + var e = t.event; + e.preventDefault && e.preventDefault() + } + + function Lf(t, e, i) { + return t.childOfName("main").contain(e, i) + } + + function kf(t, e, n, o) { + var a, r = t._creatingCover, s = t._creatingPanel, l = t._brushOption; + if (t._track.push(n.slice()), ff(t) || r) { + if (s && !r) { + "single" === l.brushMode && cf(t); + var u = i(l); + u.brushType = Pf(u.brushType, s), u.panelId = !0 === s ? null : s.panelId, r = t._creatingCover = nf(t, u), t._covers.push(r) + } + if (r) { + var h = nL[Pf(t._brushType, s)]; + r.__brushOption.range = h.getCreatingRange(Tf(t, r, t._track)), o && (of(t, r), h.updateCommon(t, r)), af(t, r), a = {isEnd: o} + } + } else o && "single" === l.brushMode && l.removeOnClick && uf(t, e, n) && cf(t) && (a = { + isEnd: o, + removeOnClick: !0 + }); + return a + } + + function Pf(t, e) { + return "auto" === t ? e.defaultBrushType : t + } + + function Nf(t) { + if (this._dragging) { + Cf(t); + var e = kf(this, t, this.group.transformCoordToLocal(t.offsetX, t.offsetY), !0); + this._dragging = !1, this._track = [], this._creatingCover = null, e && df(this, e) + } + } + + function Of(t) { + return { + createCover: function (e, i) { + return gf(FC(Sf, function (e) { + var i = [e, [0, 100]]; + return t && i.reverse(), i + }, function (e) { + return e[t] + }), e, i, [["w", "e"], ["n", "s"]][t]) + }, getCreatingRange: function (e) { + var i = pf(e); + return [UC(i[0][t], i[1][t]), XC(i[0][t], i[1][t])] + }, updateCoverShape: function (e, i, n, o) { + var a, r = hf(e, i); + if (!0 !== r && r.getLinearBrushOtherExtent) a = r.getLinearBrushOtherExtent(t, e._transform); else { + var s = e._zr; + a = [0, [s.getWidth(), s.getHeight()][1 - t]] + } + var l = [n, a]; + t && l.reverse(), mf(e, i, l, o) + }, updateCommon: vf, contain: Lf + } + } + + function Ef(t) { + return t = Bf(t), function (e, i) { + return xo(e, t) + } + } + + function Rf(t, e) { + return t = Bf(t), function (i) { + var n = null != e ? e : i, o = n ? t.width : t.height, a = n ? t.x : t.y; + return [a, a + (o || 0)] + } + } + + function zf(t, e, i) { + return t = Bf(t), function (n, o, a) { + return t.contain(o[0], o[1]) && !Wh(n, e, i) + } + } + + function Bf(t) { + return $t.create(t) + } + + function Vf(t, e, i) { + return i && "axisAreaSelect" === i.type && e.findComponents({mainType: "parallelAxis", query: i})[0] === t + } + + function Gf(t) { + var e = t.axis; + return f(t.activeIntervals, function (t) { + return {brushType: "lineX", panelId: "pl", range: [e.dataToCoord(t[0], !0), e.dataToCoord(t[1], !0)]} + }) + } + + function Wf(t, e) { + return e.getComponent("parallel", t.get("parallelIndex")) + } + + function Ff(t, e) { + var i = t._model; + return i.get("axisExpandable") && i.get("axisExpandTriggerOn") === e + } + + function Hf(t, e) { + if (!t.encodeDefine) { + var i = e.ecModel.getComponent("parallel", e.get("parallelIndex")); + if (i) { + var n = t.encodeDefine = R(); + d(i.dimensions, function (t) { + var e = Zf(t); + n.set(t, e) + }) + } + } + } + + function Zf(t) { + return +t.replace("dim", "") + } + + function Uf(t, e, i) { + var n = t.model, o = t.getRect(), a = new SS({shape: {x: o.x, y: o.y, width: o.width, height: o.height}}), + r = "horizontal" === n.get("layout") ? "width" : "height"; + return a.setShape(r, 0), po(a, {shape: {width: o.width, height: o.height}}, e, i), a + } + + function Xf(t, e, i, n) { + for (var o = [], a = 0; a < i.length; a++) { + var r = i[a], s = t.get(t.mapDimension(r), e); + Kf(s, n.getAxis(r).type) || o.push(n.dataToPoint(s, r)) + } + return o + } + + function jf(t, e, i, n, o) { + var a = Xf(t, i, n, o), r = new bS({shape: {points: a}, silent: !0, z2: 10}); + return e.add(r), t.setItemGraphicEl(i, r), r + } + + function Yf(t) { + var e = t.get("smooth", !0); + return !0 === e && (e = sL), {lineStyle: t.getModel("lineStyle").getLineStyle(), smooth: null != e ? e : sL} + } + + function qf(t, e, i, n) { + var o = n.lineStyle; + e.hasItemOption && (o = e.getItemModel(i).getModel("lineStyle").getLineStyle()), t.useStyle(o); + var a = t.style; + a.fill = null, a.stroke = e.getItemVisual(i, "color"), a.opacity = e.getItemVisual(i, "opacity"), n.smooth && (t.shape.smooth = n.smooth) + } + + function Kf(t, e) { + return "category" === e ? null == t : null == t || isNaN(t) + } + + function $f(t, e, i) { + var n = new SS({shape: {x: t.x - 10, y: t.y - 10, width: 0, height: t.height + 20}}); + return po(n, {shape: {width: t.width + 20, height: t.height + 20}}, e, i), n + } + + function Jf() { + function t(e, n) { + if (n >= i.length) return e; + for (var o = -1, a = e.length, r = i[n++], s = {}, l = {}; ++o < a;) { + var u = r(e[o]), h = l[u]; + h ? h.push(e[o]) : l[u] = [e[o]] + } + return d(l, function (e, i) { + s[i] = t(e, n) + }), s + } + + function e(t, o) { + if (o >= i.length) return t; + var a = [], r = n[o++]; + return d(t, function (t, i) { + a.push({key: i, values: e(t, o)}) + }), r ? a.sort(function (t, e) { + return r(t.key, e.key) + }) : a + } + + var i = [], n = []; + return { + key: function (t) { + return i.push(t), this + }, sortKeys: function (t) { + return n[i.length - 1] = t, this + }, entries: function (i) { + return e(t(i, 0), 0) + } + } + } + + function Qf(t, e) { + return Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}) + } + + function tp(t, e, i, n, o, a, r) { + ip(t, e, i, o), ap(t, e, a, n, r), dp(t) + } + + function ep(t) { + d(t, function (t) { + var e = gp(t.outEdges, xp), i = gp(t.inEdges, xp), n = Math.max(e, i); + t.setLayout({value: n}, !0) + }) + } + + function ip(t, e, i, n) { + for (var o = [], a = [], r = [], s = [], l = 0, u = 0; u < e.length; u++) o[u] = 1; + for (u = 0; u < t.length; u++) a[u] = t[u].inEdges.length, 0 === a[u] && r.push(t[u]); + for (; r.length;) d(r, function (n) { + n.setLayout({x: l}, !0), n.setLayout({dx: i}, !0), d(n.outEdges, function (i) { + var n = e.indexOf(i); + o[n] = 0; + var r = i.node2, l = t.indexOf(r); + 0 == --a[l] && s.push(r) + }) + }), ++l, r = s, s = []; + for (u = 0; u < o.length; u++) ; + np(t, l), op(t, (n - i) / (l - 1)) + } + + function np(t, e) { + d(t, function (t) { + t.outEdges.length || t.setLayout({x: e - 1}, !0) + }) + } + + function op(t, e) { + d(t, function (t) { + var i = t.getLayout().x * e; + t.setLayout({x: i}, !0) + }) + } + + function ap(t, e, i, n, o) { + var a = Jf().key(function (t) { + return t.getLayout().x + }).sortKeys(yp).entries(t).map(function (t) { + return t.values + }); + rp(t, a, e, i, n), sp(a, n, i); + for (var r = 1; o > 0; o--) lp(a, r *= .99), sp(a, n, i), hp(a, r), sp(a, n, i) + } + + function rp(t, e, i, n, o) { + var a = []; + d(e, function (t) { + var e = t.length, i = 0; + d(t, function (t) { + i += t.getLayout().value + }); + var r = (n - (e - 1) * o) / i; + a.push(r) + }), a.sort(function (t, e) { + return t - e + }); + var r = a[0]; + d(e, function (t) { + d(t, function (t, e) { + t.setLayout({y: e}, !0); + var i = t.getLayout().value * r; + t.setLayout({dy: i}, !0) + }) + }), d(i, function (t) { + var e = +t.getValue() * r; + t.setLayout({dy: e}, !0) + }) + } + + function sp(t, e, i) { + d(t, function (t) { + var n, o, a, r = 0, s = t.length; + for (t.sort(vp), a = 0; a < s; a++) { + if (n = t[a], (o = r - n.getLayout().y) > 0) { + l = n.getLayout().y + o; + n.setLayout({y: l}, !0) + } + r = n.getLayout().y + n.getLayout().dy + e + } + if ((o = r - e - i) > 0) { + var l = n.getLayout().y - o; + for (n.setLayout({y: l}, !0), r = n.getLayout().y, a = s - 2; a >= 0; --a) (o = (n = t[a]).getLayout().y + n.getLayout().dy + e - r) > 0 && (l = n.getLayout().y - o, n.setLayout({y: l}, !0)), r = n.getLayout().y + } + }) + } + + function lp(t, e) { + d(t.slice().reverse(), function (t) { + d(t, function (t) { + if (t.outEdges.length) { + var i = gp(t.outEdges, up) / gp(t.outEdges, xp), n = t.getLayout().y + (i - mp(t)) * e; + t.setLayout({y: n}, !0) + } + }) + }) + } + + function up(t) { + return mp(t.node2) * t.getValue() + } + + function hp(t, e) { + d(t, function (t) { + d(t, function (t) { + if (t.inEdges.length) { + var i = gp(t.inEdges, cp) / gp(t.inEdges, xp), n = t.getLayout().y + (i - mp(t)) * e; + t.setLayout({y: n}, !0) + } + }) + }) + } + + function cp(t) { + return mp(t.node1) * t.getValue() + } + + function dp(t) { + d(t, function (t) { + t.outEdges.sort(fp), t.inEdges.sort(pp) + }), d(t, function (t) { + var e = 0, i = 0; + d(t.outEdges, function (t) { + t.setLayout({sy: e}, !0), e += t.getLayout().dy + }), d(t.inEdges, function (t) { + t.setLayout({ty: i}, !0), i += t.getLayout().dy + }) + }) + } + + function fp(t, e) { + return t.node2.getLayout().y - e.node2.getLayout().y + } + + function pp(t, e) { + return t.node1.getLayout().y - e.node1.getLayout().y + } + + function gp(t, e) { + for (var i = 0, n = t.length, o = -1; ++o < n;) { + var a = +e.call(t, t[o], o); + isNaN(a) || (i += a) + } + return i + } + + function mp(t) { + return t.getLayout().y + t.getLayout().dy / 2 + } + + function vp(t, e) { + return t.getLayout().y - e.getLayout().y + } + + function yp(t, e) { + return t - e + } + + function xp(t) { + return t.getValue() + } + + function _p(t, e, i, n, o) { + var a = t.ends, r = new pL({shape: {points: o ? bp(a, n, t) : a}}); + return wp(t, r, e, i, o), r + } + + function wp(t, e, i, n, o) { + var a = i.hostModel; + (0, BS[o ? "initProps" : "updateProps"])(e, {shape: {points: t.ends}}, a, n); + var r = i.getItemModel(n), s = r.getModel(dL), l = i.getItemVisual(n, "color"), + u = s.getItemStyle(["borderColor"]); + u.stroke = l, u.strokeNoScale = !0, e.useStyle(u), e.z2 = 100, eo(e, r.getModel(fL).getItemStyle()) + } + + function bp(t, e, i) { + return f(t, function (t) { + return t = t.slice(), t[e] = i.initBaseline, t + }) + } + + function Sp(t) { + var e = [], i = []; + return t.eachSeriesByType("boxplot", function (t) { + var n = t.getBaseAxis(), o = l(i, n); + o < 0 && (o = i.length, i[o] = n, e[o] = {axis: n, seriesModels: []}), e[o].seriesModels.push(t) + }), e + } + + function Mp(t) { + var e, i, n = t.axis, o = t.seriesModels, a = o.length, r = t.boxWidthList = [], s = t.boxOffsetList = [], + l = []; + if ("category" === n.type) i = n.getBandWidth(); else { + var u = 0; + mL(o, function (t) { + u = Math.max(u, t.getData().count()) + }), e = n.getExtent(), Math.abs(e[1] - e[0]) + } + mL(o, function (t) { + var e = t.get("boxWidth"); + y(e) || (e = [e, e]), l.push([Do(e[0], i) || 0, Do(e[1], i) || 0]) + }); + var h = .8 * i - 2, c = h / a * .3, d = (h - c * (a - 1)) / a, f = d / 2 - h / 2; + mL(o, function (t, e) { + s.push(f), f += c + d, r.push(Math.min(Math.max(d, l[e][0]), l[e][1])) + }) + } + + function Ip(t, e, i) { + function n(t, i, n) { + var o = s.get(i, n), a = []; + a[u] = t, a[h] = o; + var l; + return isNaN(t) || isNaN(o) ? l = [NaN, NaN] : (l = r.dataToPoint(a))[u] += e, l + } + + function o(t, e, i) { + var n = e.slice(), o = e.slice(); + n[u] += l, o[u] -= l, i ? t.push(n, o) : t.push(o, n) + } + + function a(t, e) { + var i = e.slice(), n = e.slice(); + i[u] -= l, n[u] += l, t.push(i, n) + } + + var r = t.coordinateSystem, s = t.getData(), l = i / 2, u = "horizontal" === t.get("layout") ? 0 : 1, h = 1 - u, + c = ["x", "y"], d = s.mapDimension(c[u]), f = s.mapDimension(c[h], !0); + if (!(null == d || f.length < 5)) for (var p = 0; p < s.count(); p++) { + var g = s.get(d, p), m = n(g, f[2], p), v = n(g, f[0], p), y = n(g, f[1], p), x = n(g, f[3], p), + _ = n(g, f[4], p), w = []; + o(w, y, 0), o(w, x, 1), w.push(v, y, _, x), a(w, v), a(w, _), a(w, m), s.setItemLayout(p, { + initBaseline: m[h], + ends: w + }) + } + } + + function Tp(t, e, i) { + var n = t.ends; + return new _L({shape: {points: i ? Ap(n, t) : n}, z2: 100}) + } + + function Dp(t, e, i, n) { + var o = e.getItemModel(i), a = o.getModel(vL), r = e.getItemVisual(i, "color"), + s = e.getItemVisual(i, "borderColor") || r, l = a.getItemStyle(xL); + t.useStyle(l), t.style.strokeNoScale = !0, t.style.fill = r, t.style.stroke = s, t.__simpleBox = n, eo(t, o.getModel(yL).getItemStyle()) + } + + function Ap(t, e) { + return f(t, function (t) { + return t = t.slice(), t[1] = e.initBaseline, t + }) + } + + function Cp(t, e, i) { + var n = t.getData(), o = n.getLayout("largePoints"), a = new wL({shape: {points: o}, __sign: 1}); + e.add(a); + var r = new wL({shape: {points: o}, __sign: -1}); + e.add(r), Lp(1, a, t, n), Lp(-1, r, t, n), i && (a.incremental = !0, r.incremental = !0) + } + + function Lp(t, e, i, n) { + var o = t > 0 ? "P" : "N", a = n.getVisual("borderColor" + o) || n.getVisual("color" + o), + r = i.getModel(vL).getItemStyle(xL); + e.useStyle(r), e.style.fill = null, e.style.stroke = a + } + + function kp(t, e, i, n, o) { + return i > n ? -1 : i < n ? 1 : e > 0 ? t.get(o, e - 1) <= n ? 1 : -1 : 1 + } + + function Pp(t, e) { + var i, n = t.getBaseAxis(), + o = "category" === n.type ? n.getBandWidth() : (i = n.getExtent(), Math.abs(i[1] - i[0]) / e.count()), + a = Do(D(t.get("barMaxWidth"), o), o), r = Do(D(t.get("barMinWidth"), 1), o), s = t.get("barWidth"); + return null != s ? Do(s, o) : Math.max(Math.min(o / 2, a), r) + } + + function Np(t) { + return y(t) || (t = [+t, +t]), t + } + + function Op(t, e) { + t.eachChild(function (t) { + t.attr({ + z: e.z, + zlevel: e.zlevel, + style: { + stroke: "stroke" === e.brushType ? e.color : null, + fill: "fill" === e.brushType ? e.color : null + } + }) + }) + } + + function Ep(t, e) { + sw.call(this); + var i = new Kl(t, e), n = new sw; + this.add(i), this.add(n), n.beforeUpdate = function () { + this.attr(i.getScale()) + }, this.updateData(t, e) + } + + function Rp(t) { + var e = t.data; + e && e[0] && e[0][0] && e[0][0].coord && (t.data = f(e, function (t) { + var e = {coords: [t[0].coord, t[1].coord]}; + return t[0].name && (e.fromName = t[0].name), t[1].name && (e.toName = t[1].name), o([e, t[0], t[1]]) + })) + } + + function zp(t, e, i) { + sw.call(this), this.add(this.createLine(t, e, i)), this._updateEffectSymbol(t, e) + } + + function Bp(t, e, i) { + sw.call(this), this._createPolyline(t, e, i) + } + + function Vp(t, e, i) { + zp.call(this, t, e, i), this._lastFrame = 0, this._lastFramePercent = 0 + } + + function Gp() { + this.group = new sw + } + + function Wp(t) { + return t instanceof Array || (t = [t, t]), t + } + + function Fp() { + var t = m_(); + this.canvas = t, this.blurSize = 30, this.pointSize = 20, this.maxOpacity = 1, this.minOpacity = 0, this._gradientPixels = {} + } + + function Hp(t, e, i) { + var n = t[1] - t[0], o = (e = f(e, function (e) { + return {interval: [(e.interval[0] - t[0]) / n, (e.interval[1] - t[0]) / n]} + })).length, a = 0; + return function (t) { + for (n = a; n < o; n++) if ((r = e[n].interval)[0] <= t && t <= r[1]) { + a = n; + break + } + if (n === o) for (var n = a - 1; n >= 0; n--) { + var r = e[n].interval; + if (r[0] <= t && t <= r[1]) { + a = n; + break + } + } + return n >= 0 && n < o && i[n] + } + } + + function Zp(t, e) { + var i = t[1] - t[0]; + return e = [(e[0] - t[0]) / i, (e[1] - t[0]) / i], function (t) { + return t >= e[0] && t <= e[1] + } + } + + function Up(t) { + var e = t.dimensions; + return "lng" === e[0] && "lat" === e[1] + } + + function Xp(t, e, i, n) { + var o = t.getItemLayout(e), a = i.get("symbolRepeat"), r = i.get("symbolClip"), + s = i.get("symbolPosition") || "start", l = (i.get("symbolRotate") || 0) * Math.PI / 180 || 0, + u = i.get("symbolPatternSize") || 2, h = i.isAnimationEnabled(), c = { + dataIndex: e, + layout: o, + itemModel: i, + symbolType: t.getItemVisual(e, "symbol") || "circle", + color: t.getItemVisual(e, "color"), + symbolClip: r, + symbolRepeat: a, + symbolRepeatDirection: i.get("symbolRepeatDirection"), + symbolPatternSize: u, + rotation: l, + animationModel: h ? i : null, + hoverAnimation: h && i.get("hoverAnimation"), + z2: i.getShallow("z", !0) || 0 + }; + jp(i, a, o, n, c), qp(t, e, o, a, r, c.boundingLength, c.pxSign, u, n, c), Kp(i, c.symbolScale, l, n, c); + var d = c.symbolSize, f = i.get("symbolOffset"); + return y(f) && (f = [Do(f[0], d[0]), Do(f[1], d[1])]), $p(i, d, o, a, r, f, s, c.valueLineWidth, c.boundingLength, c.repeatCutLength, n, c), c + } + + function jp(t, e, i, n, o) { + var a, r = n.valueDim, s = t.get("symbolBoundingData"), l = n.coordSys.getOtherAxis(n.coordSys.getBaseAxis()), + u = l.toGlobalCoord(l.dataToCoord(0)), h = 1 - +(i[r.wh] <= 0); + if (y(s)) { + var c = [Yp(l, s[0]) - u, Yp(l, s[1]) - u]; + c[1] < c[0] && c.reverse(), a = c[h] + } else a = null != s ? Yp(l, s) - u : e ? n.coordSysExtent[r.index][h] - u : i[r.wh]; + o.boundingLength = a, e && (o.repeatCutLength = i[r.wh]), o.pxSign = a > 0 ? 1 : a < 0 ? -1 : 0 + } + + function Yp(t, e) { + return t.toGlobalCoord(t.dataToCoord(t.scale.parse(e))) + } + + function qp(t, e, i, n, o, a, r, s, l, u) { + var h = l.valueDim, c = l.categoryDim, d = Math.abs(i[c.wh]), f = t.getItemVisual(e, "symbolSize"); + y(f) ? f = f.slice() : (null == f && (f = "100%"), f = [f, f]), f[c.index] = Do(f[c.index], d), f[h.index] = Do(f[h.index], n ? d : Math.abs(a)), u.symbolSize = f, (u.symbolScale = [f[0] / s, f[1] / s])[h.index] *= (l.isHorizontal ? -1 : 1) * r + } + + function Kp(t, e, i, n, o) { + var a = t.get(FL) || 0; + a && (ZL.attr({ + scale: e.slice(), + rotation: i + }), ZL.updateTransform(), a /= ZL.getLineScale(), a *= e[n.valueDim.index]), o.valueLineWidth = a + } + + function $p(t, e, i, n, o, r, s, l, u, h, c, d) { + var f = c.categoryDim, p = c.valueDim, g = d.pxSign, m = Math.max(e[p.index] + l, 0), v = m; + if (n) { + var y = Math.abs(u), x = T(t.get("symbolMargin"), "15%") + "", _ = !1; + x.lastIndexOf("!") === x.length - 1 && (_ = !0, x = x.slice(0, x.length - 1)), x = Do(x, e[p.index]); + var w = Math.max(m + 2 * x, 0), b = _ ? 0 : 2 * x, S = Wo(n), M = S ? n : pg((y + b) / w); + w = m + 2 * (x = (y - M * m) / 2 / (_ ? M : M - 1)), b = _ ? 0 : 2 * x, S || "fixed" === n || (M = h ? pg((Math.abs(h) + b) / w) : 0), v = M * w - b, d.repeatTimes = M, d.symbolMargin = x + } + var I = g * (v / 2), D = d.pathPosition = []; + D[f.index] = i[f.wh] / 2, D[p.index] = "start" === s ? I : "end" === s ? u - I : u / 2, r && (D[0] += r[0], D[1] += r[1]); + var A = d.bundlePosition = []; + A[f.index] = i[f.xy], A[p.index] = i[p.xy]; + var C = d.barRectShape = a({}, i); + C[p.wh] = g * Math.max(Math.abs(i[p.wh]), Math.abs(D[p.index] + I)), C[f.wh] = i[f.wh]; + var L = d.clipShape = {}; + L[f.xy] = -i[f.xy], L[f.wh] = c.ecSize[f.wh], L[p.xy] = 0, L[p.wh] = i[p.wh] + } + + function Jp(t) { + var e = t.symbolPatternSize, i = Tl(t.symbolType, -e / 2, -e / 2, e, e, t.color); + return i.attr({culling: !0}), "image" !== i.type && i.setStyle({strokeNoScale: !0}), i + } + + function Qp(t, e, i, n) { + function o(t) { + var e = l.slice(), n = i.pxSign, o = t; + return ("start" === i.symbolRepeatDirection ? n > 0 : n < 0) && (o = h - 1 - t), e[u.index] = d * (o - h / 2 + .5) + l[u.index], { + position: e, + scale: i.symbolScale.slice(), + rotation: i.rotation + } + } + + var a = t.__pictorialBundle, r = i.symbolSize, s = i.valueLineWidth, l = i.pathPosition, u = e.valueDim, + h = i.repeatTimes || 0, c = 0, d = r[e.valueDim.index] + s + 2 * i.symbolMargin; + for (cg(t, function (t) { + t.__pictorialAnimationIndex = c, t.__pictorialRepeatTimes = h, c < h ? dg(t, null, o(c), i, n) : dg(t, null, {scale: [0, 0]}, i, n, function () { + a.remove(t) + }), rg(t, i), c++ + }); c < h; c++) { + var f = Jp(i); + f.__pictorialAnimationIndex = c, f.__pictorialRepeatTimes = h, a.add(f); + var p = o(c); + dg(f, {position: p.position, scale: [0, 0]}, { + scale: p.scale, + rotation: p.rotation + }, i, n), f.on("mouseover", function () { + cg(t, function (t) { + t.trigger("emphasis") + }) + }).on("mouseout", function () { + cg(t, function (t) { + t.trigger("normal") + }) + }), rg(f, i) + } + } + + function tg(t, e, i, n) { + var o = t.__pictorialBundle, a = t.__pictorialMainPath; + a ? dg(a, null, { + position: i.pathPosition.slice(), + scale: i.symbolScale.slice(), + rotation: i.rotation + }, i, n) : (a = t.__pictorialMainPath = Jp(i), o.add(a), dg(a, { + position: i.pathPosition.slice(), + scale: [0, 0], + rotation: i.rotation + }, {scale: i.symbolScale.slice()}, i, n), a.on("mouseover", function () { + this.trigger("emphasis") + }).on("mouseout", function () { + this.trigger("normal") + })), rg(a, i) + } + + function eg(t, e, i) { + var n = a({}, e.barRectShape), o = t.__pictorialBarRect; + o ? dg(o, null, {shape: n}, e, i) : (o = t.__pictorialBarRect = new SS({ + z2: 2, + shape: n, + silent: !0, + style: {stroke: "transparent", fill: "transparent", lineWidth: 0} + }), t.add(o)) + } + + function ig(t, e, i, n) { + if (i.symbolClip) { + var o = t.__pictorialClipPath, r = a({}, i.clipShape), s = e.valueDim, l = i.animationModel, + u = i.dataIndex; + if (o) fo(o, {shape: r}, l, u); else { + r[s.wh] = 0, o = new SS({shape: r}), t.__pictorialBundle.setClipPath(o), t.__pictorialClipPath = o; + var h = {}; + h[s.wh] = i.clipShape[s.wh], BS[n ? "updateProps" : "initProps"](o, {shape: h}, l, u) + } + } + } + + function ng(t, e) { + var i = t.getItemModel(e); + return i.getAnimationDelayParams = og, i.isAnimationEnabled = ag, i + } + + function og(t) { + return {index: t.__pictorialAnimationIndex, count: t.__pictorialRepeatTimes} + } + + function ag() { + return this.parentModel.isAnimationEnabled() && !!this.getShallow("animation") + } + + function rg(t, e) { + t.off("emphasis").off("normal"); + var i = e.symbolScale.slice(); + e.hoverAnimation && t.on("emphasis", function () { + this.animateTo({scale: [1.1 * i[0], 1.1 * i[1]]}, 400, "elasticOut") + }).on("normal", function () { + this.animateTo({scale: i.slice()}, 400, "elasticOut") + }) + } + + function sg(t, e, i, n) { + var o = new sw, a = new sw; + return o.add(a), o.__pictorialBundle = a, a.attr("position", i.bundlePosition.slice()), i.symbolRepeat ? Qp(o, e, i) : tg(o, e, i), eg(o, i, n), ig(o, e, i, n), o.__pictorialShapeStr = hg(t, i), o.__pictorialSymbolMeta = i, o + } + + function lg(t, e, i) { + var n = i.animationModel, o = i.dataIndex; + fo(t.__pictorialBundle, {position: i.bundlePosition.slice()}, n, o), i.symbolRepeat ? Qp(t, e, i, !0) : tg(t, e, i, !0), eg(t, i, !0), ig(t, e, i, !0) + } + + function ug(t, e, i, n) { + var o = n.__pictorialBarRect; + o && (o.style.text = null); + var a = []; + cg(n, function (t) { + a.push(t) + }), n.__pictorialMainPath && a.push(n.__pictorialMainPath), n.__pictorialClipPath && (i = null), d(a, function (t) { + fo(t, {scale: [0, 0]}, i, e, function () { + n.parent && n.parent.remove(n) + }) + }), t.setItemGraphicEl(e, null) + } + + function hg(t, e) { + return [t.getItemVisual(e.dataIndex, "symbol") || "none", !!e.symbolRepeat, !!e.symbolClip].join(":") + } + + function cg(t, e, i) { + d(t.__pictorialBundle.children(), function (n) { + n !== t.__pictorialBarRect && e.call(i, n) + }) + } + + function dg(t, e, i, n, o, a) { + e && t.attr(e), n.symbolClip && !o ? i && t.attr(i) : i && BS[o ? "updateProps" : "initProps"](t, i, n.animationModel, n.dataIndex, a) + } + + function fg(t, e, i) { + var n = i.color, o = i.dataIndex, a = i.itemModel, s = a.getModel("itemStyle").getItemStyle(["color"]), + l = a.getModel("emphasis.itemStyle").getItemStyle(), u = a.getShallow("cursor"); + cg(t, function (t) { + t.setColor(n), t.setStyle(r({fill: n, opacity: i.opacity}, s)), eo(t, l), u && (t.cursor = u), t.z2 = i.z2 + }); + var h = {}, c = e.valueDim.posDesc[+(i.boundingLength > 0)], d = t.__pictorialBarRect; + ih(d.style, h, a, n, e.seriesModel, o, c), eo(d, h) + } + + function pg(t) { + var e = Math.round(t); + return Math.abs(t - e) < 1e-4 ? e : Math.ceil(t) + } + + function gg(t, e, i) { + this.dimension = "single", this.dimensions = ["single"], this._axis = null, this._rect, this._init(t, e, i), this.model = t + } + + function mg(t, e) { + e = e || {}; + var i = t.coordinateSystem, n = t.axis, o = {}, a = n.position, r = n.orient, s = i.getRect(), + l = [s.x, s.x + s.width, s.y, s.y + s.height], + u = {horizontal: {top: l[2], bottom: l[3]}, vertical: {left: l[0], right: l[1]}}; + o.position = ["vertical" === r ? u.vertical[a] : l[0], "horizontal" === r ? u.horizontal[a] : l[3]]; + var h = {horizontal: 0, vertical: 1}; + o.rotation = Math.PI / 2 * h[r]; + var c = {top: -1, bottom: 1, right: 1, left: -1}; + o.labelDirection = o.tickDirection = o.nameDirection = c[a], t.get("axisTick.inside") && (o.tickDirection = -o.tickDirection), T(e.labelInside, t.get("axisLabel.inside")) && (o.labelDirection = -o.labelDirection); + var d = e.rotate; + return null == d && (d = t.get("axisLabel.rotate")), o.labelRotation = "top" === a ? -d : d, o.z2 = 1, o + } + + function vg(t, e, i, n, o) { + var r = t.axis; + if (!r.scale.isBlank() && r.containData(e)) if (t.involveSeries) { + var s = yg(e, t), l = s.payloadBatch, u = s.snapToValue; + l[0] && null == o.seriesIndex && a(o, l[0]), !n && t.snap && r.containData(u) && null != u && (e = u), i.showPointer(t, e, l, o), i.showTooltip(t, s, u) + } else i.showPointer(t, e) + } + + function yg(t, e) { + var i = e.axis, n = i.dim, o = t, a = [], r = Number.MAX_VALUE, s = -1; + return $L(e.seriesModels, function (e, l) { + var u, h, c = e.getData().mapDimension(n, !0); + if (e.getAxisTooltipData) { + var d = e.getAxisTooltipData(c, t, i); + h = d.dataIndices, u = d.nestestValue + } else { + if (!(h = e.getData().indicesOfNearest(c[0], t, "category" === i.type ? .5 : null)).length) return; + u = e.getData().get(c[0], h[0]) + } + if (null != u && isFinite(u)) { + var f = t - u, p = Math.abs(f); + p <= r && ((p < r || f >= 0 && s < 0) && (r = p, s = f, o = u, a.length = 0), $L(h, function (t) { + a.push({seriesIndex: e.seriesIndex, dataIndexInside: t, dataIndex: e.getData().getRawIndex(t)}) + })) + } + }), {payloadBatch: a, snapToValue: o} + } + + function xg(t, e, i, n) { + t[e.key] = {value: i, payloadBatch: n} + } + + function _g(t, e, i, n) { + var o = i.payloadBatch, a = e.axis, r = a.model, s = e.axisPointerModel; + if (e.triggerTooltip && o.length) { + var l = e.coordSys.model, u = Ju(l), h = t.map[u]; + h || (h = t.map[u] = { + coordSysId: l.id, + coordSysIndex: l.componentIndex, + coordSysType: l.type, + coordSysMainType: l.mainType, + dataByAxis: [] + }, t.list.push(h)), h.dataByAxis.push({ + axisDim: a.dim, + axisIndex: r.componentIndex, + axisType: r.type, + axisId: r.id, + value: n, + valueLabelOpt: {precision: s.get("label.precision"), formatter: s.get("label.formatter")}, + seriesDataIndices: o.slice() + }) + } + } + + function wg(t, e, i) { + var n = i.axesInfo = []; + $L(e, function (e, i) { + var o = e.axisPointerModel.option, a = t[i]; + a ? (!e.useHandle && (o.status = "show"), o.value = a.value, o.seriesDataIndices = (a.payloadBatch || []).slice()) : !e.useHandle && (o.status = "hide"), "show" === o.status && n.push({ + axisDim: e.axis.dim, + axisIndex: e.axis.model.componentIndex, + value: o.value + }) + }) + } + + function bg(t, e, i, n) { + if (!Tg(e) && t.list.length) { + var o = ((t.list[0].dataByAxis[0] || {}).seriesDataIndices || [])[0] || {}; + n({ + type: "showTip", + escapeConnect: !0, + x: e[0], + y: e[1], + tooltipOption: i.tooltipOption, + position: i.position, + dataIndexInside: o.dataIndexInside, + dataIndex: o.dataIndex, + seriesIndex: o.seriesIndex, + dataByCoordSys: t.list + }) + } else n({type: "hideTip"}) + } + + function Sg(t, e, i) { + var n = i.getZr(), o = QL(n).axisPointerLastHighlights || {}, a = QL(n).axisPointerLastHighlights = {}; + $L(t, function (t, e) { + var i = t.axisPointerModel.option; + "show" === i.status && $L(i.seriesDataIndices, function (t) { + var e = t.seriesIndex + " | " + t.dataIndex; + a[e] = t + }) + }); + var r = [], s = []; + d(o, function (t, e) { + !a[e] && s.push(t) + }), d(a, function (t, e) { + !o[e] && r.push(t) + }), s.length && i.dispatchAction({ + type: "downplay", + escapeConnect: !0, + batch: s + }), r.length && i.dispatchAction({type: "highlight", escapeConnect: !0, batch: r}) + } + + function Mg(t, e) { + for (var i = 0; i < (t || []).length; i++) { + var n = t[i]; + if (e.axis.dim === n.axisDim && e.axis.model.componentIndex === n.axisIndex) return n + } + } + + function Ig(t) { + var e = t.axis.model, i = {}, n = i.axisDim = t.axis.dim; + return i.axisIndex = i[n + "AxisIndex"] = e.componentIndex, i.axisName = i[n + "AxisName"] = e.name, i.axisId = i[n + "AxisId"] = e.id, i + } + + function Tg(t) { + return !t || null == t[0] || isNaN(t[0]) || null == t[1] || isNaN(t[1]) + } + + function Dg(t, e, i) { + if (!a_.node) { + var n = e.getZr(); + tk(n).records || (tk(n).records = {}), Ag(n, e), (tk(n).records[t] || (tk(n).records[t] = {})).handler = i + } + } + + function Ag(t, e) { + function i(i, n) { + t.on(i, function (i) { + var o = Pg(e); + ek(tk(t).records, function (t) { + t && n(t, i, o.dispatchAction) + }), Cg(o.pendings, e) + }) + } + + tk(t).initialized || (tk(t).initialized = !0, i("click", v(kg, "click")), i("mousemove", v(kg, "mousemove")), i("globalout", Lg)) + } + + function Cg(t, e) { + var i, n = t.showTip.length, o = t.hideTip.length; + n ? i = t.showTip[n - 1] : o && (i = t.hideTip[o - 1]), i && (i.dispatchAction = null, e.dispatchAction(i)) + } + + function Lg(t, e, i) { + t.handler("leave", null, i) + } + + function kg(t, e, i, n) { + e.handler(t, i, n) + } + + function Pg(t) { + var e = {showTip: [], hideTip: []}, i = function (n) { + var o = e[n.type]; + o ? o.push(n) : (n.dispatchAction = i, t.dispatchAction(n)) + }; + return {dispatchAction: i, pendings: e} + } + + function Ng(t, e) { + if (!a_.node) { + var i = e.getZr(); + (tk(i).records || {})[t] && (tk(i).records[t] = null) + } + } + + function Og() { + } + + function Eg(t, e, i, n) { + Rg(nk(i).lastProp, n) || (nk(i).lastProp = n, e ? fo(i, n, t) : (i.stopAnimation(), i.attr(n))) + } + + function Rg(t, e) { + if (w(t) && w(e)) { + var i = !0; + return d(e, function (e, n) { + i = i && Rg(t[n], e) + }), !!i + } + return t === e + } + + function zg(t, e) { + t[e.get("label.show") ? "show" : "hide"]() + } + + function Bg(t) { + return {position: t.position.slice(), rotation: t.rotation || 0} + } + + function Vg(t, e, i) { + var n = e.get("z"), o = e.get("zlevel"); + t && t.traverse(function (t) { + "group" !== t.type && (null != n && (t.z = n), null != o && (t.zlevel = o), t.silent = i) + }) + } + + function Gg(t) { + var e, i = t.get("type"), n = t.getModel(i + "Style"); + return "line" === i ? (e = n.getLineStyle()).fill = null : "shadow" === i && ((e = n.getAreaStyle()).stroke = null), e + } + + function Wg(t, e, i, n, o) { + var a = Hg(i.get("value"), e.axis, e.ecModel, i.get("seriesDataIndices"), { + precision: i.get("label.precision"), + formatter: i.get("label.formatter") + }), r = i.getModel("label"), s = KS(r.get("padding") || 0), l = r.getFont(), u = me(a, l), h = o.position, + c = u.width + s[1] + s[3], d = u.height + s[0] + s[2], f = o.align; + "right" === f && (h[0] -= c), "center" === f && (h[0] -= c / 2); + var p = o.verticalAlign; + "bottom" === p && (h[1] -= d), "middle" === p && (h[1] -= d / 2), Fg(h, c, d, n); + var g = r.get("backgroundColor"); + g && "auto" !== g || (g = e.get("axisLine.lineStyle.color")), t.label = { + shape: { + x: 0, + y: 0, + width: c, + height: d, + r: r.get("borderRadius") + }, + position: h.slice(), + style: { + text: a, + textFont: l, + textFill: r.getTextColor(), + textPosition: "inside", + fill: g, + stroke: r.get("borderColor") || "transparent", + lineWidth: r.get("borderWidth") || 0, + shadowBlur: r.get("shadowBlur"), + shadowColor: r.get("shadowColor"), + shadowOffsetX: r.get("shadowOffsetX"), + shadowOffsetY: r.get("shadowOffsetY") + }, + z2: 10 + } + } + + function Fg(t, e, i, n) { + var o = n.getWidth(), a = n.getHeight(); + t[0] = Math.min(t[0] + e, o) - e, t[1] = Math.min(t[1] + i, a) - i, t[0] = Math.max(t[0], 0), t[1] = Math.max(t[1], 0) + } + + function Hg(t, e, i, n, o) { + t = e.scale.parse(t); + var a = e.scale.getLabel(t, {precision: o.precision}), r = o.formatter; + if (r) { + var s = {value: bl(e, t), seriesData: []}; + d(n, function (t) { + var e = i.getSeriesByIndex(t.seriesIndex), n = t.dataIndexInside, o = e && e.getDataParams(n); + o && s.seriesData.push(o) + }), _(r) ? a = r.replace("{value}", a) : x(r) && (a = r(s)) + } + return a + } + + function Zg(t, e, i) { + var n = st(); + return dt(n, n, i.rotation), ct(n, n, i.position), mo([t.dataToCoord(e), (i.labelOffset || 0) + (i.labelDirection || 1) * (i.labelMargin || 0)], n) + } + + function Ug(t, e, i, n, o, a) { + var r = LD.innerTextLayout(i.rotation, 0, i.labelDirection); + i.labelMargin = o.get("label.margin"), Wg(e, n, o, a, { + position: Zg(n.axis, t, i), + align: r.textAlign, + verticalAlign: r.textVerticalAlign + }) + } + + function Xg(t, e, i) { + return i = i || 0, {x1: t[i], y1: t[1 - i], x2: e[i], y2: e[1 - i]} + } + + function jg(t, e, i) { + return i = i || 0, {x: t[i], y: t[1 - i], width: e[i], height: e[1 - i]} + } + + function Yg(t, e, i, n, o, a) { + return {cx: t, cy: e, r0: i, r: n, startAngle: o, endAngle: a, clockwise: !0} + } + + function qg(t, e) { + var i = {}; + return i[e.dim + "AxisIndex"] = e.index, t.getCartesian(i) + } + + function Kg(t) { + return "x" === t.dim ? 0 : 1 + } + + function $g(t) { + return t.isHorizontal() ? 0 : 1 + } + + function Jg(t, e) { + var i = t.getRect(); + return [i[lk[e]], i[lk[e]] + i[uk[e]]] + } + + function Qg(t, e, i) { + var n = new SS({shape: {x: t.x - 10, y: t.y - 10, width: 0, height: t.height + 20}}); + return po(n, {shape: {width: t.width + 20, height: t.height + 20}}, e, i), n + } + + function tm(t, e, i) { + if (t.count()) for (var n, o = e.coordinateSystem, a = e.getLayerSeries(), r = t.mapDimension("single"), s = t.mapDimension("value"), l = f(a, function (e) { + return f(e.indices, function (e) { + var i = o.dataToPoint(t.get(r, e)); + return i[1] = t.get(s, e), i + }) + }), u = em(l), h = u.y0, c = i / u.max, d = a.length, p = a[0].indices.length, g = 0; g < p; ++g) { + n = h[g] * c, t.setItemLayout(a[0].indices[g], {layerIndex: 0, x: l[0][g][0], y0: n, y: l[0][g][1] * c}); + for (var m = 1; m < d; ++m) n += l[m - 1][g][1] * c, t.setItemLayout(a[m].indices[g], { + layerIndex: m, + x: l[m][g][0], + y0: n, + y: l[m][g][1] * c + }) + } + } + + function em(t) { + for (var e = t.length, i = t[0].length, n = [], o = [], a = 0, r = {}, s = 0; s < i; ++s) { + for (var l = 0, u = 0; l < e; ++l) u += t[l][s][1]; + u > a && (a = u), n.push(u) + } + for (var h = 0; h < i; ++h) o[h] = (a - n[h]) / 2; + a = 0; + for (var c = 0; c < i; ++c) { + var d = n[c] + o[c]; + d > a && (a = d) + } + return r.y0 = o, r.max = a, r + } + + function im(t) { + var e = 0; + d(t.children, function (t) { + im(t); + var i = t.value; + y(i) && (i = i[0]), e += i + }); + var i = t.value; + y(i) && (i = i[0]), (null == i || isNaN(i)) && (i = e), i < 0 && (i = 0), y(t.value) ? t.value[0] = i : t.value = i + } + + function nm(t, e, i) { + function n() { + r.ignore = r.hoverIgnore + } + + function o() { + r.ignore = r.normalIgnore + } + + sw.call(this); + var a = new vS({z2: pk}); + a.seriesIndex = e.seriesIndex; + var r = new fS({z2: gk, silent: t.getModel("label").get("silent")}); + this.add(a), this.add(r), this.updateData(!0, t, "normal", e, i), this.on("emphasis", n).on("normal", o).on("mouseover", n).on("mouseout", o) + } + + function om(t, e, i) { + var n = t.getVisual("color"), o = t.getVisual("visualMeta"); + o && 0 !== o.length || (n = null); + var a = t.getModel("itemStyle").get("color"); + if (a) return a; + if (n) return n; + if (0 === t.depth) return i.option.color[0]; + var r = i.option.color.length; + return a = i.option.color[am(t) % r] + } + + function am(t) { + for (var e = t; e.depth > 1;) e = e.parentNode; + return l(t.getAncestors()[0].children, e) + } + + function rm(t, e, i) { + return i !== fk.NONE && (i === fk.SELF ? t === e : i === fk.ANCESTOR ? t === e || t.isAncestorOf(e) : t === e || t.isDescendantOf(e)) + } + + function sm(t, e) { + var i = t.children || []; + t.children = lm(i, e), i.length && d(t.children, function (t) { + sm(t, e) + }) + } + + function lm(t, e) { + if ("function" == typeof e) return t.sort(e); + var i = "asc" === e; + return t.sort(function (t, e) { + var n = (t.getValue() - e.getValue()) * (i ? 1 : -1); + return 0 === n ? (t.dataIndex - e.dataIndex) * (i ? -1 : 1) : n + }) + } + + function um(t, e) { + return e = e || [0, 0], f(["x", "y"], function (i, n) { + var o = this.getAxis(i), a = e[n], r = t[n] / 2; + return "category" === o.type ? o.getBandWidth() : Math.abs(o.dataToCoord(a - r) - o.dataToCoord(a + r)) + }, this) + } + + function hm(t, e) { + return e = e || [0, 0], f([0, 1], function (i) { + var n = e[i], o = t[i] / 2, a = [], r = []; + return a[i] = n - o, r[i] = n + o, a[1 - i] = r[1 - i] = e[1 - i], Math.abs(this.dataToPoint(a)[i] - this.dataToPoint(r)[i]) + }, this) + } + + function cm(t, e) { + var i = this.getAxis(), n = e instanceof Array ? e[0] : e, o = (t instanceof Array ? t[0] : t) / 2; + return "category" === i.type ? i.getBandWidth() : Math.abs(i.dataToCoord(n - o) - i.dataToCoord(n + o)) + } + + function dm(t, e) { + return f(["Radius", "Angle"], function (i, n) { + var o = this["get" + i + "Axis"](), a = e[n], r = t[n] / 2, s = "dataTo" + i, + l = "category" === o.type ? o.getBandWidth() : Math.abs(o[s](a - r) - o[s](a + r)); + return "Angle" === i && (l = l * Math.PI / 180), l + }, this) + } + + function fm(t) { + var e, i = t.type; + if ("path" === i) { + var n = t.shape; + (e = Rn(n.pathData, null, { + x: n.x || 0, + y: n.y || 0, + width: n.width || 0, + height: n.height || 0 + }, "center")).__customPathData = t.pathData + } else "image" === i ? (e = new Je({})).__customImagePath = t.style.image : "text" === i ? (e = new fS({})).__customText = t.style.text : e = new (0, BS[i.charAt(0).toUpperCase() + i.slice(1)]); + return e.__customGraphicType = i, e.name = t.name, e + } + + function pm(t, e, n, o, a, r) { + var s = {}, l = n.style || {}; + if (n.shape && (s.shape = i(n.shape)), n.position && (s.position = n.position.slice()), n.scale && (s.scale = n.scale.slice()), n.origin && (s.origin = n.origin.slice()), n.rotation && (s.rotation = n.rotation), "image" === t.type && n.style) { + u = s.style = {}; + d(["x", "y", "width", "height"], function (e) { + gm(e, u, l, t.style, r) + }) + } + if ("text" === t.type && n.style) { + var u = s.style = {}; + d(["x", "y"], function (e) { + gm(e, u, l, t.style, r) + }), !l.hasOwnProperty("textFill") && l.fill && (l.textFill = l.fill), !l.hasOwnProperty("textStroke") && l.stroke && (l.textStroke = l.stroke) + } + if ("group" !== t.type && (t.useStyle(l), r)) { + t.style.opacity = 0; + var h = l.opacity; + null == h && (h = 1), po(t, {style: {opacity: h}}, o, e) + } + r ? t.attr(s) : fo(t, s, o, e), t.attr({ + z2: n.z2 || 0, + silent: n.silent + }), !1 !== n.styleEmphasis && eo(t, n.styleEmphasis) + } + + function gm(t, e, i, n, o) { + null == i[t] || o || (e[t] = i[t], i[t] = n[t]) + } + + function mm(t, e, i, n) { + function o(t) { + null == t && (t = h), v && (c = e.getItemModel(t), d = c.getModel(bk), f = c.getModel(Sk), p = e.getItemVisual(t, "color"), v = !1) + } + + var s = t.get("renderItem"), l = t.coordinateSystem, u = {}; + l && (u = l.prepareCustoms ? l.prepareCustoms() : Ik[l.type](l)); + var h, c, d, f, p, g = r({ + getWidth: n.getWidth, + getHeight: n.getHeight, + getZr: n.getZr, + getDevicePixelRatio: n.getDevicePixelRatio, + value: function (t, i) { + return null == i && (i = h), e.get(e.getDimension(t || 0), i) + }, + style: function (i, n) { + null == n && (n = h), o(n); + var r = c.getModel(_k).getItemStyle(); + null != p && (r.fill = p); + var s = e.getItemVisual(n, "opacity"); + return null != s && (r.opacity = s), no(r, d, null, { + autoColor: p, + isRectText: !0 + }), r.text = d.getShallow("show") ? D(t.getFormattedLabel(n, "normal"), ql(e, n)) : null, i && a(r, i), r + }, + styleEmphasis: function (i, n) { + null == n && (n = h), o(n); + var r = c.getModel(wk).getItemStyle(); + return no(r, f, null, {isRectText: !0}, !0), r.text = f.getShallow("show") ? A(t.getFormattedLabel(n, "emphasis"), t.getFormattedLabel(n, "normal"), ql(e, n)) : null, i && a(r, i), r + }, + visual: function (t, i) { + return null == i && (i = h), e.getItemVisual(i, t) + }, + barLayout: function (t) { + if (l.getBaseAxis) return rl(r({axis: l.getBaseAxis()}, t), n) + }, + currentSeriesIndices: function () { + return i.getCurrentSeriesIndices() + }, + font: function (t) { + return ho(t, i) + } + }, u.api || {}), m = { + context: {}, + seriesId: t.id, + seriesName: t.name, + seriesIndex: t.seriesIndex, + coordSys: u.coordSys, + dataInsideLength: e.count(), + encode: vm(t.getData()) + }, v = !0; + return function (t) { + return h = t, v = !0, s && s(r({dataIndexInside: t, dataIndex: e.getRawIndex(t)}, m), g) || {} + } + } + + function vm(t) { + var e = {}; + return d(t.dimensions, function (i, n) { + var o = t.getDimensionInfo(i); + if (!o.isExtraCoord) { + var a = o.coordDim; + (e[a] = e[a] || [])[o.coordDimIndex] = n + } + }), e + } + + function ym(t, e, i, n, o, a) { + return (t = xm(t, e, i, n, o, a)) && a.setItemGraphicEl(e, t), t + } + + function xm(t, e, i, n, o, a) { + var r = i.type; + if (!t || r === t.__customGraphicType || "path" === r && i.pathData === t.__customPathData || "image" === r && i.style.image === t.__customImagePath || "text" === r && i.style.text === t.__customText || (o.remove(t), t = null), null != r) { + var s = !t; + if (!t && (t = fm(i)), pm(t, e, i, n, a, s), "group" === r) { + var l = t.children() || [], u = i.children || []; + if (i.diffChildrenByName) _m({ + oldChildren: l, + newChildren: u, + dataIndex: e, + animatableModel: n, + group: t, + data: a + }); else { + for (var h = 0; h < u.length; h++) xm(t.childAt(h), e, u[h], n, t, a); + for (; h < l.length; h++) l[h] && t.remove(l[h]) + } + } + return o.add(t), t + } + } + + function _m(t) { + new Ss(t.oldChildren, t.newChildren, wm, wm, t).add(bm).update(bm).remove(Sm).execute() + } + + function wm(t, e) { + var i = t && t.name; + return null != i ? i : Mk + e + } + + function bm(t, e) { + var i = this.context, n = null != t ? i.newChildren[t] : null; + xm(null != e ? i.oldChildren[e] : null, i.dataIndex, n, i.animatableModel, i.group, i.data) + } + + function Sm(t) { + var e = this.context, i = e.oldChildren[t]; + i && e.group.remove(i) + } + + function Mm(t, e, i, n) { + var o = i.type, a = new (0, BS[o.charAt(0).toUpperCase() + o.slice(1)])(i); + e.add(a), n.set(t, a), a.__ecGraphicId = t + } + + function Im(t, e) { + var i = t && t.parent; + i && ("group" === t.type && t.traverse(function (t) { + Im(t, e) + }), e.removeKey(t.__ecGraphicId), i.remove(t)) + } + + function Tm(t) { + return t = a({}, t), d(["id", "parentId", "$action", "hv", "bounding"].concat(aM), function (e) { + delete t[e] + }), t + } + + function Dm(t, e) { + var i; + return d(e, function (e) { + null != t[e] && "auto" !== t[e] && (i = !0) + }), i + } + + function Am(t, e) { + var i = t.exist; + if (e.id = t.keyInfo.id, !e.type && i && (e.type = i.type), null == e.parentId) { + var n = e.parentOption; + n ? e.parentId = n.id : i && (e.parentId = i.parentId) + } + e.parentOption = null + } + + function Cm(t, e, i) { + var o = a({}, i), r = t[e], s = i.$action || "merge"; + "merge" === s ? r ? (n(r, o, !0), ia(r, o, {ignoreSize: !0}), oa(i, r)) : t[e] = o : "replace" === s ? t[e] = o : "remove" === s && r && (t[e] = null) + } + + function Lm(t, e) { + t && (t.hv = e.hv = [Dm(e, ["left", "right"]), Dm(e, ["top", "bottom"])], "group" === t.type && (null == t.width && (t.width = e.width = 0), null == t.height && (t.height = e.height = 0))) + } + + function km(t, e, i) { + var n, o = {}, a = "toggleSelected" === t; + return i.eachComponent("legend", function (i) { + a && null != n ? i[n ? "select" : "unSelect"](e.name) : (i[t](e.name), n = i.isSelected(e.name)), d(i.getData(), function (t) { + var e = t.get("name"); + if ("\n" !== e && "" !== e) { + var n = i.isSelected(e); + o.hasOwnProperty(e) ? o[e] = o[e] && n : o[e] = n + } + }) + }), {name: e.name, selected: o} + } + + function Pm(t, e, i) { + var n = e.getBoxLayoutParams(), o = e.get("padding"), a = {width: i.getWidth(), height: i.getHeight()}, + r = Qo(n, a, o); + sM(e.get("orient"), t, e.get("itemGap"), r.width, r.height), ta(t, n, a, o) + } + + function Nm(t, e) { + var i = KS(e.get("padding")), n = e.getItemStyle(["color", "opacity"]); + return n.fill = e.get("backgroundColor"), t = new SS({ + shape: { + x: t.x - i[3], + y: t.y - i[0], + width: t.width + i[1] + i[3], + height: t.height + i[0] + i[2], + r: e.get("borderRadius") + }, style: n, silent: !0, z2: -1 + }) + } + + function Om(t, e) { + e.dispatchAction({type: "legendToggleSelect", name: t}) + } + + function Em(t, e, i, n) { + var o = i.getZr().storage.getDisplayList()[0]; + o && o.useHoverLayer || i.dispatchAction({type: "highlight", seriesName: t.name, name: e, excludeSeriesId: n}) + } + + function Rm(t, e, i, n) { + var o = i.getZr().storage.getDisplayList()[0]; + o && o.useHoverLayer || i.dispatchAction({type: "downplay", seriesName: t.name, name: e, excludeSeriesId: n}) + } + + function zm(t, e, i) { + var n = [1, 1]; + n[t.getOrient().index] = 0, ia(e, i, {type: "box", ignoreSize: n}) + } + + function Bm(t) { + var e = "left " + t + "s cubic-bezier(0.23, 1, 0.32, 1),top " + t + "s cubic-bezier(0.23, 1, 0.32, 1)"; + return f(Vk, function (t) { + return t + "transition:" + e + }).join(";") + } + + function Vm(t) { + var e = [], i = t.get("fontSize"), n = t.getTextColor(); + return n && e.push("color:" + n), e.push("font:" + t.getFont()), i && e.push("line-height:" + Math.round(3 * i / 2) + "px"), zk(["decoration", "align"], function (i) { + var n = t.get(i); + n && e.push("text-" + i + ":" + n) + }), e.join(";") + } + + function Gm(t) { + var e = [], i = t.get("transitionDuration"), n = t.get("backgroundColor"), o = t.getModel("textStyle"), + a = t.get("padding"); + return i && e.push(Bm(i)), n && (a_.canvasSupported ? e.push("background-Color:" + n) : (e.push("background-Color:#" + Pt(n)), e.push("filter:alpha(opacity=70)"))), zk(["width", "color", "radius"], function (i) { + var n = "border-" + i, o = Bk(n), a = t.get(o); + null != a && e.push(n + ":" + a + ("color" === i ? "" : "px")) + }), e.push(Vm(o)), null != a && e.push("padding:" + KS(a).join("px ") + "px"), e.join(";") + ";" + } + + function Wm(t, e) { + if (a_.wxa) return null; + var i = document.createElement("div"), n = this._zr = e.getZr(); + this.el = i, this._x = e.getWidth() / 2, this._y = e.getHeight() / 2, t.appendChild(i), this._container = t, this._show = !1, this._hideTimeout; + var o = this; + i.onmouseenter = function () { + o._enterable && (clearTimeout(o._hideTimeout), o._show = !0), o._inContent = !0 + }, i.onmousemove = function (e) { + if (e = e || window.event, !o._enterable) { + var i = n.handler; + li(t, e, !0), i.dispatch("mousemove", e) + } + }, i.onmouseleave = function () { + o._enterable && o._show && o.hideLater(o._hideDelay), o._inContent = !1 + } + } + + function Fm(t) { + for (var e = t.pop(); t.length;) { + var i = t.pop(); + i && (wo.isInstance(i) && (i = i.get("tooltip", !0)), "string" == typeof i && (i = {formatter: i}), e = new wo(i, e, e.ecModel)) + } + return e + } + + function Hm(t, e) { + return t.dispatchAction || m(e.dispatchAction, e) + } + + function Zm(t, e, i, n, o, a, r) { + var s = Xm(i), l = s.width, u = s.height; + return null != a && (t + l + a > n ? t -= l + a : t += a), null != r && (e + u + r > o ? e -= u + r : e += r), [t, e] + } + + function Um(t, e, i, n, o) { + var a = Xm(i), r = a.width, s = a.height; + return t = Math.min(t + r, n) - r, e = Math.min(e + s, o) - s, t = Math.max(t, 0), e = Math.max(e, 0), [t, e] + } + + function Xm(t) { + var e = t.clientWidth, i = t.clientHeight; + if (document.defaultView && document.defaultView.getComputedStyle) { + var n = document.defaultView.getComputedStyle(t); + n && (e += parseInt(n.paddingLeft, 10) + parseInt(n.paddingRight, 10) + parseInt(n.borderLeftWidth, 10) + parseInt(n.borderRightWidth, 10), i += parseInt(n.paddingTop, 10) + parseInt(n.paddingBottom, 10) + parseInt(n.borderTopWidth, 10) + parseInt(n.borderBottomWidth, 10)) + } + return {width: e, height: i} + } + + function jm(t, e, i) { + var n = i[0], o = i[1], a = 0, r = 0, s = e.width, l = e.height; + switch (t) { + case"inside": + a = e.x + s / 2 - n / 2, r = e.y + l / 2 - o / 2; + break; + case"top": + a = e.x + s / 2 - n / 2, r = e.y - o - 5; + break; + case"bottom": + a = e.x + s / 2 - n / 2, r = e.y + l + 5; + break; + case"left": + a = e.x - n - 5, r = e.y + l / 2 - o / 2; + break; + case"right": + a = e.x + s + 5, r = e.y + l / 2 - o / 2 + } + return [a, r] + } + + function Ym(t) { + return "center" === t || "middle" === t + } + + function qm(t) { + return t.get("stack") || "__ec_stack_" + t.seriesIndex + } + + function Km(t) { + return t.dim + } + + function $m(t, e) { + var i = {}; + d(t, function (t, e) { + var n = t.getData(), o = t.coordinateSystem.getBaseAxis(), a = o.getExtent(), + r = "category" === o.type ? o.getBandWidth() : Math.abs(a[1] - a[0]) / n.count(), s = i[Km(o)] || { + bandWidth: r, + remainedWidth: r, + autoWidthCount: 0, + categoryGap: "20%", + gap: "30%", + stacks: {} + }, l = s.stacks; + i[Km(o)] = s; + var u = qm(t); + l[u] || s.autoWidthCount++, l[u] = l[u] || {width: 0, maxWidth: 0}; + var h = Do(t.get("barWidth"), r), c = Do(t.get("barMaxWidth"), r), d = t.get("barGap"), + f = t.get("barCategoryGap"); + h && !l[u].width && (h = Math.min(s.remainedWidth, h), l[u].width = h, s.remainedWidth -= h), c && (l[u].maxWidth = c), null != d && (s.gap = d), null != f && (s.categoryGap = f) + }); + var n = {}; + return d(i, function (t, e) { + n[e] = {}; + var i = t.stacks, o = t.bandWidth, a = Do(t.categoryGap, o), r = Do(t.gap, 1), s = t.remainedWidth, + l = t.autoWidthCount, u = (s - a) / (l + (l - 1) * r); + u = Math.max(u, 0), d(i, function (t, e) { + var i = t.maxWidth; + i && i < u && (i = Math.min(i, s), t.width && (i = Math.min(i, t.width)), s -= i, t.width = i, l--) + }), u = (s - a) / (l + (l - 1) * r), u = Math.max(u, 0); + var h, c = 0; + d(i, function (t, e) { + t.width || (t.width = u), h = t, c += t.width * (1 + r) + }), h && (c -= h.width * r); + var f = -c / 2; + d(i, function (t, i) { + n[e][i] = n[e][i] || {offset: f, width: t.width}, f += t.width * (1 + r) + }) + }), n + } + + function Jm(t, e) { + qT.call(this, "radius", t, e), this.type = "category" + } + + function Qm(t, e) { + e = e || [0, 360], qT.call(this, "angle", t, e), this.type = "category" + } + + function tv(t, e) { + return e.type || (e.data ? "category" : "value") + } + + function ev(t, e, i) { + var n = e.get("center"), o = i.getWidth(), a = i.getHeight(); + t.cx = Do(n[0], o), t.cy = Do(n[1], a); + var r = t.getRadiusAxis(), s = Math.min(o, a) / 2, l = Do(e.get("radius"), s); + r.inverse ? r.setExtent(l, 0) : r.setExtent(0, l) + } + + function iv(t, e) { + var i = this, n = i.getAngleAxis(), o = i.getRadiusAxis(); + if (n.scale.setExtent(1 / 0, -1 / 0), o.scale.setExtent(1 / 0, -1 / 0), t.eachSeries(function (t) { + if (t.coordinateSystem === i) { + var e = t.getData(); + d(e.mapDimension("radius", !0), function (t) { + o.scale.unionExtentFromData(e, Us(e, t)) + }), d(e.mapDimension("angle", !0), function (t) { + n.scale.unionExtentFromData(e, Us(e, t)) + }) + } + }), yl(n.scale, n.model), yl(o.scale, o.model), "category" === n.type && !n.onBand) { + var a = n.getExtent(), r = 360 / n.scale.count(); + n.inverse ? a[1] += r : a[1] -= r, n.setExtent(a[0], a[1]) + } + } + + function nv(t, e) { + if (t.type = e.get("type"), t.scale = xl(e), t.onBand = e.get("boundaryGap") && "category" === t.type, t.inverse = e.get("inverse"), "angleAxis" === e.mainType) { + t.inverse ^= e.get("clockwise"); + var i = e.get("startAngle"); + t.setExtent(i, i + (t.inverse ? -360 : 360)) + } + e.axis = t, t.model = e + } + + function ov(t, e, i) { + e[1] > e[0] && (e = e.slice().reverse()); + var n = t.coordToPoint([e[0], i]), o = t.coordToPoint([e[1], i]); + return {x1: n[0], y1: n[1], x2: o[0], y2: o[1]} + } + + function av(t) { + return t.getRadiusAxis().inverse ? 0 : 1 + } + + function rv(t) { + var e = t[0], i = t[t.length - 1]; + e && i && Math.abs(Math.abs(e.coord - i.coord) - 360) < 1e-4 && t.pop() + } + + function sv(t, e, i) { + return { + position: [t.cx, t.cy], + rotation: i / 180 * Math.PI, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1, + labelRotate: e.getModel("axisLabel").get("rotate"), + z2: 1 + } + } + + function lv(t, e, i, n, o) { + var a = e.axis, r = a.dataToCoord(t), s = n.getAngleAxis().getExtent()[0]; + s = s / 180 * Math.PI; + var l, u, h, c = n.getRadiusAxis().getExtent(); + if ("radius" === a.dim) { + var d = st(); + dt(d, d, s), ct(d, d, [n.cx, n.cy]), l = mo([r, -o], d); + var f = e.getModel("axisLabel").get("rotate") || 0, p = LD.innerTextLayout(s, f * Math.PI / 180, -1); + u = p.textAlign, h = p.textVerticalAlign + } else { + var g = c[1]; + l = n.coordToPoint([g + o, r]); + var m = n.cx, v = n.cy; + u = Math.abs(l[0] - m) / g < .3 ? "center" : l[0] > m ? "left" : "right", h = Math.abs(l[1] - v) / g < .3 ? "middle" : l[1] > v ? "top" : "bottom" + } + return {position: l, align: u, verticalAlign: h} + } + + function uv(t, e) { + e.update = "updateView", cs(e, function (e, i) { + var n = {}; + return i.eachComponent({mainType: "geo", query: e}, function (i) { + i[t](e.name), d(i.coordinateSystem.regions, function (t) { + n[t.name] = i.isSelected(t.name) || !1 + }) + }), {selected: n, name: e.name} + }) + } + + function hv(t) { + var e = {}; + d(t, function (t) { + e[t] = 1 + }), t.length = 0, d(e, function (e, i) { + t.push(i) + }) + } + + function cv(t) { + if (t) for (var e in t) if (t.hasOwnProperty(e)) return !0 + } + + function dv(t, e, n) { + function o() { + var t = function () { + }; + return t.prototype.__hidden = t.prototype, new t + } + + var a = {}; + return tP(e, function (e) { + var r = a[e] = o(); + tP(t[e], function (t, o) { + if (ZA.isValidType(o)) { + var a = {type: o, visual: t}; + n && n(a, e), r[o] = new ZA(a), "opacity" === o && ((a = i(a)).type = "colorAlpha", r.__hidden.__alphaForOpacity = new ZA(a)) + } + }) + }), a + } + + function fv(t, e, n) { + var o; + d(n, function (t) { + e.hasOwnProperty(t) && cv(e[t]) && (o = !0) + }), o && d(n, function (n) { + e.hasOwnProperty(n) && cv(e[n]) ? t[n] = i(e[n]) : delete t[n] + }) + } + + function pv(t, e, i, n, o, a) { + function r(t) { + return i.getItemVisual(h, t) + } + + function s(t, e) { + i.setItemVisual(h, t, e) + } + + function l(t, l) { + h = null == a ? t : l; + var c = i.getRawDataItem(h); + if (!c || !1 !== c.visualMap) for (var d = n.call(o, t), f = e[d], p = u[d], g = 0, m = p.length; g < m; g++) { + var v = p[g]; + f[v] && f[v].applyVisual(t, r, s) + } + } + + var u = {}; + d(t, function (t) { + var i = ZA.prepareVisualTypes(e[t]); + u[t] = i + }); + var h; + null == a ? i.each(l) : i.each([a], l) + } + + function gv(t, e, i, n) { + var o = {}; + return d(t, function (t) { + var i = ZA.prepareVisualTypes(e[t]); + o[t] = i + }), { + progress: function (t, a) { + null != n && (n = a.getDimension(n)); + for (var r; null != (r = t.next());) { + var s = a.getRawDataItem(r); + if (s && !1 === s.visualMap) return; + for (var l = null != n ? a.get(n, r, !0) : r, u = i(l), h = e[u], c = o[u], d = 0, f = c.length; d < f; d++) { + var p = c[d]; + h[p] && h[p].applyVisual(l, function (t) { + return a.getItemVisual(r, t) + }, function (t, e) { + a.setItemVisual(r, t, e) + }) + } + } + } + } + } + + function mv(t) { + var e = ["x", "y"], i = ["width", "height"]; + return { + point: function (e, i, n) { + if (e) { + var o = n.range; + return vv(e[t], o) + } + }, rect: function (n, o, a) { + if (n) { + var r = a.range, s = [n[e[t]], n[e[t]] + n[i[t]]]; + return s[1] < s[0] && s.reverse(), vv(s[0], r) || vv(s[1], r) || vv(r[0], s) || vv(r[1], s) + } + } + } + } + + function vv(t, e) { + return e[0] <= t && t <= e[1] + } + + function yv(t, e, i, n, o) { + for (var a = 0, r = o[o.length - 1]; a < o.length; a++) { + var s = o[a]; + if (xv(t, e, i, n, s[0], s[1], r[0], r[1])) return !0; + r = s + } + } + + function xv(t, e, i, n, o, a, r, s) { + var l = wv(i - t, o - r, n - e, a - s); + if (_v(l)) return !1; + var u = wv(o - t, o - r, a - e, a - s) / l; + if (u < 0 || u > 1) return !1; + var h = wv(i - t, o - t, n - e, a - e) / l; + return !(h < 0 || h > 1) + } + + function _v(t) { + return t <= 1e-6 && t >= -1e-6 + } + + function wv(t, e, i, n) { + return t * n - e * i + } + + function bv(t, e, i) { + var n = this._targetInfoList = [], o = {}, a = Mv(e, t); + iP(lP, function (t, e) { + (!i || !i.include || nP(i.include, e) >= 0) && t(a, n, o) + }) + } + + function Sv(t) { + return t[0] > t[1] && t.reverse(), t + } + + function Mv(t, e) { + return Oi(t, e, {includeMainTypes: rP}) + } + + function Iv(t, e, i, n) { + var o = i.getAxis(["x", "y"][t]), a = Sv(f([0, 1], function (t) { + return e ? o.coordToData(o.toLocalCoord(n[t])) : o.toGlobalCoord(o.dataToCoord(n[t])) + })), r = []; + return r[t] = a, r[1 - t] = [NaN, NaN], {values: a, xyMinMax: r} + } + + function Tv(t, e, i, n) { + return [e[0] - n[t] * i[0], e[1] - n[t] * i[1]] + } + + function Dv(t, e) { + var i = Av(t), n = Av(e), o = [i[0] / n[0], i[1] / n[1]]; + return isNaN(o[0]) && (o[0] = 1), isNaN(o[1]) && (o[1] = 1), o + } + + function Av(t) { + return t ? [t[0][1] - t[0][0], t[1][1] - t[1][0]] : [NaN, NaN] + } + + function Cv(t, e, i, n, o) { + if (o) { + var a = t.getZr(); + a[gP] || (a[pP] || (a[pP] = Lv), wr(a, pP, i, e)(t, n)) + } + } + + function Lv(t, e) { + if (!t.isDisposed()) { + var i = t.getZr(); + i[gP] = !0, t.dispatchAction({type: "brushSelect", batch: e}), i[gP] = !1 + } + } + + function kv(t, e, i, n) { + for (var o = 0, a = e.length; o < a; o++) { + var r = e[o]; + if (t[r.brushType](n, i, r.selectors, r)) return !0 + } + } + + function Pv(t) { + var e = t.brushSelector; + if (_(e)) { + var i = []; + return d(eP, function (t, n) { + i[n] = function (i, n, o, a) { + var r = n.getItemLayout(i); + return t[e](r, o, a) + } + }), i + } + if (x(e)) { + var n = {}; + return d(eP, function (t, i) { + n[i] = e + }), n + } + return e + } + + function Nv(t, e) { + var i = t.option.seriesIndex; + return null != i && "all" !== i && (y(i) ? l(i, e) < 0 : e !== i) + } + + function Ov(t) { + var e = t.selectors = {}; + return d(eP[t.brushType], function (i, n) { + e[n] = function (n) { + return i(n, e, t) + } + }), t + } + + function Ev(t) { + return new $t(t[0][0], t[1][0], t[0][1] - t[0][0], t[1][1] - t[1][0]) + } + + function Rv(t, e) { + return n({ + brushType: t.brushType, + brushMode: t.brushMode, + transformable: t.transformable, + brushStyle: new wo(t.brushStyle).getItemStyle(), + removeOnClick: t.removeOnClick, + z: t.z + }, e, !0) + } + + function zv(t, e, i, n) { + (!n || n.$from !== t.id) && this._brushController.setPanels(t.brushTargetManager.makePanelOpts(i)).enableBrush(t.brushOption).updateCovers(t.areas.slice()) + } + + function Bv(t, e) { + xP[t] = e + } + + function Vv(t) { + return xP[t] + } + + function Gv(t, e, i) { + this.model = t, this.ecModel = e, this.api = i, this._brushType, this._brushMode + } + + function Wv(t, e, i) { + this._model = t + } + + function Fv(t, e, i, n) { + var o = i.calendarModel, a = i.seriesModel, r = o ? o.coordinateSystem : a ? a.coordinateSystem : null; + return r === this ? r[t](n) : null + } + + function Hv(t, e) { + var i = t.cellSize; + y(i) ? 1 === i.length && (i[1] = i[0]) : i = t.cellSize = [i, i]; + var n = f([0, 1], function (t) { + return ea(e, t) && (i[t] = "auto"), null != i[t] && "auto" !== i[t] + }); + ia(t, e, {type: "box", ignoreSize: n}) + } + + function Zv(t) { + return l(IP, t) >= 0 + } + + function Uv(t, e, i) { + function n(t, e) { + return l(e.nodes, t) >= 0 + } + + function o(t, n) { + var o = !1; + return e(function (e) { + d(i(t, e) || [], function (t) { + n.records[e.name][t] && (o = !0) + }) + }), o + } + + function a(t, n) { + n.nodes.push(t), e(function (e) { + d(i(t, e) || [], function (t) { + n.records[e.name][t] = !0 + }) + }) + } + + return function (i) { + var r = {nodes: [], records: {}}; + if (e(function (t) { + r.records[t.name] = {} + }), !i) return r; + a(i, r); + var s; + do { + s = !1, t(function (t) { + !n(t, r) && o(t, r) && (a(t, r), s = !0) + }) + } while (s); + return r + } + } + + function Xv(t, e, i) { + var n = [1 / 0, -1 / 0]; + return DP(i, function (t) { + var i = t.getData(); + i && DP(i.mapDimension(e, !0), function (t) { + var e = i.getApproximateExtent(t); + e[0] < n[0] && (n[0] = e[0]), e[1] > n[1] && (n[1] = e[1]) + }) + }), n[1] < n[0] && (n = [NaN, NaN]), jv(t, n), n + } + + function jv(t, e) { + var i = t.getAxisModel(), n = i.getMin(!0), o = "category" === i.get("type"), a = o && i.getCategories().length; + null != n && "dataMin" !== n && "function" != typeof n ? e[0] = n : o && (e[0] = a > 0 ? 0 : NaN); + var r = i.getMax(!0); + return null != r && "dataMax" !== r && "function" != typeof r ? e[1] = r : o && (e[1] = a > 0 ? a - 1 : NaN), i.get("scale", !0) || (e[0] > 0 && (e[0] = 0), e[1] < 0 && (e[1] = 0)), e + } + + function Yv(t, e) { + var i = t.getAxisModel(), n = t._percentWindow, o = t._valueWindow; + if (n) { + var a = Po(o, [0, 500]); + a = Math.min(a, 20); + var r = e || 0 === n[0] && 100 === n[1]; + i.setRange(r ? null : +o[0].toFixed(a), r ? null : +o[1].toFixed(a)) + } + } + + function qv(t) { + var e = t._minMaxSpan = {}, i = t._dataZoomModel; + DP(["min", "max"], function (n) { + e[n + "Span"] = i.get(n + "Span"); + var o = i.get(n + "ValueSpan"); + if (null != o && (e[n + "ValueSpan"] = o, null != (o = t.getAxisModel().axis.scale.parse(o)))) { + var a = t._dataExtent; + e[n + "Span"] = To(a[0] + o, a, [0, 100], !0) + } + }) + } + + function Kv(t) { + var e = {}; + return LP(["start", "end", "startValue", "endValue", "throttle"], function (i) { + t.hasOwnProperty(i) && (e[i] = t[i]) + }), e + } + + function $v(t, e) { + var i = t._rangePropMode, n = t.get("rangeMode"); + LP([["start", "startValue"], ["end", "endValue"]], function (t, o) { + var a = null != e[t[0]], r = null != e[t[1]]; + a && !r ? i[o] = "percent" : !a && r ? i[o] = "value" : n ? i[o] = n[o] : a && (i[o] = "percent") + }) + } + + function Jv(t) { + return {x: "y", y: "x", radius: "angle", angle: "radius"}[t] + } + + function Qv(t) { + return "vertical" === t ? "ns-resize" : "ew-resize" + } + + function ty(t, e) { + var i = ny(t), n = e.dataZoomId, o = e.coordId; + d(i, function (t, i) { + var a = t.dataZoomInfos; + a[n] && l(e.allCoordIds, o) < 0 && (delete a[n], t.count--) + }), ay(i); + var a = i[o]; + a || ((a = i[o] = { + coordId: o, + dataZoomInfos: {}, + count: 0 + }).controller = oy(t, a), a.dispatchAction = v(uy, t)), !a.dataZoomInfos[n] && a.count++, a.dataZoomInfos[n] = e; + var r = hy(a.dataZoomInfos); + a.controller.enable(r.controlType, r.opt), a.controller.setPointerChecker(e.containsPoint), wr(a, "dispatchAction", e.throttleRate, "fixRate") + } + + function ey(t, e) { + var i = ny(t); + d(i, function (t) { + t.controller.dispose(); + var i = t.dataZoomInfos; + i[e] && (delete i[e], t.count--) + }), ay(i) + } + + function iy(t) { + return t.type + "\0_" + t.id + } + + function ny(t) { + var e = t.getZr(); + return e[ZP] || (e[ZP] = {}) + } + + function oy(t, e) { + var i = new kh(t.getZr()); + return i.on("pan", HP(ry, e)), i.on("zoom", HP(sy, e)), i + } + + function ay(t) { + d(t, function (e, i) { + e.count || (e.controller.dispose(), delete t[i]) + }) + } + + function ry(t, e, i, n, o, a, r) { + ly(t, function (s) { + return s.panGetRange(t.controller, e, i, n, o, a, r) + }) + } + + function sy(t, e, i, n) { + ly(t, function (o) { + return o.zoomGetRange(t.controller, e, i, n) + }) + } + + function ly(t, e) { + var i = []; + d(t.dataZoomInfos, function (t) { + var n = e(t); + !t.disabled && n && i.push({dataZoomId: t.dataZoomId, start: n[0], end: n[1]}) + }), i.length && t.dispatchAction(i) + } + + function uy(t, e) { + t.dispatchAction({type: "dataZoom", batch: e}) + } + + function hy(t) { + var e, i = {}, n = {type_true: 2, type_move: 1, type_false: 0, type_undefined: -1}; + return d(t, function (t) { + var o = !t.disabled && (!t.zoomLock || "move"); + n["type_" + o] > n["type_" + e] && (e = o), a(i, t.roamControllerOpt) + }), {controlType: e, opt: i} + } + + function cy(t, e) { + return t && t.hasOwnProperty && t.hasOwnProperty(e) + } + + function dy(t, e, i, n) { + for (var o = e.targetVisuals[n], a = ZA.prepareVisualTypes(o), r = {color: t.getData().getVisual("color")}, s = 0, l = a.length; s < l; s++) { + var u = a[s], h = o["opacity" === u ? "__alphaForOpacity" : u]; + h && h.applyVisual(i, function (t) { + return r[t] + }, function (t, e) { + r[t] = e + }) + } + return r.color + } + + function fy(t, e, i) { + if (i[0] === i[1]) return i.slice(); + for (var n = (i[1] - i[0]) / 200, o = i[0], a = [], r = 0; r <= 200 && o < i[1]; r++) a.push(o), o += n; + return a.push(i[1]), a + } + + function py(t, e, i) { + var n = t.option, o = n.align; + if (null != o && "auto" !== o) return o; + for (var a = { + width: e.getWidth(), + height: e.getHeight() + }, r = "horizontal" === n.orient ? 1 : 0, s = [["left", "right", "width"], ["top", "bottom", "height"]], l = s[r], u = [0, null, 10], h = {}, c = 0; c < 3; c++) h[s[1 - r][c]] = u[c], h[l[c]] = 2 === c ? i[0] : n[l[c]]; + var d = [["x", "width", 3], ["y", "height", 0]][r], f = Qo(h, a, n.padding); + return l[(f.margin[d[2]] || 0) + f[d[0]] + .5 * f[d[1]] < .5 * a[d[1]] ? 0 : 1] + } + + function gy(t) { + return d(t || [], function (e) { + null != t.dataIndex && (t.dataIndexInside = t.dataIndex, t.dataIndex = null) + }), t + } + + function my(t, e, i, n) { + return new wS({ + shape: {points: t}, draggable: !!i, cursor: e, drift: i, onmousemove: function (t) { + zw(t.event) + }, ondragend: n + }) + } + + function vy(t, e) { + return 0 === t ? [[0, 0], [e, 0], [e, -e]] : [[0, 0], [e, 0], [e, e]] + } + + function yy(t, e, i, n) { + return t ? [[0, -dN(e, fN(i, 0))], [gN, 0], [0, dN(e, fN(n - i, 0))]] : [[0, 0], [5, -5], [5, 5]] + } + + function xy(t, e, i) { + var n = pN / 2, o = t.get("hoverLinkDataSize"); + return o && (n = hN(o, e, i, !0) / 2), n + } + + function _y(t) { + var e = t.get("hoverLinkOnHandle"); + return !!(null == e ? t.get("realtime") : e) + } + + function wy(t) { + return "vertical" === t ? "ns-resize" : "ew-resize" + } + + function by(t, e) { + var i = t.inverse; + ("vertical" === t.orient ? !i : i) && e.reverse() + } + + function Sy(t) { + Mi(t, "label", ["show"]) + } + + function My(t) { + return !(isNaN(parseFloat(t.x)) && isNaN(parseFloat(t.y))) + } + + function Iy(t) { + return !isNaN(parseFloat(t.x)) && !isNaN(parseFloat(t.y)) + } + + function Ty(t, e, i, n, o, a) { + var r = [], s = Zs(e, n) ? e.getCalculationInfo("stackResultDimension") : n, l = Py(e, s, t), + u = e.indicesOfNearest(s, l)[0]; + r[o] = e.get(i, u), r[a] = e.get(n, u); + var h = Lo(e.get(n, u)); + return (h = Math.min(h, 20)) >= 0 && (r[a] = +r[a].toFixed(h)), r + } + + function Dy(t, e) { + var n = t.getData(), o = t.coordinateSystem; + if (e && !Iy(e) && !y(e.coord) && o) { + var a = o.dimensions, r = Ay(e, n, o, t); + if ((e = i(e)).type && MN[e.type] && r.baseAxis && r.valueAxis) { + var s = bN(a, r.baseAxis.dim), l = bN(a, r.valueAxis.dim); + e.coord = MN[e.type](n, r.baseDataDim, r.valueDataDim, s, l), e.value = e.coord[l] + } else { + for (var u = [null != e.xAxis ? e.xAxis : e.radiusAxis, null != e.yAxis ? e.yAxis : e.angleAxis], h = 0; h < 2; h++) MN[u[h]] && (u[h] = Py(n, n.mapDimension(a[h]), u[h])); + e.coord = u + } + } + return e + } + + function Ay(t, e, i, n) { + var o = {}; + return null != t.valueIndex || null != t.valueDim ? (o.valueDataDim = null != t.valueIndex ? e.getDimension(t.valueIndex) : t.valueDim, o.valueAxis = i.getAxis(Cy(n, o.valueDataDim)), o.baseAxis = i.getOtherAxis(o.valueAxis), o.baseDataDim = e.mapDimension(o.baseAxis.dim)) : (o.baseAxis = n.getBaseAxis(), o.valueAxis = i.getOtherAxis(o.baseAxis), o.baseDataDim = e.mapDimension(o.baseAxis.dim), o.valueDataDim = e.mapDimension(o.valueAxis.dim)), o + } + + function Cy(t, e) { + var i = t.getData(), n = i.dimensions; + e = i.getDimension(e); + for (var o = 0; o < n.length; o++) { + var a = i.getDimensionInfo(n[o]); + if (a.name === e) return a.coordDim + } + } + + function Ly(t, e) { + return !(t && t.containData && e.coord && !My(e)) || t.containData(e.coord) + } + + function ky(t, e, i, n) { + return n < 2 ? t.coord && t.coord[n] : t.value + } + + function Py(t, e, i) { + if ("average" === i) { + var n = 0, o = 0; + return t.each(e, function (t, e) { + isNaN(t) || (n += t, o++) + }), n / o + } + return "median" === i ? t.getMedian(e) : t.getDataExtent(e, !0)["max" === i ? 1 : 0] + } + + function Ny(t, e, i) { + var n = e.coordinateSystem; + t.each(function (o) { + var a, r = t.getItemModel(o), s = Do(r.get("x"), i.getWidth()), l = Do(r.get("y"), i.getHeight()); + if (isNaN(s) || isNaN(l)) { + if (e.getMarkerPosition) a = e.getMarkerPosition(t.getValues(t.dimensions, o)); else if (n) { + var u = t.get(n.dimensions[0], o), h = t.get(n.dimensions[1], o); + a = n.dataToPoint([u, h]) + } + } else a = [s, l]; + isNaN(s) || (a[0] = s), isNaN(l) || (a[1] = l), t.setItemLayout(o, a) + }) + } + + function Oy(t, e, i) { + var n; + n = t ? f(t && t.dimensions, function (t) { + return r({name: t}, e.getData().getDimensionInfo(e.getData().mapDimension(t)) || {}) + }) : [{name: "value", type: "float"}]; + var o = new sT(n, i), a = f(i.get("data"), v(Dy, e)); + return t && (a = g(a, v(Ly, t))), o.initData(a, null, t ? ky : function (t) { + return t.value + }), o + } + + function Ey(t) { + return !isNaN(t) && !isFinite(t) + } + + function Ry(t, e, i, n) { + var o = 1 - t, a = n.dimensions[t]; + return Ey(e[o]) && Ey(i[o]) && e[t] === i[t] && n.getAxis(a).containData(e[t]) + } + + function zy(t, e) { + if ("cartesian2d" === t.type) { + var i = e[0].coord, n = e[1].coord; + if (i && n && (Ry(1, i, n, t) || Ry(0, i, n, t))) return !0 + } + return Ly(t, e[0]) && Ly(t, e[1]) + } + + function By(t, e, i, n, o) { + var a, r = n.coordinateSystem, s = t.getItemModel(e), l = Do(s.get("x"), o.getWidth()), + u = Do(s.get("y"), o.getHeight()); + if (isNaN(l) || isNaN(u)) { + if (n.getMarkerPosition) a = n.getMarkerPosition(t.getValues(t.dimensions, e)); else { + var h = r.dimensions, c = t.get(h[0], e), d = t.get(h[1], e); + a = r.dataToPoint([c, d]) + } + if ("cartesian2d" === r.type) { + var f = r.getAxis("x"), p = r.getAxis("y"), h = r.dimensions; + Ey(t.get(h[0], e)) ? a[0] = f.toGlobalCoord(f.getExtent()[i ? 0 : 1]) : Ey(t.get(h[1], e)) && (a[1] = p.toGlobalCoord(p.getExtent()[i ? 0 : 1])) + } + isNaN(l) || (a[0] = l), isNaN(u) || (a[1] = u) + } else a = [l, u]; + t.setItemLayout(e, a) + } + + function Vy(t, e, i) { + var n; + n = t ? f(t && t.dimensions, function (t) { + return r({name: t}, e.getData().getDimensionInfo(e.getData().mapDimension(t)) || {}) + }) : [{name: "value", type: "float"}]; + var o = new sT(n, i), a = new sT(n, i), s = new sT([], i), l = f(i.get("data"), v(TN, e, t, i)); + t && (l = g(l, v(zy, t))); + var u = t ? ky : function (t) { + return t.value + }; + return o.initData(f(l, function (t) { + return t[0] + }), null, u), a.initData(f(l, function (t) { + return t[1] + }), null, u), s.initData(f(l, function (t) { + return t[2] + })), s.hasItemOption = !0, {from: o, to: a, line: s} + } + + function Gy(t) { + return !isNaN(t) && !isFinite(t) + } + + function Wy(t, e, i, n) { + var o = 1 - t; + return Gy(e[o]) && Gy(i[o]) + } + + function Fy(t, e) { + var i = e.coord[0], n = e.coord[1]; + return !("cartesian2d" !== t.type || !i || !n || !Wy(1, i, n, t) && !Wy(0, i, n, t)) || (Ly(t, { + coord: i, + x: e.x0, + y: e.y0 + }) || Ly(t, {coord: n, x: e.x1, y: e.y1})) + } + + function Hy(t, e, i, n, o) { + var a, r = n.coordinateSystem, s = t.getItemModel(e), l = Do(s.get(i[0]), o.getWidth()), + u = Do(s.get(i[1]), o.getHeight()); + if (isNaN(l) || isNaN(u)) { + if (n.getMarkerPosition) a = n.getMarkerPosition(t.getValues(i, e)); else { + var h = [f = t.get(i[0], e), p = t.get(i[1], e)]; + r.clampData && r.clampData(h, h), a = r.dataToPoint(h, !0) + } + if ("cartesian2d" === r.type) { + var c = r.getAxis("x"), d = r.getAxis("y"), f = t.get(i[0], e), p = t.get(i[1], e); + Gy(f) ? a[0] = c.toGlobalCoord(c.getExtent()["x0" === i[0] ? 0 : 1]) : Gy(p) && (a[1] = d.toGlobalCoord(d.getExtent()["y0" === i[1] ? 0 : 1])) + } + isNaN(l) || (a[0] = l), isNaN(u) || (a[1] = u) + } else a = [l, u]; + return a + } + + function Zy(t, e, i) { + var n, o, a = ["x0", "y0", "x1", "y1"]; + t ? (n = f(t && t.dimensions, function (t) { + var i = e.getData(); + return r({name: t}, i.getDimensionInfo(i.mapDimension(t)) || {}) + }), o = new sT(f(a, function (t, e) { + return {name: t, type: n[e % 2].type} + }), i)) : o = new sT(n = [{name: "value", type: "float"}], i); + var s = f(i.get("data"), v(DN, e, t, i)); + t && (s = g(s, v(Fy, t))); + var l = t ? function (t, e, i, n) { + return t.coord[Math.floor(n / 2)][n % 2] + } : function (t) { + return t.value + }; + return o.initData(s, null, l), o.hasItemOption = !0, o + } + + function Uy(t) { + var e = t.type, i = {number: "value", time: "time"}; + if (i[e] && (t.axisType = i[e], delete t.type), Xy(t), jy(t, "controlPosition")) { + var n = t.controlStyle || (t.controlStyle = {}); + jy(n, "position") || (n.position = t.controlPosition), "none" !== n.position || jy(n, "show") || (n.show = !1, delete n.position), delete t.controlPosition + } + d(t.data || [], function (t) { + w(t) && !y(t) && (!jy(t, "value") && jy(t, "name") && (t.value = t.name), Xy(t)) + }) + } + + function Xy(t) { + var e = t.itemStyle || (t.itemStyle = {}), i = e.emphasis || (e.emphasis = {}), n = t.label || t.label || {}, + o = n.normal || (n.normal = {}), a = {normal: 1, emphasis: 1}; + d(n, function (t, e) { + a[e] || jy(o, e) || (o[e] = t) + }), i.label && !jy(n, "emphasis") && (n.emphasis = i.label, delete i.label) + } + + function jy(t, e) { + return t.hasOwnProperty(e) + } + + function Yy(t, e) { + return Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}, t.get("padding")) + } + + function qy(t, e, n, o) { + return Rn(t.get(e).replace(/^path:\/\//, ""), i(o || {}), new $t(n[0], n[1], n[2], n[3]), "center") + } + + function Ky(t, e, i, o, a, r) { + var s = e.get("color"); + a ? (a.setColor(s), i.add(a), r && r.onUpdate(a)) : ((a = Tl(t.get("symbol"), -1, -1, 2, 2, s)).setStyle("strokeNoScale", !0), i.add(a), r && r.onCreate(a)); + var l = e.getItemStyle(["color", "symbol", "symbolSize"]); + a.setStyle(l), o = n({rectHover: !0, z2: 100}, o, !0); + var u = t.get("symbolSize"); + (u = u instanceof Array ? u.slice() : [+u, +u])[0] /= 2, u[1] /= 2, o.scale = u; + var h = t.get("symbolOffset"); + if (h) { + var c = o.position = o.position || [0, 0]; + c[0] += Do(h[0], u[0]), c[1] += Do(h[1], u[1]) + } + var d = t.get("symbolRotate"); + return o.rotation = (d || 0) * Math.PI / 180 || 0, a.attr(o), a.updateTransform(), a + } + + function $y(t, e, i, n, o) { + if (!t.dragging) { + var a = n.getModel("checkpointStyle"), r = i.dataToCoord(n.getData().get(["value"], e)); + o || !a.get("animation", !0) ? t.attr({position: [r, 0]}) : (t.stopAnimation(!0), t.animateTo({position: [r, 0]}, a.get("animationDuration", !0), a.get("animationEasing", !0))) + } + } + + function Jy(t) { + return 0 === t.indexOf("my") + } + + function Qy(t) { + this.model = t + } + + function tx(t) { + this.model = t + } + + function ex(t) { + var e = {}, i = [], n = []; + return t.eachRawSeries(function (t) { + var o = t.coordinateSystem; + if (!o || "cartesian2d" !== o.type && "polar" !== o.type) i.push(t); else { + var a = o.getBaseAxis(); + if ("category" === a.type) { + var r = a.dim + "_" + a.index; + e[r] || (e[r] = {categoryAxis: a, valueAxis: o.getOtherAxis(a), series: []}, n.push({ + axisDim: a.dim, + axisIndex: a.index + })), e[r].series.push(t) + } else i.push(t) + } + }), {seriesGroupByCategoryAxis: e, other: i, meta: n} + } + + function ix(t) { + var e = []; + return d(t, function (t, i) { + var n = t.categoryAxis, o = t.valueAxis.dim, a = [" "].concat(f(t.series, function (t) { + return t.name + })), r = [n.model.getCategories()]; + d(t.series, function (t) { + r.push(t.getRawData().mapArray(o, function (t) { + return t + })) + }); + for (var s = [a.join(HN)], l = 0; l < r[0].length; l++) { + for (var u = [], h = 0; h < r.length; h++) u.push(r[h][l]); + s.push(u.join(HN)) + } + e.push(s.join("\n")) + }), e.join("\n\n" + FN + "\n\n") + } + + function nx(t) { + return f(t, function (t) { + var e = t.getRawData(), i = [t.name], n = []; + return e.each(e.dimensions, function () { + for (var t = arguments.length, o = arguments[t - 1], a = e.getName(o), r = 0; r < t - 1; r++) n[r] = arguments[r]; + i.push((a ? a + HN : "") + n.join(HN)) + }), i.join("\n") + }).join("\n\n" + FN + "\n\n") + } + + function ox(t) { + var e = ex(t); + return { + value: g([ix(e.seriesGroupByCategoryAxis), nx(e.other)], function (t) { + return t.replace(/[\n\t\s]/g, "") + }).join("\n\n" + FN + "\n\n"), meta: e.meta + } + } + + function ax(t) { + return t.replace(/^\s\s*/, "").replace(/\s\s*$/, "") + } + + function rx(t) { + if (t.slice(0, t.indexOf("\n")).indexOf(HN) >= 0) return !0 + } + + function sx(t) { + for (var e = t.split(/\n+/g), i = [], n = f(ax(e.shift()).split(ZN), function (t) { + return {name: t, data: []} + }), o = 0; o < e.length; o++) { + var a = ax(e[o]).split(ZN); + i.push(a.shift()); + for (var r = 0; r < a.length; r++) n[r] && (n[r].data[o] = a[r]) + } + return {series: n, categories: i} + } + + function lx(t) { + for (var e = t.split(/\n+/g), i = ax(e.shift()), n = [], o = 0; o < e.length; o++) { + var a, r = ax(e[o]).split(ZN), s = "", l = !1; + isNaN(r[0]) ? (l = !0, s = r[0], r = r.slice(1), n[o] = { + name: s, + value: [] + }, a = n[o].value) : a = n[o] = []; + for (var u = 0; u < r.length; u++) a.push(+r[u]); + 1 === a.length && (l ? n[o].value = a[0] : n[o] = a[0]) + } + return {name: i, data: n} + } + + function ux(t, e) { + var i = {series: []}; + return d(t.split(new RegExp("\n*" + FN + "\n*", "g")), function (t, n) { + if (rx(t)) { + var o = sx(t), a = e[n], r = a.axisDim + "Axis"; + a && (i[r] = i[r] || [], i[r][a.axisIndex] = {data: o.categories}, i.series = i.series.concat(o.series)) + } else { + o = lx(t); + i.series.push(o) + } + }), i + } + + function hx(t) { + this._dom = null, this.model = t + } + + function cx(t, e) { + return f(t, function (t, i) { + var n = e && e[i]; + return w(n) && !y(n) ? (w(t) && !y(t) && (t = t.value), r({value: t}, n)) : t + }) + } + + function dx(t, e) { + var i = mx(t); + UN(e, function (e, n) { + for (var o = i.length - 1; o >= 0 && !i[o][n]; o--) ; + if (o < 0) { + var a = t.queryComponents({mainType: "dataZoom", subType: "select", id: n})[0]; + if (a) { + var r = a.getPercentRange(); + i[0][n] = {dataZoomId: n, start: r[0], end: r[1]} + } + } + }), i.push(e) + } + + function fx(t) { + var e = mx(t), i = e[e.length - 1]; + e.length > 1 && e.pop(); + var n = {}; + return UN(i, function (t, i) { + for (var o = e.length - 1; o >= 0; o--) if (t = e[o][i]) { + n[i] = t; + break + } + }), n + } + + function px(t) { + t[XN] = null + } + + function gx(t) { + return mx(t).length + } + + function mx(t) { + var e = t[XN]; + return e || (e = t[XN] = [{}]), e + } + + function vx(t, e, i) { + (this._brushController = new Qd(i.getZr())).on("brush", m(this._onBrush, this)).mount(), this._isZoomActive + } + + function yx(t) { + var e = {}; + return d(["xAxisIndex", "yAxisIndex"], function (i) { + e[i] = t[i], null == e[i] && (e[i] = "all"), (!1 === e[i] || "none" === e[i]) && (e[i] = []) + }), e + } + + function xx(t, e) { + t.setIconStatus("back", gx(e) > 1 ? "emphasis" : "normal") + } + + function _x(t, e, i, n, o) { + var a = i._isZoomActive; + n && "takeGlobalCursor" === n.type && (a = "dataZoomSelect" === n.key && n.dataZoomSelectActive), i._isZoomActive = a, t.setIconStatus("zoom", a ? "emphasis" : "normal"); + var r = new bv(yx(t.option), e, {include: ["grid"]}); + i._brushController.setPanels(r.makePanelOpts(o, function (t) { + return t.xAxisDeclared && !t.yAxisDeclared ? "lineX" : !t.xAxisDeclared && t.yAxisDeclared ? "lineY" : "rect" + })).enableBrush(!!a && {brushType: "auto", brushStyle: {lineWidth: 0, fill: "rgba(0,0,0,0.2)"}}) + } + + function bx(t) { + this.model = t + } + + function Sx(t) { + return QN(t) + } + + function Mx() { + if (!iO && nO) { + iO = !0; + var t = nO.styleSheets; + t.length < 31 ? nO.createStyleSheet().addRule(".zrvml", "behavior:url(#default#VML)") : t[0].addRule(".zrvml", "behavior:url(#default#VML)") + } + } + + function Ix(t) { + return parseInt(t, 10) + } + + function Tx(t, e) { + Mx(), this.root = t, this.storage = e; + var i = document.createElement("div"), n = document.createElement("div"); + i.style.cssText = "display:inline-block;overflow:hidden;position:relative;width:300px;height:150px;", n.style.cssText = "position:absolute;left:0;top:0;", t.appendChild(i), this._vmlRoot = n, this._vmlViewport = i, this.resize(); + var o = e.delFromStorage, a = e.addToStorage; + e.delFromStorage = function (t) { + o.call(e, t), t && t.onRemove && t.onRemove(n) + }, e.addToStorage = function (t) { + t.onAdd && t.onAdd(n), a.call(e, t) + }, this._firstPaint = !0 + } + + function Dx(t) { + return function () { + ew('In IE8.0 VML mode painter not support method "' + t + '"') + } + } + + function Ax(t) { + return document.createElementNS(zO, t) + } + + function Cx(t) { + return WO(1e4 * t) / 1e4 + } + + function Lx(t) { + return t < jO && t > -jO + } + + function kx(t, e) { + var i = e ? t.textFill : t.fill; + return null != i && i !== GO + } + + function Px(t, e) { + var i = e ? t.textStroke : t.stroke; + return null != i && i !== GO + } + + function Nx(t, e) { + e && Ox(t, "transform", "matrix(" + VO.call(e, ",") + ")") + } + + function Ox(t, e, i) { + (!i || "linear" !== i.type && "radial" !== i.type) && ("string" == typeof i && i.indexOf("NaN") > -1 && console.log(i), t.setAttribute(e, i)) + } + + function Ex(t, e, i) { + t.setAttributeNS("http://www.w3.org/1999/xlink", e, i) + } + + function Rx(t, e, i) { + if (kx(e, i)) { + var n = i ? e.textFill : e.fill; + n = "transparent" === n ? GO : n, "none" !== t.getAttribute("clip-path") && n === GO && (n = "rgba(0, 0, 0, 0.002)"), Ox(t, "fill", n), Ox(t, "fill-opacity", e.opacity) + } else Ox(t, "fill", GO); + if (Px(e, i)) { + var o = i ? e.textStroke : e.stroke; + Ox(t, "stroke", o = "transparent" === o ? GO : o), Ox(t, "stroke-width", (i ? e.textStrokeWidth : e.lineWidth) / (!i && e.strokeNoScale ? e.host.getLineScale() : 1)), Ox(t, "paint-order", i ? "stroke" : "fill"), Ox(t, "stroke-opacity", e.opacity), e.lineDash ? (Ox(t, "stroke-dasharray", e.lineDash.join(",")), Ox(t, "stroke-dashoffset", WO(e.lineDashOffset || 0))) : Ox(t, "stroke-dasharray", ""), e.lineCap && Ox(t, "stroke-linecap", e.lineCap), e.lineJoin && Ox(t, "stroke-linejoin", e.lineJoin), e.miterLimit && Ox(t, "stroke-miterlimit", e.miterLimit) + } else Ox(t, "stroke", GO) + } + + function zx(t) { + for (var e = [], i = t.data, n = t.len(), o = 0; o < n;) { + var a = "", r = 0; + switch (i[o++]) { + case BO.M: + a = "M", r = 2; + break; + case BO.L: + a = "L", r = 2; + break; + case BO.Q: + a = "Q", r = 4; + break; + case BO.C: + a = "C", r = 6; + break; + case BO.A: + var s = i[o++], l = i[o++], u = i[o++], h = i[o++], c = i[o++], d = i[o++], f = i[o++], p = i[o++], + g = Math.abs(d), m = Lx(g - UO) && !Lx(g), v = !1; + v = g >= UO || !Lx(g) && (d > -ZO && d < 0 || d > ZO) == !!p; + var y = Cx(s + u * HO(c)), x = Cx(l + h * FO(c)); + m && (d = p ? UO - 1e-4 : 1e-4 - UO, v = !0, 9 === o && e.push("M", y, x)); + var _ = Cx(s + u * HO(c + d)), w = Cx(l + h * FO(c + d)); + e.push("A", Cx(u), Cx(h), WO(f * XO), +v, +p, _, w); + break; + case BO.Z: + a = "Z"; + break; + case BO.R: + var _ = Cx(i[o++]), w = Cx(i[o++]), b = Cx(i[o++]), S = Cx(i[o++]); + e.push("M", _, w, "L", _ + b, w, "L", _ + b, w + S, "L", _, w + S, "L", _, w) + } + a && e.push(a); + for (var M = 0; M < r; M++) e.push(Cx(i[o++])) + } + return e.join(" ") + } + + function Bx(t) { + return "middle" === t ? "middle" : "bottom" === t ? "baseline" : "hanging" + } + + function Vx() { + } + + function Gx(t, e, i, n) { + for (var o = 0, a = e.length, r = 0, s = 0; o < a; o++) { + var l = e[o]; + if (l.removed) { + for (var u = [], h = s; h < s + l.count; h++) u.push(h); + l.indices = u, s += l.count + } else { + for (var u = [], h = r; h < r + l.count; h++) u.push(h); + l.indices = u, r += l.count, l.added || (s += l.count) + } + } + return e + } + + function Wx(t) { + return {newPos: t.newPos, components: t.components.slice(0)} + } + + function Fx(t, e, i, n, o) { + this._zrId = t, this._svgRoot = e, this._tagNames = "string" == typeof i ? [i] : i, this._markLabel = n, this._domName = o || "_dom", this.nextId = 0 + } + + function Hx(t, e) { + Fx.call(this, t, e, ["linearGradient", "radialGradient"], "__gradient_in_use__") + } + + function Zx(t, e) { + Fx.call(this, t, e, "clipPath", "__clippath_in_use__") + } + + function Ux(t, e) { + Fx.call(this, t, e, ["filter"], "__filter_in_use__", "_shadowDom") + } + + function Xx(t) { + return t && (t.shadowBlur || t.shadowOffsetX || t.shadowOffsetY || t.textShadowBlur || t.textShadowOffsetX || t.textShadowOffsetY) + } + + function jx(t) { + return parseInt(t, 10) + } + + function Yx(t) { + return t instanceof In ? YO : t instanceof Je ? qO : t instanceof fS ? KO : YO + } + + function qx(t, e) { + return e && t && e.parentNode !== t + } + + function Kx(t, e, i) { + if (qx(t, e) && i) { + var n = i.nextSibling; + n ? t.insertBefore(e, n) : t.appendChild(e) + } + } + + function $x(t, e) { + if (qx(t, e)) { + var i = t.firstChild; + i ? t.insertBefore(e, i) : t.appendChild(e) + } + } + + function Jx(t, e) { + e && t && e.parentNode === t && t.removeChild(e) + } + + function Qx(t) { + return t.__textSvgEl + } + + function t_(t) { + return t.__svgEl + } + + function e_(t) { + return function () { + ew('In SVG mode painter not support method "' + t + '"') + } + } + + var i_ = 2311, n_ = function () { + return i_++ + }, o_ = {}, a_ = o_ = "object" == typeof wx && "function" == typeof wx.getSystemInfoSync ? { + browser: {}, + os: {}, + node: !1, + wxa: !0, + canvasSupported: !0, + svgSupported: !1, + touchEventsSupported: !0 + } : "undefined" == typeof document && "undefined" != typeof self ? { + browser: {}, + os: {}, + node: !1, + worker: !0, + canvasSupported: !0 + } : "undefined" == typeof navigator ? { + browser: {}, + os: {}, + node: !0, + worker: !1, + canvasSupported: !0, + svgSupported: !0 + } : function (t) { + var e = {}, i = {}, n = t.match(/Firefox\/([\d.]+)/), + o = t.match(/MSIE\s([\d.]+)/) || t.match(/Trident\/.+?rv:(([\d.]+))/), a = t.match(/Edge\/([\d.]+)/), + r = /micromessenger/i.test(t); + return n && (i.firefox = !0, i.version = n[1]), o && (i.ie = !0, i.version = o[1]), a && (i.edge = !0, i.version = a[1]), r && (i.weChat = !0), { + browser: i, + os: e, + node: !1, + canvasSupported: !!document.createElement("canvas").getContext, + svgSupported: "undefined" != typeof SVGRect, + touchEventsSupported: "ontouchstart" in window && !i.ie && !i.edge, + pointerEventsSupported: "onpointerdown" in window && (i.edge || i.ie && i.version >= 11) + } + }(navigator.userAgent), r_ = { + "[object Function]": 1, + "[object RegExp]": 1, + "[object Date]": 1, + "[object Error]": 1, + "[object CanvasGradient]": 1, + "[object CanvasPattern]": 1, + "[object Image]": 1, + "[object Canvas]": 1 + }, s_ = { + "[object Int8Array]": 1, + "[object Uint8Array]": 1, + "[object Uint8ClampedArray]": 1, + "[object Int16Array]": 1, + "[object Uint16Array]": 1, + "[object Int32Array]": 1, + "[object Uint32Array]": 1, + "[object Float32Array]": 1, + "[object Float64Array]": 1 + }, l_ = Object.prototype.toString, u_ = Array.prototype, h_ = u_.forEach, c_ = u_.filter, d_ = u_.slice, + f_ = u_.map, p_ = u_.reduce, g_ = {}, m_ = function () { + return g_.createCanvas() + }; + g_.createCanvas = function () { + return document.createElement("canvas") + }; + var v_, y_ = "__ec_primitive__"; + E.prototype = { + constructor: E, get: function (t) { + return this.hasOwnProperty(t) ? this[t] : null + }, set: function (t, e) { + return this[t] = e + }, each: function (t, e) { + void 0 !== e && (t = m(t, e)); + for (var i in this) this.hasOwnProperty(i) && t(this[i], i) + }, removeKey: function (t) { + delete this[t] + } + }; + var x_ = (Object.freeze || Object)({ + $override: e, + clone: i, + merge: n, + mergeAll: o, + extend: a, + defaults: r, + createCanvas: m_, + getContext: s, + indexOf: l, + inherits: u, + mixin: h, + isArrayLike: c, + each: d, + map: f, + reduce: p, + filter: g, + find: function (t, e, i) { + if (t && e) for (var n = 0, o = t.length; n < o; n++) if (e.call(i, t[n], n, t)) return t[n] + }, + bind: m, + curry: v, + isArray: y, + isFunction: x, + isString: _, + isObject: w, + isBuiltInObject: b, + isTypedArray: S, + isDom: M, + eqNaN: I, + retrieve: T, + retrieve2: D, + retrieve3: A, + slice: C, + normalizeCssArray: L, + assert: k, + trim: P, + setAsPrimitive: N, + isPrimitive: O, + createHashMap: R, + concatArray: z, + noop: B + }), __ = "undefined" == typeof Float32Array ? Array : Float32Array, w_ = X, b_ = j, S_ = K, M_ = $, + I_ = (Object.freeze || Object)({ + create: V, + copy: G, + clone: W, + set: F, + add: H, + scaleAndAdd: Z, + sub: U, + len: X, + length: w_, + lenSquare: j, + lengthSquare: b_, + mul: function (t, e, i) { + return t[0] = e[0] * i[0], t[1] = e[1] * i[1], t + }, + div: function (t, e, i) { + return t[0] = e[0] / i[0], t[1] = e[1] / i[1], t + }, + dot: function (t, e) { + return t[0] * e[0] + t[1] * e[1] + }, + scale: Y, + normalize: q, + distance: K, + dist: S_, + distanceSquare: $, + distSquare: M_, + negate: function (t, e) { + return t[0] = -e[0], t[1] = -e[1], t + }, + lerp: J, + applyTransform: Q, + min: tt, + max: et + }); + it.prototype = { + constructor: it, _dragStart: function (t) { + var e = t.target; + e && e.draggable && (this._draggingTarget = e, e.dragging = !0, this._x = t.offsetX, this._y = t.offsetY, this.dispatchToElement(nt(e, t), "dragstart", t.event)) + }, _drag: function (t) { + var e = this._draggingTarget; + if (e) { + var i = t.offsetX, n = t.offsetY, o = i - this._x, a = n - this._y; + this._x = i, this._y = n, e.drift(o, a, t), this.dispatchToElement(nt(e, t), "drag", t.event); + var r = this.findHover(i, n, e).target, s = this._dropTarget; + this._dropTarget = r, e !== r && (s && r !== s && this.dispatchToElement(nt(s, t), "dragleave", t.event), r && r !== s && this.dispatchToElement(nt(r, t), "dragenter", t.event)) + } + }, _dragEnd: function (t) { + var e = this._draggingTarget; + e && (e.dragging = !1), this.dispatchToElement(nt(e, t), "dragend", t.event), this._dropTarget && this.dispatchToElement(nt(this._dropTarget, t), "drop", t.event), this._draggingTarget = null, this._dropTarget = null + } + }; + var T_ = Array.prototype.slice, D_ = function () { + this._$handlers = {} + }; + D_.prototype = { + constructor: D_, one: function (t, e, i) { + var n = this._$handlers; + if (!e || !t) return this; + n[t] || (n[t] = []); + for (var o = 0; o < n[t].length; o++) if (n[t][o].h === e) return this; + return n[t].push({h: e, one: !0, ctx: i || this}), this + }, on: function (t, e, i) { + var n = this._$handlers; + if (!e || !t) return this; + n[t] || (n[t] = []); + for (var o = 0; o < n[t].length; o++) if (n[t][o].h === e) return this; + return n[t].push({h: e, one: !1, ctx: i || this}), this + }, isSilent: function (t) { + var e = this._$handlers; + return e[t] && e[t].length + }, off: function (t, e) { + var i = this._$handlers; + if (!t) return this._$handlers = {}, this; + if (e) { + if (i[t]) { + for (var n = [], o = 0, a = i[t].length; o < a; o++) i[t][o].h != e && n.push(i[t][o]); + i[t] = n + } + i[t] && 0 === i[t].length && delete i[t] + } else delete i[t]; + return this + }, trigger: function (t) { + if (this._$handlers[t]) { + var e = arguments, i = e.length; + i > 3 && (e = T_.call(e, 1)); + for (var n = this._$handlers[t], o = n.length, a = 0; a < o;) { + switch (i) { + case 1: + n[a].h.call(n[a].ctx); + break; + case 2: + n[a].h.call(n[a].ctx, e[1]); + break; + case 3: + n[a].h.call(n[a].ctx, e[1], e[2]); + break; + default: + n[a].h.apply(n[a].ctx, e) + } + n[a].one ? (n.splice(a, 1), o--) : a++ + } + } + return this + }, triggerWithContext: function (t) { + if (this._$handlers[t]) { + var e = arguments, i = e.length; + i > 4 && (e = T_.call(e, 1, e.length - 1)); + for (var n = e[e.length - 1], o = this._$handlers[t], a = o.length, r = 0; r < a;) { + switch (i) { + case 1: + o[r].h.call(n); + break; + case 2: + o[r].h.call(n, e[1]); + break; + case 3: + o[r].h.call(n, e[1], e[2]); + break; + default: + o[r].h.apply(n, e) + } + o[r].one ? (o.splice(r, 1), a--) : r++ + } + } + return this + } + }; + var A_ = "silent"; + at.prototype.dispose = function () { + }; + var C_ = ["click", "dblclick", "mousewheel", "mouseout", "mouseup", "mousedown", "mousemove", "contextmenu"], + L_ = function (t, e, i, n) { + D_.call(this), this.storage = t, this.painter = e, this.painterRoot = n, i = i || new at, this.proxy = null, this._hovered = {}, this._lastTouchMoment, this._lastX, this._lastY, it.call(this), this.setHandlerProxy(i) + }; + L_.prototype = { + constructor: L_, setHandlerProxy: function (t) { + this.proxy && this.proxy.dispose(), t && (d(C_, function (e) { + t.on && t.on(e, this[e], this) + }, this), t.handler = this), this.proxy = t + }, mousemove: function (t) { + var e = t.zrX, i = t.zrY, n = this._hovered, o = n.target; + o && !o.__zr && (o = (n = this.findHover(n.x, n.y)).target); + var a = this._hovered = this.findHover(e, i), r = a.target, s = this.proxy; + s.setCursor && s.setCursor(r ? r.cursor : "default"), o && r !== o && this.dispatchToElement(n, "mouseout", t), this.dispatchToElement(a, "mousemove", t), r && r !== o && this.dispatchToElement(a, "mouseover", t) + }, mouseout: function (t) { + this.dispatchToElement(this._hovered, "mouseout", t); + var e, i = t.toElement || t.relatedTarget; + do { + i = i && i.parentNode + } while (i && 9 != i.nodeType && !(e = i === this.painterRoot)); + !e && this.trigger("globalout", {event: t}) + }, resize: function (t) { + this._hovered = {} + }, dispatch: function (t, e) { + var i = this[t]; + i && i.call(this, e) + }, dispose: function () { + this.proxy.dispose(), this.storage = this.proxy = this.painter = null + }, setCursorStyle: function (t) { + var e = this.proxy; + e.setCursor && e.setCursor(t) + }, dispatchToElement: function (t, e, i) { + var n = (t = t || {}).target; + if (!n || !n.silent) { + for (var o = "on" + e, a = ot(e, t, i); n && (n[o] && (a.cancelBubble = n[o].call(n, a)), n.trigger(e, a), n = n.parent, !a.cancelBubble);) ; + a.cancelBubble || (this.trigger(e, a), this.painter && this.painter.eachOtherLayer(function (t) { + "function" == typeof t[o] && t[o].call(t, a), t.trigger && t.trigger(e, a) + })) + } + }, findHover: function (t, e, i) { + for (var n = this.storage.getDisplayList(), o = {x: t, y: e}, a = n.length - 1; a >= 0; a--) { + var r; + if (n[a] !== i && !n[a].ignore && (r = rt(n[a], t, e)) && (!o.topTarget && (o.topTarget = n[a]), r !== A_)) { + o.target = n[a]; + break + } + } + return o + } + }, d(["click", "mousedown", "mouseup", "mousewheel", "dblclick", "contextmenu"], function (t) { + L_.prototype[t] = function (e) { + var i = this.findHover(e.zrX, e.zrY), n = i.target; + if ("mousedown" === t) this._downEl = n, this._downPoint = [e.zrX, e.zrY], this._upEl = n; else if ("mouseup" === t) this._upEl = n; else if ("click" === t) { + if (this._downEl !== this._upEl || !this._downPoint || S_(this._downPoint, [e.zrX, e.zrY]) > 4) return; + this._downPoint = null + } + this.dispatchToElement(i, t, e) + } + }), h(L_, D_), h(L_, it); + var k_ = "undefined" == typeof Float32Array ? Array : Float32Array, P_ = (Object.freeze || Object)({ + create: st, + identity: lt, + copy: ut, + mul: ht, + translate: ct, + rotate: dt, + scale: ft, + invert: pt, + clone: gt + }), N_ = lt, O_ = 5e-5, E_ = function (t) { + (t = t || {}).position || (this.position = [0, 0]), null == t.rotation && (this.rotation = 0), t.scale || (this.scale = [1, 1]), this.origin = this.origin || null + }, R_ = E_.prototype; + R_.transform = null, R_.needLocalTransform = function () { + return mt(this.rotation) || mt(this.position[0]) || mt(this.position[1]) || mt(this.scale[0] - 1) || mt(this.scale[1] - 1) + }, R_.updateTransform = function () { + var t = this.parent, e = t && t.transform, i = this.needLocalTransform(), n = this.transform; + i || e ? (n = n || st(), i ? this.getLocalTransform(n) : N_(n), e && (i ? ht(n, t.transform, n) : ut(n, t.transform)), this.transform = n, this.invTransform = this.invTransform || st(), pt(this.invTransform, n)) : n && N_(n) + }, R_.getLocalTransform = function (t) { + return E_.getLocalTransform(this, t) + }, R_.setTransform = function (t) { + var e = this.transform, i = t.dpr || 1; + e ? t.setTransform(i * e[0], i * e[1], i * e[2], i * e[3], i * e[4], i * e[5]) : t.setTransform(i, 0, 0, i, 0, 0) + }, R_.restoreTransform = function (t) { + var e = t.dpr || 1; + t.setTransform(e, 0, 0, e, 0, 0) + }; + var z_ = []; + R_.decomposeTransform = function () { + if (this.transform) { + var t = this.parent, e = this.transform; + t && t.transform && (ht(z_, t.invTransform, e), e = z_); + var i = e[0] * e[0] + e[1] * e[1], n = e[2] * e[2] + e[3] * e[3], o = this.position, a = this.scale; + mt(i - 1) && (i = Math.sqrt(i)), mt(n - 1) && (n = Math.sqrt(n)), e[0] < 0 && (i = -i), e[3] < 0 && (n = -n), o[0] = e[4], o[1] = e[5], a[0] = i, a[1] = n, this.rotation = Math.atan2(-e[1] / n, e[0] / i) + } + }, R_.getGlobalScale = function () { + var t = this.transform; + if (!t) return [1, 1]; + var e = Math.sqrt(t[0] * t[0] + t[1] * t[1]), i = Math.sqrt(t[2] * t[2] + t[3] * t[3]); + return t[0] < 0 && (e = -e), t[3] < 0 && (i = -i), [e, i] + }, R_.transformCoordToLocal = function (t, e) { + var i = [t, e], n = this.invTransform; + return n && Q(i, i, n), i + }, R_.transformCoordToGlobal = function (t, e) { + var i = [t, e], n = this.transform; + return n && Q(i, i, n), i + }, E_.getLocalTransform = function (t, e) { + N_(e = e || []); + var i = t.origin, n = t.scale || [1, 1], o = t.rotation || 0, a = t.position || [0, 0]; + return i && (e[4] -= i[0], e[5] -= i[1]), ft(e, e, n), o && dt(e, e, o), i && (e[4] += i[0], e[5] += i[1]), e[4] += a[0], e[5] += a[1], e + }; + var B_ = { + linear: function (t) { + return t + }, quadraticIn: function (t) { + return t * t + }, quadraticOut: function (t) { + return t * (2 - t) + }, quadraticInOut: function (t) { + return (t *= 2) < 1 ? .5 * t * t : -.5 * (--t * (t - 2) - 1) + }, cubicIn: function (t) { + return t * t * t + }, cubicOut: function (t) { + return --t * t * t + 1 + }, cubicInOut: function (t) { + return (t *= 2) < 1 ? .5 * t * t * t : .5 * ((t -= 2) * t * t + 2) + }, quarticIn: function (t) { + return t * t * t * t + }, quarticOut: function (t) { + return 1 - --t * t * t * t + }, quarticInOut: function (t) { + return (t *= 2) < 1 ? .5 * t * t * t * t : -.5 * ((t -= 2) * t * t * t - 2) + }, quinticIn: function (t) { + return t * t * t * t * t + }, quinticOut: function (t) { + return --t * t * t * t * t + 1 + }, quinticInOut: function (t) { + return (t *= 2) < 1 ? .5 * t * t * t * t * t : .5 * ((t -= 2) * t * t * t * t + 2) + }, sinusoidalIn: function (t) { + return 1 - Math.cos(t * Math.PI / 2) + }, sinusoidalOut: function (t) { + return Math.sin(t * Math.PI / 2) + }, sinusoidalInOut: function (t) { + return .5 * (1 - Math.cos(Math.PI * t)) + }, exponentialIn: function (t) { + return 0 === t ? 0 : Math.pow(1024, t - 1) + }, exponentialOut: function (t) { + return 1 === t ? 1 : 1 - Math.pow(2, -10 * t) + }, exponentialInOut: function (t) { + return 0 === t ? 0 : 1 === t ? 1 : (t *= 2) < 1 ? .5 * Math.pow(1024, t - 1) : .5 * (2 - Math.pow(2, -10 * (t - 1))) + }, circularIn: function (t) { + return 1 - Math.sqrt(1 - t * t) + }, circularOut: function (t) { + return Math.sqrt(1 - --t * t) + }, circularInOut: function (t) { + return (t *= 2) < 1 ? -.5 * (Math.sqrt(1 - t * t) - 1) : .5 * (Math.sqrt(1 - (t -= 2) * t) + 1) + }, elasticIn: function (t) { + var e, i = .1; + return 0 === t ? 0 : 1 === t ? 1 : (!i || i < 1 ? (i = 1, e = .1) : e = .4 * Math.asin(1 / i) / (2 * Math.PI), -i * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - e) * (2 * Math.PI) / .4)) + }, elasticOut: function (t) { + var e, i = .1; + return 0 === t ? 0 : 1 === t ? 1 : (!i || i < 1 ? (i = 1, e = .1) : e = .4 * Math.asin(1 / i) / (2 * Math.PI), i * Math.pow(2, -10 * t) * Math.sin((t - e) * (2 * Math.PI) / .4) + 1) + }, elasticInOut: function (t) { + var e, i = .1; + return 0 === t ? 0 : 1 === t ? 1 : (!i || i < 1 ? (i = 1, e = .1) : e = .4 * Math.asin(1 / i) / (2 * Math.PI), (t *= 2) < 1 ? i * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - e) * (2 * Math.PI) / .4) * -.5 : i * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - e) * (2 * Math.PI) / .4) * .5 + 1) + }, backIn: function (t) { + var e = 1.70158; + return t * t * ((e + 1) * t - e) + }, backOut: function (t) { + var e = 1.70158; + return --t * t * ((e + 1) * t + e) + 1 + }, backInOut: function (t) { + var e = 2.5949095; + return (t *= 2) < 1 ? t * t * ((e + 1) * t - e) * .5 : .5 * ((t -= 2) * t * ((e + 1) * t + e) + 2) + }, bounceIn: function (t) { + return 1 - B_.bounceOut(1 - t) + }, bounceOut: function (t) { + return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375 + }, bounceInOut: function (t) { + return t < .5 ? .5 * B_.bounceIn(2 * t) : .5 * B_.bounceOut(2 * t - 1) + .5 + } + }; + vt.prototype = { + constructor: vt, step: function (t, e) { + if (this._initialized || (this._startTime = t + this._delay, this._initialized = !0), this._paused) this._pausedTime += e; else { + var i = (t - this._startTime - this._pausedTime) / this._life; + if (!(i < 0)) { + i = Math.min(i, 1); + var n = this.easing, o = "string" == typeof n ? B_[n] : n, a = "function" == typeof o ? o(i) : i; + return this.fire("frame", a), 1 == i ? this.loop ? (this.restart(t), "restart") : (this._needsRemove = !0, "destroy") : null + } + } + }, restart: function (t) { + var e = (t - this._startTime - this._pausedTime) % this._life; + this._startTime = t - e + this.gap, this._pausedTime = 0, this._needsRemove = !1 + }, fire: function (t, e) { + this[t = "on" + t] && this[t](this._target, e) + }, pause: function () { + this._paused = !0 + }, resume: function () { + this._paused = !1 + } + }; + var V_ = function () { + this.head = null, this.tail = null, this._len = 0 + }, G_ = V_.prototype; + G_.insert = function (t) { + var e = new W_(t); + return this.insertEntry(e), e + }, G_.insertEntry = function (t) { + this.head ? (this.tail.next = t, t.prev = this.tail, t.next = null, this.tail = t) : this.head = this.tail = t, this._len++ + }, G_.remove = function (t) { + var e = t.prev, i = t.next; + e ? e.next = i : this.head = i, i ? i.prev = e : this.tail = e, t.next = t.prev = null, this._len-- + }, G_.len = function () { + return this._len + }, G_.clear = function () { + this.head = this.tail = null, this._len = 0 + }; + var W_ = function (t) { + this.value = t, this.next, this.prev + }, F_ = function (t) { + this._list = new V_, this._map = {}, this._maxSize = t || 10, this._lastRemovedEntry = null + }, H_ = F_.prototype; + H_.put = function (t, e) { + var i = this._list, n = this._map, o = null; + if (null == n[t]) { + var a = i.len(), r = this._lastRemovedEntry; + if (a >= this._maxSize && a > 0) { + var s = i.head; + i.remove(s), delete n[s.key], o = s.value, this._lastRemovedEntry = s + } + r ? r.value = e : r = new W_(e), r.key = t, i.insertEntry(r), n[t] = r + } + return o + }, H_.get = function (t) { + var e = this._map[t], i = this._list; + if (null != e) return e !== i.tail && (i.remove(e), i.insertEntry(e)), e.value + }, H_.clear = function () { + this._list.clear(), this._map = {} + }; + var Z_ = { + transparent: [0, 0, 0, 0], + aliceblue: [240, 248, 255, 1], + antiquewhite: [250, 235, 215, 1], + aqua: [0, 255, 255, 1], + aquamarine: [127, 255, 212, 1], + azure: [240, 255, 255, 1], + beige: [245, 245, 220, 1], + bisque: [255, 228, 196, 1], + black: [0, 0, 0, 1], + blanchedalmond: [255, 235, 205, 1], + blue: [0, 0, 255, 1], + blueviolet: [138, 43, 226, 1], + brown: [165, 42, 42, 1], + burlywood: [222, 184, 135, 1], + cadetblue: [95, 158, 160, 1], + chartreuse: [127, 255, 0, 1], + chocolate: [210, 105, 30, 1], + coral: [255, 127, 80, 1], + cornflowerblue: [100, 149, 237, 1], + cornsilk: [255, 248, 220, 1], + crimson: [220, 20, 60, 1], + cyan: [0, 255, 255, 1], + darkblue: [0, 0, 139, 1], + darkcyan: [0, 139, 139, 1], + darkgoldenrod: [184, 134, 11, 1], + darkgray: [169, 169, 169, 1], + darkgreen: [0, 100, 0, 1], + darkgrey: [169, 169, 169, 1], + darkkhaki: [189, 183, 107, 1], + darkmagenta: [139, 0, 139, 1], + darkolivegreen: [85, 107, 47, 1], + darkorange: [255, 140, 0, 1], + darkorchid: [153, 50, 204, 1], + darkred: [139, 0, 0, 1], + darksalmon: [233, 150, 122, 1], + darkseagreen: [143, 188, 143, 1], + darkslateblue: [72, 61, 139, 1], + darkslategray: [47, 79, 79, 1], + darkslategrey: [47, 79, 79, 1], + darkturquoise: [0, 206, 209, 1], + darkviolet: [148, 0, 211, 1], + deeppink: [255, 20, 147, 1], + deepskyblue: [0, 191, 255, 1], + dimgray: [105, 105, 105, 1], + dimgrey: [105, 105, 105, 1], + dodgerblue: [30, 144, 255, 1], + firebrick: [178, 34, 34, 1], + floralwhite: [255, 250, 240, 1], + forestgreen: [34, 139, 34, 1], + fuchsia: [255, 0, 255, 1], + gainsboro: [220, 220, 220, 1], + ghostwhite: [248, 248, 255, 1], + gold: [255, 215, 0, 1], + goldenrod: [218, 165, 32, 1], + gray: [128, 128, 128, 1], + green: [0, 128, 0, 1], + greenyellow: [173, 255, 47, 1], + grey: [128, 128, 128, 1], + honeydew: [240, 255, 240, 1], + hotpink: [255, 105, 180, 1], + indianred: [205, 92, 92, 1], + indigo: [75, 0, 130, 1], + ivory: [255, 255, 240, 1], + khaki: [240, 230, 140, 1], + lavender: [230, 230, 250, 1], + lavenderblush: [255, 240, 245, 1], + lawngreen: [124, 252, 0, 1], + lemonchiffon: [255, 250, 205, 1], + lightblue: [173, 216, 230, 1], + lightcoral: [240, 128, 128, 1], + lightcyan: [224, 255, 255, 1], + lightgoldenrodyellow: [250, 250, 210, 1], + lightgray: [211, 211, 211, 1], + lightgreen: [144, 238, 144, 1], + lightgrey: [211, 211, 211, 1], + lightpink: [255, 182, 193, 1], + lightsalmon: [255, 160, 122, 1], + lightseagreen: [32, 178, 170, 1], + lightskyblue: [135, 206, 250, 1], + lightslategray: [119, 136, 153, 1], + lightslategrey: [119, 136, 153, 1], + lightsteelblue: [176, 196, 222, 1], + lightyellow: [255, 255, 224, 1], + lime: [0, 255, 0, 1], + limegreen: [50, 205, 50, 1], + linen: [250, 240, 230, 1], + magenta: [255, 0, 255, 1], + maroon: [128, 0, 0, 1], + mediumaquamarine: [102, 205, 170, 1], + mediumblue: [0, 0, 205, 1], + mediumorchid: [186, 85, 211, 1], + mediumpurple: [147, 112, 219, 1], + mediumseagreen: [60, 179, 113, 1], + mediumslateblue: [123, 104, 238, 1], + mediumspringgreen: [0, 250, 154, 1], + mediumturquoise: [72, 209, 204, 1], + mediumvioletred: [199, 21, 133, 1], + midnightblue: [25, 25, 112, 1], + mintcream: [245, 255, 250, 1], + mistyrose: [255, 228, 225, 1], + moccasin: [255, 228, 181, 1], + navajowhite: [255, 222, 173, 1], + navy: [0, 0, 128, 1], + oldlace: [253, 245, 230, 1], + olive: [128, 128, 0, 1], + olivedrab: [107, 142, 35, 1], + orange: [255, 165, 0, 1], + orangered: [255, 69, 0, 1], + orchid: [218, 112, 214, 1], + palegoldenrod: [238, 232, 170, 1], + palegreen: [152, 251, 152, 1], + paleturquoise: [175, 238, 238, 1], + palevioletred: [219, 112, 147, 1], + papayawhip: [255, 239, 213, 1], + peachpuff: [255, 218, 185, 1], + peru: [205, 133, 63, 1], + pink: [255, 192, 203, 1], + plum: [221, 160, 221, 1], + powderblue: [176, 224, 230, 1], + purple: [128, 0, 128, 1], + red: [255, 0, 0, 1], + rosybrown: [188, 143, 143, 1], + royalblue: [65, 105, 225, 1], + saddlebrown: [139, 69, 19, 1], + salmon: [250, 128, 114, 1], + sandybrown: [244, 164, 96, 1], + seagreen: [46, 139, 87, 1], + seashell: [255, 245, 238, 1], + sienna: [160, 82, 45, 1], + silver: [192, 192, 192, 1], + skyblue: [135, 206, 235, 1], + slateblue: [106, 90, 205, 1], + slategray: [112, 128, 144, 1], + slategrey: [112, 128, 144, 1], + snow: [255, 250, 250, 1], + springgreen: [0, 255, 127, 1], + steelblue: [70, 130, 180, 1], + tan: [210, 180, 140, 1], + teal: [0, 128, 128, 1], + thistle: [216, 191, 216, 1], + tomato: [255, 99, 71, 1], + turquoise: [64, 224, 208, 1], + violet: [238, 130, 238, 1], + wheat: [245, 222, 179, 1], + white: [255, 255, 255, 1], + whitesmoke: [245, 245, 245, 1], + yellow: [255, 255, 0, 1], + yellowgreen: [154, 205, 50, 1] + }, U_ = new F_(20), X_ = null, j_ = Nt, Y_ = Ot, q_ = (Object.freeze || Object)({ + parse: At, + lift: kt, + toHex: Pt, + fastLerp: Nt, + fastMapToColor: j_, + lerp: Ot, + mapToColor: Y_, + modifyHSL: Et, + modifyAlpha: Rt, + stringify: zt + }), K_ = Array.prototype.slice, $_ = function (t, e, i, n) { + this._tracks = {}, this._target = t, this._loop = e || !1, this._getter = i || Bt, this._setter = n || Vt, this._clipCount = 0, this._delay = 0, this._doneList = [], this._onframeList = [], this._clipList = [] + }; + $_.prototype = { + when: function (t, e) { + var i = this._tracks; + for (var n in e) if (e.hasOwnProperty(n)) { + if (!i[n]) { + i[n] = []; + var o = this._getter(this._target, n); + if (null == o) continue; + 0 !== t && i[n].push({time: 0, value: jt(o)}) + } + i[n].push({time: t, value: e[n]}) + } + return this + }, during: function (t) { + return this._onframeList.push(t), this + }, pause: function () { + for (var t = 0; t < this._clipList.length; t++) this._clipList[t].pause(); + this._paused = !0 + }, resume: function () { + for (var t = 0; t < this._clipList.length; t++) this._clipList[t].resume(); + this._paused = !1 + }, isPaused: function () { + return !!this._paused + }, _doneCallback: function () { + this._tracks = {}, this._clipList.length = 0; + for (var t = this._doneList, e = t.length, i = 0; i < e; i++) t[i].call(this) + }, start: function (t, e) { + var i, n = this, o = 0; + for (var a in this._tracks) if (this._tracks.hasOwnProperty(a)) { + var r = Kt(this, t, function () { + --o || n._doneCallback() + }, this._tracks[a], a, e); + r && (this._clipList.push(r), o++, this.animation && this.animation.addClip(r), i = r) + } + if (i) { + var s = i.onframe; + i.onframe = function (t, e) { + s(t, e); + for (var i = 0; i < n._onframeList.length; i++) n._onframeList[i](t, e) + } + } + return o || this._doneCallback(), this + }, stop: function (t) { + for (var e = this._clipList, i = this.animation, n = 0; n < e.length; n++) { + var o = e[n]; + t && o.onframe(this._target, 1), i && i.removeClip(o) + } + e.length = 0 + }, delay: function (t) { + return this._delay = t, this + }, done: function (t) { + return t && this._doneList.push(t), this + }, getClips: function () { + return this._clipList + } + }; + var J_ = 1; + "undefined" != typeof window && (J_ = Math.max(window.devicePixelRatio || 1, 1)); + var Q_ = J_, tw = function () { + }, ew = tw, iw = function () { + this.animators = [] + }; + iw.prototype = { + constructor: iw, animate: function (t, e) { + var i, n = !1, o = this, a = this.__zr; + if (t) { + var r = t.split("."), s = o; + n = "shape" === r[0]; + for (var u = 0, h = r.length; u < h; u++) s && (s = s[r[u]]); + s && (i = s) + } else i = o; + if (i) { + var c = o.animators, d = new $_(i, e); + return d.during(function (t) { + o.dirty(n) + }).done(function () { + c.splice(l(c, d), 1) + }), c.push(d), a && a.animation.addAnimator(d), d + } + ew('Property "' + t + '" is not existed in element ' + o.id) + }, stopAnimation: function (t) { + for (var e = this.animators, i = e.length, n = 0; n < i; n++) e[n].stop(t); + return e.length = 0, this + }, animateTo: function (t, e, i, n, o, a) { + _(i) ? (o = n, n = i, i = 0) : x(n) ? (o = n, n = "linear", i = 0) : x(i) ? (o = i, i = 0) : x(e) ? (o = e, e = 500) : e || (e = 500), this.stopAnimation(), this._animateToShallow("", this, t, e, i); + var r = this.animators.slice(), s = r.length; + s || o && o(); + for (var l = 0; l < r.length; l++) r[l].done(function () { + --s || o && o() + }).start(n, a) + }, _animateToShallow: function (t, e, i, n, o) { + var a = {}, r = 0; + for (var s in i) if (i.hasOwnProperty(s)) if (null != e[s]) w(i[s]) && !c(i[s]) ? this._animateToShallow(t ? t + "." + s : s, e[s], i[s], n, o) : (a[s] = i[s], r++); else if (null != i[s]) if (t) { + var l = {}; + l[t] = {}, l[t][s] = i[s], this.attr(l) + } else this.attr(s, i[s]); + return r > 0 && this.animate(t, !1).when(null == n ? 500 : n, a).delay(o || 0), this + } + }; + var nw = function (t) { + E_.call(this, t), D_.call(this, t), iw.call(this, t), this.id = t.id || n_() + }; + nw.prototype = { + type: "element", name: "", __zr: null, ignore: !1, clipPath: null, isGroup: !1, drift: function (t, e) { + switch (this.draggable) { + case"horizontal": + e = 0; + break; + case"vertical": + t = 0 + } + var i = this.transform; + i || (i = this.transform = [1, 0, 0, 1, 0, 0]), i[4] += t, i[5] += e, this.decomposeTransform(), this.dirty(!1) + }, beforeUpdate: function () { + }, afterUpdate: function () { + }, update: function () { + this.updateTransform() + }, traverse: function (t, e) { + }, attrKV: function (t, e) { + if ("position" === t || "scale" === t || "origin" === t) { + if (e) { + var i = this[t]; + i || (i = this[t] = []), i[0] = e[0], i[1] = e[1] + } + } else this[t] = e + }, hide: function () { + this.ignore = !0, this.__zr && this.__zr.refresh() + }, show: function () { + this.ignore = !1, this.__zr && this.__zr.refresh() + }, attr: function (t, e) { + if ("string" == typeof t) this.attrKV(t, e); else if (w(t)) for (var i in t) t.hasOwnProperty(i) && this.attrKV(i, t[i]); + return this.dirty(!1), this + }, setClipPath: function (t) { + var e = this.__zr; + e && t.addSelfToZr(e), this.clipPath && this.clipPath !== t && this.removeClipPath(), this.clipPath = t, t.__zr = e, t.__clipTarget = this, this.dirty(!1) + }, removeClipPath: function () { + var t = this.clipPath; + t && (t.__zr && t.removeSelfFromZr(t.__zr), t.__zr = null, t.__clipTarget = null, this.clipPath = null, this.dirty(!1)) + }, addSelfToZr: function (t) { + this.__zr = t; + var e = this.animators; + if (e) for (var i = 0; i < e.length; i++) t.animation.addAnimator(e[i]); + this.clipPath && this.clipPath.addSelfToZr(t) + }, removeSelfFromZr: function (t) { + this.__zr = null; + var e = this.animators; + if (e) for (var i = 0; i < e.length; i++) t.animation.removeAnimator(e[i]); + this.clipPath && this.clipPath.removeSelfFromZr(t) + } + }, h(nw, iw), h(nw, E_), h(nw, D_); + var ow = Q, aw = Math.min, rw = Math.max; + $t.prototype = { + constructor: $t, union: function (t) { + var e = aw(t.x, this.x), i = aw(t.y, this.y); + this.width = rw(t.x + t.width, this.x + this.width) - e, this.height = rw(t.y + t.height, this.y + this.height) - i, this.x = e, this.y = i + }, applyTransform: function () { + var t = [], e = [], i = [], n = []; + return function (o) { + if (o) { + t[0] = i[0] = this.x, t[1] = n[1] = this.y, e[0] = n[0] = this.x + this.width, e[1] = i[1] = this.y + this.height, ow(t, t, o), ow(e, e, o), ow(i, i, o), ow(n, n, o), this.x = aw(t[0], e[0], i[0], n[0]), this.y = aw(t[1], e[1], i[1], n[1]); + var a = rw(t[0], e[0], i[0], n[0]), r = rw(t[1], e[1], i[1], n[1]); + this.width = a - this.x, this.height = r - this.y + } + } + }(), calculateTransform: function (t) { + var e = this, i = t.width / e.width, n = t.height / e.height, o = st(); + return ct(o, o, [-e.x, -e.y]), ft(o, o, [i, n]), ct(o, o, [t.x, t.y]), o + }, intersect: function (t) { + if (!t) return !1; + t instanceof $t || (t = $t.create(t)); + var e = this, i = e.x, n = e.x + e.width, o = e.y, a = e.y + e.height, r = t.x, s = t.x + t.width, l = t.y, + u = t.y + t.height; + return !(n < r || s < i || a < l || u < o) + }, contain: function (t, e) { + var i = this; + return t >= i.x && t <= i.x + i.width && e >= i.y && e <= i.y + i.height + }, clone: function () { + return new $t(this.x, this.y, this.width, this.height) + }, copy: function (t) { + this.x = t.x, this.y = t.y, this.width = t.width, this.height = t.height + }, plain: function () { + return {x: this.x, y: this.y, width: this.width, height: this.height} + } + }, $t.create = function (t) { + return new $t(t.x, t.y, t.width, t.height) + }; + var sw = function (t) { + t = t || {}, nw.call(this, t); + for (var e in t) t.hasOwnProperty(e) && (this[e] = t[e]); + this._children = [], this.__storage = null, this.__dirty = !0 + }; + sw.prototype = { + constructor: sw, isGroup: !0, type: "group", silent: !1, children: function () { + return this._children.slice() + }, childAt: function (t) { + return this._children[t] + }, childOfName: function (t) { + for (var e = this._children, i = 0; i < e.length; i++) if (e[i].name === t) return e[i] + }, childCount: function () { + return this._children.length + }, add: function (t) { + return t && t !== this && t.parent !== this && (this._children.push(t), this._doAdd(t)), this + }, addBefore: function (t, e) { + if (t && t !== this && t.parent !== this && e && e.parent === this) { + var i = this._children, n = i.indexOf(e); + n >= 0 && (i.splice(n, 0, t), this._doAdd(t)) + } + return this + }, _doAdd: function (t) { + t.parent && t.parent.remove(t), t.parent = this; + var e = this.__storage, i = this.__zr; + e && e !== t.__storage && (e.addToStorage(t), t instanceof sw && t.addChildrenToStorage(e)), i && i.refresh() + }, remove: function (t) { + var e = this.__zr, i = this.__storage, n = this._children, o = l(n, t); + return o < 0 ? this : (n.splice(o, 1), t.parent = null, i && (i.delFromStorage(t), t instanceof sw && t.delChildrenFromStorage(i)), e && e.refresh(), this) + }, removeAll: function () { + var t, e, i = this._children, n = this.__storage; + for (e = 0; e < i.length; e++) t = i[e], n && (n.delFromStorage(t), t instanceof sw && t.delChildrenFromStorage(n)), t.parent = null; + return i.length = 0, this + }, eachChild: function (t, e) { + for (var i = this._children, n = 0; n < i.length; n++) { + var o = i[n]; + t.call(e, o, n) + } + return this + }, traverse: function (t, e) { + for (var i = 0; i < this._children.length; i++) { + var n = this._children[i]; + t.call(e, n), "group" === n.type && n.traverse(t, e) + } + return this + }, addChildrenToStorage: function (t) { + for (var e = 0; e < this._children.length; e++) { + var i = this._children[e]; + t.addToStorage(i), i instanceof sw && i.addChildrenToStorage(t) + } + }, delChildrenFromStorage: function (t) { + for (var e = 0; e < this._children.length; e++) { + var i = this._children[e]; + t.delFromStorage(i), i instanceof sw && i.delChildrenFromStorage(t) + } + }, dirty: function () { + return this.__dirty = !0, this.__zr && this.__zr.refresh(), this + }, getBoundingRect: function (t) { + for (var e = null, i = new $t(0, 0, 0, 0), n = t || this._children, o = [], a = 0; a < n.length; a++) { + var r = n[a]; + if (!r.ignore && !r.invisible) { + var s = r.getBoundingRect(), l = r.getLocalTransform(o); + l ? (i.copy(s), i.applyTransform(l), (e = e || i.clone()).union(i)) : (e = e || s.clone()).union(s) + } + } + return e || i + } + }, u(sw, nw); + var lw = 32, uw = 7, hw = function () { + this._roots = [], this._displayList = [], this._displayListLen = 0 + }; + hw.prototype = { + constructor: hw, traverse: function (t, e) { + for (var i = 0; i < this._roots.length; i++) this._roots[i].traverse(t, e) + }, getDisplayList: function (t, e) { + return e = e || !1, t && this.updateDisplayList(e), this._displayList + }, updateDisplayList: function (t) { + this._displayListLen = 0; + for (var e = this._roots, i = this._displayList, n = 0, o = e.length; n < o; n++) this._updateAndAddDisplayable(e[n], null, t); + i.length = this._displayListLen, a_.canvasSupported && ae(i, re) + }, _updateAndAddDisplayable: function (t, e, i) { + if (!t.ignore || i) { + t.beforeUpdate(), t.__dirty && t.update(), t.afterUpdate(); + var n = t.clipPath; + if (n) { + e = e ? e.slice() : []; + for (var o = n, a = t; o;) o.parent = a, o.updateTransform(), e.push(o), a = o, o = o.clipPath + } + if (t.isGroup) { + for (var r = t._children, s = 0; s < r.length; s++) { + var l = r[s]; + t.__dirty && (l.__dirty = !0), this._updateAndAddDisplayable(l, e, i) + } + t.__dirty = !1 + } else t.__clipPaths = e, this._displayList[this._displayListLen++] = t + } + }, addRoot: function (t) { + t.__storage !== this && (t instanceof sw && t.addChildrenToStorage(this), this.addToStorage(t), this._roots.push(t)) + }, delRoot: function (t) { + if (null == t) { + for (i = 0; i < this._roots.length; i++) { + var e = this._roots[i]; + e instanceof sw && e.delChildrenFromStorage(this) + } + return this._roots = [], this._displayList = [], void (this._displayListLen = 0) + } + if (t instanceof Array) for (var i = 0, n = t.length; i < n; i++) this.delRoot(t[i]); else { + var o = l(this._roots, t); + o >= 0 && (this.delFromStorage(t), this._roots.splice(o, 1), t instanceof sw && t.delChildrenFromStorage(this)) + } + }, addToStorage: function (t) { + return t && (t.__storage = this, t.dirty(!1)), this + }, delFromStorage: function (t) { + return t && (t.__storage = null), this + }, dispose: function () { + this._renderList = this._roots = null + }, displayableSortFunc: re + }; + var cw = { + shadowBlur: 1, + shadowOffsetX: 1, + shadowOffsetY: 1, + textShadowBlur: 1, + textShadowOffsetX: 1, + textShadowOffsetY: 1, + textBoxShadowBlur: 1, + textBoxShadowOffsetX: 1, + textBoxShadowOffsetY: 1 + }, dw = function (t, e, i) { + return cw.hasOwnProperty(e) ? i *= t.dpr : i + }, + fw = [["shadowBlur", 0], ["shadowOffsetX", 0], ["shadowOffsetY", 0], ["shadowColor", "#000"], ["lineCap", "butt"], ["lineJoin", "miter"], ["miterLimit", 10]], + pw = function (t, e) { + this.extendFrom(t, !1), this.host = e + }; + pw.prototype = { + constructor: pw, + host: null, + fill: "#000", + stroke: null, + opacity: 1, + lineDash: null, + lineDashOffset: 0, + shadowBlur: 0, + shadowOffsetX: 0, + shadowOffsetY: 0, + lineWidth: 1, + strokeNoScale: !1, + text: null, + font: null, + textFont: null, + fontStyle: null, + fontWeight: null, + fontSize: null, + fontFamily: null, + textTag: null, + textFill: "#000", + textStroke: null, + textWidth: null, + textHeight: null, + textStrokeWidth: 0, + textLineHeight: null, + textPosition: "inside", + textRect: null, + textOffset: null, + textAlign: null, + textVerticalAlign: null, + textDistance: 5, + textShadowColor: "transparent", + textShadowBlur: 0, + textShadowOffsetX: 0, + textShadowOffsetY: 0, + textBoxShadowColor: "transparent", + textBoxShadowBlur: 0, + textBoxShadowOffsetX: 0, + textBoxShadowOffsetY: 0, + transformText: !1, + textRotation: 0, + textOrigin: null, + textBackgroundColor: null, + textBorderColor: null, + textBorderWidth: 0, + textBorderRadius: 0, + textPadding: null, + rich: null, + truncate: null, + blend: null, + bind: function (t, e, i) { + for (var n = this, o = i && i.style, a = !o, r = 0; r < fw.length; r++) { + var s = fw[r], l = s[0]; + (a || n[l] !== o[l]) && (t[l] = dw(t, l, n[l] || s[1])) + } + if ((a || n.fill !== o.fill) && (t.fillStyle = n.fill), (a || n.stroke !== o.stroke) && (t.strokeStyle = n.stroke), (a || n.opacity !== o.opacity) && (t.globalAlpha = null == n.opacity ? 1 : n.opacity), (a || n.blend !== o.blend) && (t.globalCompositeOperation = n.blend || "source-over"), this.hasStroke()) { + var u = n.lineWidth; + t.lineWidth = u / (this.strokeNoScale && e && e.getLineScale ? e.getLineScale() : 1) + } + }, + hasFill: function () { + var t = this.fill; + return null != t && "none" !== t + }, + hasStroke: function () { + var t = this.stroke; + return null != t && "none" !== t && this.lineWidth > 0 + }, + extendFrom: function (t, e) { + if (t) for (var i in t) !t.hasOwnProperty(i) || !0 !== e && (!1 === e ? this.hasOwnProperty(i) : null == t[i]) || (this[i] = t[i]) + }, + set: function (t, e) { + "string" == typeof t ? this[t] = e : this.extendFrom(t, !0) + }, + clone: function () { + var t = new this.constructor; + return t.extendFrom(this, !0), t + }, + getGradient: function (t, e, i) { + for (var n = ("radial" === e.type ? le : se)(t, e, i), o = e.colorStops, a = 0; a < o.length; a++) n.addColorStop(o[a].offset, o[a].color); + return n + } + }; + for (var gw = pw.prototype, mw = 0; mw < fw.length; mw++) { + var vw = fw[mw]; + vw[0] in gw || (gw[vw[0]] = vw[1]) + } + pw.getGradient = gw.getGradient; + var yw = function (t, e) { + this.image = t, this.repeat = e, this.type = "pattern" + }; + yw.prototype.getCanvasPattern = function (t) { + return t.createPattern(this.image, this.repeat || "repeat") + }; + var xw = function (t, e, i) { + var n; + i = i || Q_, "string" == typeof t ? n = he(t, e, i) : w(t) && (t = (n = t).id), this.id = t, this.dom = n; + var o = n.style; + o && (n.onselectstart = ue, o["-webkit-user-select"] = "none", o["user-select"] = "none", o["-webkit-touch-callout"] = "none", o["-webkit-tap-highlight-color"] = "rgba(0,0,0,0)", o.padding = 0, o.margin = 0, o["border-width"] = 0), this.domBack = null, this.ctxBack = null, this.painter = e, this.config = null, this.clearColor = 0, this.motionBlur = !1, this.lastFrameAlpha = .7, this.dpr = i + }; + xw.prototype = { + constructor: xw, + __dirty: !0, + __used: !1, + __drawIndex: 0, + __startIndex: 0, + __endIndex: 0, + incremental: !1, + getElementCount: function () { + return this.__endIndex - this.__startIndex + }, + initContext: function () { + this.ctx = this.dom.getContext("2d"), this.ctx.dpr = this.dpr + }, + createBackBuffer: function () { + var t = this.dpr; + this.domBack = he("back-" + this.id, this.painter, t), this.ctxBack = this.domBack.getContext("2d"), 1 != t && this.ctxBack.scale(t, t) + }, + resize: function (t, e) { + var i = this.dpr, n = this.dom, o = n.style, a = this.domBack; + o && (o.width = t + "px", o.height = e + "px"), n.width = t * i, n.height = e * i, a && (a.width = t * i, a.height = e * i, 1 != i && this.ctxBack.scale(i, i)) + }, + clear: function (t, e) { + var i = this.dom, n = this.ctx, o = i.width, a = i.height, e = e || this.clearColor, + r = this.motionBlur && !t, s = this.lastFrameAlpha, l = this.dpr; + if (r && (this.domBack || this.createBackBuffer(), this.ctxBack.globalCompositeOperation = "copy", this.ctxBack.drawImage(i, 0, 0, o / l, a / l)), n.clearRect(0, 0, o, a), e && "transparent" !== e) { + var u; + e.colorStops ? (u = e.__canvasGradient || pw.getGradient(n, e, { + x: 0, + y: 0, + width: o, + height: a + }), e.__canvasGradient = u) : e.image && (u = yw.prototype.getCanvasPattern.call(e, n)), n.save(), n.fillStyle = u || e, n.fillRect(0, 0, o, a), n.restore() + } + if (r) { + var h = this.domBack; + n.save(), n.globalAlpha = s, n.drawImage(h, 0, 0, o, a), n.restore() + } + } + }; + var _w = "undefined" != typeof window && (window.requestAnimationFrame && window.requestAnimationFrame.bind(window) || window.msRequestAnimationFrame && window.msRequestAnimationFrame.bind(window) || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame) || function (t) { + setTimeout(t, 16) + }, ww = new F_(50), bw = {}, Sw = 0, Mw = 5e3, Iw = /\{([a-zA-Z0-9_]+)\|([^}]*)\}/g, Tw = "12px sans-serif", + Dw = {}; + Dw.measureText = function (t, e) { + var i = s(); + return i.font = e || Tw, i.measureText(t) + }; + var Aw = {left: 1, right: 1, center: 1}, Cw = {top: 1, bottom: 1, middle: 1}, Lw = new $t, kw = function () { + }; + kw.prototype = { + constructor: kw, drawRectText: function (t, e) { + var i = this.style; + e = i.textRect || e, this.__dirty && Ne(i); + var n = i.text; + if (null != n && (n += ""), Ke(n, i)) { + t.save(); + var o = this.transform; + i.transformText ? this.setTransform(t) : o && (Lw.copy(e), Lw.applyTransform(o), e = Lw), Ee(this, t, n, i, e), t.restore() + } + } + }, $e.prototype = { + constructor: $e, + type: "displayable", + __dirty: !0, + invisible: !1, + z: 0, + z2: 0, + zlevel: 0, + draggable: !1, + dragging: !1, + silent: !1, + culling: !1, + cursor: "pointer", + rectHover: !1, + progressive: !1, + incremental: !1, + inplace: !1, + beforeBrush: function (t) { + }, + afterBrush: function (t) { + }, + brush: function (t, e) { + }, + getBoundingRect: function () { + }, + contain: function (t, e) { + return this.rectContain(t, e) + }, + traverse: function (t, e) { + t.call(e, this) + }, + rectContain: function (t, e) { + var i = this.transformCoordToLocal(t, e); + return this.getBoundingRect().contain(i[0], i[1]) + }, + dirty: function () { + this.__dirty = !0, this._rect = null, this.__zr && this.__zr.refresh() + }, + animateStyle: function (t) { + return this.animate("style", t) + }, + attrKV: function (t, e) { + "style" !== t ? nw.prototype.attrKV.call(this, t, e) : this.style.set(e) + }, + setStyle: function (t, e) { + return this.style.set(t, e), this.dirty(!1), this + }, + useStyle: function (t) { + return this.style = new pw(t, this), this.dirty(!1), this + } + }, u($e, nw), h($e, kw), Je.prototype = { + constructor: Je, type: "image", brush: function (t, e) { + var i = this.style, n = i.image; + i.bind(t, this, e); + var o = this._image = de(n, this._image, this, this.onload); + if (o && pe(o)) { + var a = i.x || 0, r = i.y || 0, s = i.width, l = i.height, u = o.width / o.height; + if (null == s && null != l ? s = l * u : null == l && null != s ? l = s / u : null == s && null == l && (s = o.width, l = o.height), this.setTransform(t), i.sWidth && i.sHeight) { + var h = i.sx || 0, c = i.sy || 0; + t.drawImage(o, h, c, i.sWidth, i.sHeight, a, r, s, l) + } else if (i.sx && i.sy) { + var d = s - (h = i.sx), f = l - (c = i.sy); + t.drawImage(o, h, c, d, f, a, r, s, l) + } else t.drawImage(o, a, r, s, l); + null != i.text && (this.restoreTransform(t), this.drawRectText(t, this.getBoundingRect())) + } + }, getBoundingRect: function () { + var t = this.style; + return this._rect || (this._rect = new $t(t.x || 0, t.y || 0, t.width || 0, t.height || 0)), this._rect + } + }, u(Je, $e); + var Pw = new $t(0, 0, 0, 0), Nw = new $t(0, 0, 0, 0), Ow = function (t, e, i) { + this.type = "canvas"; + var n = !t.nodeName || "CANVAS" === t.nodeName.toUpperCase(); + this._opts = i = a({}, i || {}), this.dpr = i.devicePixelRatio || Q_, this._singleCanvas = n, this.root = t; + var o = t.style; + o && (o["-webkit-tap-highlight-color"] = "transparent", o["-webkit-user-select"] = o["user-select"] = o["-webkit-touch-callout"] = "none", t.innerHTML = ""), this.storage = e; + var r = this._zlevelList = [], s = this._layers = {}; + if (this._layerConfig = {}, this._needsManuallyCompositing = !1, n) { + var l = t.width, u = t.height; + null != i.width && (l = i.width), null != i.height && (u = i.height), this.dpr = i.devicePixelRatio || 1, t.width = l * this.dpr, t.height = u * this.dpr, this._width = l, this._height = u; + var h = new xw(t, this, this.dpr); + h.__builtin__ = !0, h.initContext(), s[314159] = h, h.zlevel = 314159, r.push(314159), this._domRoot = t + } else { + this._width = this._getSize(0), this._height = this._getSize(1); + var c = this._domRoot = oi(this._width, this._height); + t.appendChild(c) + } + this._hoverlayer = null, this._hoverElements = [] + }; + Ow.prototype = { + constructor: Ow, getType: function () { + return "canvas" + }, isSingleCanvas: function () { + return this._singleCanvas + }, getViewportRoot: function () { + return this._domRoot + }, getViewportRootOffset: function () { + var t = this.getViewportRoot(); + if (t) return {offsetLeft: t.offsetLeft || 0, offsetTop: t.offsetTop || 0} + }, refresh: function (t) { + var e = this.storage.getDisplayList(!0), i = this._zlevelList; + this._redrawId = Math.random(), this._paintList(e, t, this._redrawId); + for (var n = 0; n < i.length; n++) { + var o = i[n], a = this._layers[o]; + if (!a.__builtin__ && a.refresh) { + var r = 0 === n ? this._backgroundColor : null; + a.refresh(r) + } + } + return this.refreshHover(), this + }, addHover: function (t, e) { + if (!t.__hoverMir) { + var i = new t.constructor({style: t.style, shape: t.shape}); + i.__from = t, t.__hoverMir = i, i.setStyle(e), this._hoverElements.push(i) + } + }, removeHover: function (t) { + var e = t.__hoverMir, i = this._hoverElements, n = l(i, e); + n >= 0 && i.splice(n, 1), t.__hoverMir = null + }, clearHover: function (t) { + for (var e = this._hoverElements, i = 0; i < e.length; i++) { + var n = e[i].__from; + n && (n.__hoverMir = null) + } + e.length = 0 + }, refreshHover: function () { + var t = this._hoverElements, e = t.length, i = this._hoverlayer; + if (i && i.clear(), e) { + ae(t, this.storage.displayableSortFunc), i || (i = this._hoverlayer = this.getLayer(1e5)); + var n = {}; + i.ctx.save(); + for (var o = 0; o < e;) { + var a = t[o], r = a.__from; + r && r.__zr ? (o++, r.invisible || (a.transform = r.transform, a.invTransform = r.invTransform, a.__clipPaths = r.__clipPaths, this._doPaintEl(a, i, !0, n))) : (t.splice(o, 1), r.__hoverMir = null, e--) + } + i.ctx.restore() + } + }, getHoverLayer: function () { + return this.getLayer(1e5) + }, _paintList: function (t, e, i) { + if (this._redrawId === i) { + e = e || !1, this._updateLayerStatus(t); + var n = this._doPaintList(t, e); + if (this._needsManuallyCompositing && this._compositeManually(), !n) { + var o = this; + _w(function () { + o._paintList(t, e, i) + }) + } + } + }, _compositeManually: function () { + var t = this.getLayer(314159).ctx, e = this._domRoot.width, i = this._domRoot.height; + t.clearRect(0, 0, e, i), this.eachBuiltinLayer(function (n) { + n.virtual && t.drawImage(n.dom, 0, 0, e, i) + }) + }, _doPaintList: function (t, e) { + for (var i = [], n = 0; n < this._zlevelList.length; n++) { + var o = this._zlevelList[n]; + (s = this._layers[o]).__builtin__ && s !== this._hoverlayer && (s.__dirty || e) && i.push(s) + } + for (var a = !0, r = 0; r < i.length; r++) { + var s = i[r], l = s.ctx, u = {}; + l.save(); + var h = e ? s.__startIndex : s.__drawIndex, c = !e && s.incremental && Date.now, f = c && Date.now(), + p = s.zlevel === this._zlevelList[0] ? this._backgroundColor : null; + if (s.__startIndex === s.__endIndex) s.clear(!1, p); else if (h === s.__startIndex) { + var g = t[h]; + g.incremental && g.notClear && !e || s.clear(!1, p) + } + -1 === h && (console.error("For some unknown reason. drawIndex is -1"), h = s.__startIndex); + for (var m = h; m < s.__endIndex; m++) { + var v = t[m]; + if (this._doPaintEl(v, s, e, u), v.__dirty = !1, c && Date.now() - f > 15) break + } + s.__drawIndex = m, s.__drawIndex < s.__endIndex && (a = !1), u.prevElClipPaths && l.restore(), l.restore() + } + return a_.wxa && d(this._layers, function (t) { + t && t.ctx && t.ctx.draw && t.ctx.draw() + }), a + }, _doPaintEl: function (t, e, i, n) { + var o = e.ctx, a = t.transform; + if ((e.__dirty || i) && !t.invisible && 0 !== t.style.opacity && (!a || a[0] || a[3]) && (!t.culling || !ei(t, this._width, this._height))) { + var r = t.__clipPaths; + n.prevElClipPaths && !ii(r, n.prevElClipPaths) || (n.prevElClipPaths && (e.ctx.restore(), n.prevElClipPaths = null, n.prevEl = null), r && (o.save(), ni(r, o), n.prevElClipPaths = r)), t.beforeBrush && t.beforeBrush(o), t.brush(o, n.prevEl || null), n.prevEl = t, t.afterBrush && t.afterBrush(o) + } + }, getLayer: function (t, e) { + this._singleCanvas && !this._needsManuallyCompositing && (t = 314159); + var i = this._layers[t]; + return i || ((i = new xw("zr_" + t, this, this.dpr)).zlevel = t, i.__builtin__ = !0, this._layerConfig[t] && n(i, this._layerConfig[t], !0), e && (i.virtual = e), this.insertLayer(t, i), i.initContext()), i + }, insertLayer: function (t, e) { + var i = this._layers, n = this._zlevelList, o = n.length, a = null, r = -1, s = this._domRoot; + if (i[t]) ew("ZLevel " + t + " has been used already"); else if (ti(e)) { + if (o > 0 && t > n[0]) { + for (r = 0; r < o - 1 && !(n[r] < t && n[r + 1] > t); r++) ; + a = i[n[r]] + } + if (n.splice(r + 1, 0, t), i[t] = e, !e.virtual) if (a) { + var l = a.dom; + l.nextSibling ? s.insertBefore(e.dom, l.nextSibling) : s.appendChild(e.dom) + } else s.firstChild ? s.insertBefore(e.dom, s.firstChild) : s.appendChild(e.dom) + } else ew("Layer of zlevel " + t + " is not valid") + }, eachLayer: function (t, e) { + var i, n, o = this._zlevelList; + for (n = 0; n < o.length; n++) i = o[n], t.call(e, this._layers[i], i) + }, eachBuiltinLayer: function (t, e) { + var i, n, o, a = this._zlevelList; + for (o = 0; o < a.length; o++) n = a[o], (i = this._layers[n]).__builtin__ && t.call(e, i, n) + }, eachOtherLayer: function (t, e) { + var i, n, o, a = this._zlevelList; + for (o = 0; o < a.length; o++) n = a[o], (i = this._layers[n]).__builtin__ || t.call(e, i, n) + }, getLayers: function () { + return this._layers + }, _updateLayerStatus: function (t) { + function e(t) { + i && (i.__endIndex !== t && (i.__dirty = !0), i.__endIndex = t) + } + + if (this.eachBuiltinLayer(function (t, e) { + t.__dirty = t.__used = !1 + }), this._singleCanvas) for (o = 1; o < t.length; o++) if ((r = t[o]).zlevel !== t[o - 1].zlevel || r.incremental) { + this._needsManuallyCompositing = !0; + break + } + for (var i = null, n = 0, o = 0; o < t.length; o++) { + var a, r = t[o], s = r.zlevel; + r.incremental ? ((a = this.getLayer(s + .001, this._needsManuallyCompositing)).incremental = !0, n = 1) : a = this.getLayer(s + (n > 0 ? .01 : 0), this._needsManuallyCompositing), a.__builtin__ || ew("ZLevel " + s + " has been used by unkown layer " + a.id), a !== i && (a.__used = !0, a.__startIndex !== o && (a.__dirty = !0), a.__startIndex = o, a.incremental ? a.__drawIndex = -1 : a.__drawIndex = o, e(o), i = a), r.__dirty && (a.__dirty = !0, a.incremental && a.__drawIndex < 0 && (a.__drawIndex = o)) + } + e(o), this.eachBuiltinLayer(function (t, e) { + !t.__used && t.getElementCount() > 0 && (t.__dirty = !0, t.__startIndex = t.__endIndex = t.__drawIndex = 0), t.__dirty && t.__drawIndex < 0 && (t.__drawIndex = t.__startIndex) + }) + }, clear: function () { + return this.eachBuiltinLayer(this._clearLayer), this + }, _clearLayer: function (t) { + t.clear() + }, setBackgroundColor: function (t) { + this._backgroundColor = t + }, configLayer: function (t, e) { + if (e) { + var i = this._layerConfig; + i[t] ? n(i[t], e, !0) : i[t] = e; + for (var o = 0; o < this._zlevelList.length; o++) { + var a = this._zlevelList[o]; + a !== t && a !== t + .01 || n(this._layers[a], i[t], !0) + } + } + }, delLayer: function (t) { + var e = this._layers, i = this._zlevelList, n = e[t]; + n && (n.dom.parentNode.removeChild(n.dom), delete e[t], i.splice(l(i, t), 1)) + }, resize: function (t, e) { + if (this._domRoot.style) { + var i = this._domRoot; + i.style.display = "none"; + var n = this._opts; + if (null != t && (n.width = t), null != e && (n.height = e), t = this._getSize(0), e = this._getSize(1), i.style.display = "", this._width != t || e != this._height) { + i.style.width = t + "px", i.style.height = e + "px"; + for (var o in this._layers) this._layers.hasOwnProperty(o) && this._layers[o].resize(t, e); + d(this._progressiveLayers, function (i) { + i.resize(t, e) + }), this.refresh(!0) + } + this._width = t, this._height = e + } else { + if (null == t || null == e) return; + this._width = t, this._height = e, this.getLayer(314159).resize(t, e) + } + return this + }, clearLayer: function (t) { + var e = this._layers[t]; + e && e.clear() + }, dispose: function () { + this.root.innerHTML = "", this.root = this.storage = this._domRoot = this._layers = null + }, getRenderedCanvas: function (t) { + if (t = t || {}, this._singleCanvas && !this._compositeManually) return this._layers[314159].dom; + var e = new xw("image", this, t.pixelRatio || this.dpr); + if (e.initContext(), e.clear(!1, t.backgroundColor || this._backgroundColor), t.pixelRatio <= this.dpr) { + this.refresh(); + var i = e.dom.width, n = e.dom.height, o = e.ctx; + this.eachLayer(function (t) { + t.__builtin__ ? o.drawImage(t.dom, 0, 0, i, n) : t.renderToCanvas && (e.ctx.save(), t.renderToCanvas(e.ctx), e.ctx.restore()) + }) + } else for (var a = {}, r = this.storage.getDisplayList(!0), s = 0; s < r.length; s++) { + var l = r[s]; + this._doPaintEl(l, e, !0, a) + } + return e.dom + }, getWidth: function () { + return this._width + }, getHeight: function () { + return this._height + }, _getSize: function (t) { + var e = this._opts, i = ["width", "height"][t], n = ["clientWidth", "clientHeight"][t], + o = ["paddingLeft", "paddingTop"][t], a = ["paddingRight", "paddingBottom"][t]; + if (null != e[i] && "auto" !== e[i]) return parseFloat(e[i]); + var r = this.root, s = document.defaultView.getComputedStyle(r); + return (r[n] || Qe(s[i]) || Qe(r.style[i])) - (Qe(s[o]) || 0) - (Qe(s[a]) || 0) | 0 + }, pathToImage: function (t, e) { + e = e || this.dpr; + var i = document.createElement("canvas"), n = i.getContext("2d"), o = t.getBoundingRect(), a = t.style, + r = a.shadowBlur * e, s = a.shadowOffsetX * e, l = a.shadowOffsetY * e, + u = a.hasStroke() ? a.lineWidth : 0, h = Math.max(u / 2, -s + r), c = Math.max(u / 2, s + r), + d = Math.max(u / 2, -l + r), f = Math.max(u / 2, l + r), p = o.width + h + c, g = o.height + d + f; + i.width = p * e, i.height = g * e, n.scale(e, e), n.clearRect(0, 0, p, g), n.dpr = e; + var m = {position: t.position, rotation: t.rotation, scale: t.scale}; + t.position = [h - o.x, d - o.y], t.rotation = 0, t.scale = [1, 1], t.updateTransform(), t && t.brush(n); + var v = new Je({style: {x: 0, y: 0, image: i}}); + return null != m.position && (v.position = t.position = m.position), null != m.rotation && (v.rotation = t.rotation = m.rotation), null != m.scale && (v.scale = t.scale = m.scale), v + } + }; + var Ew = "undefined" != typeof window && !!window.addEventListener, + Rw = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, zw = Ew ? function (t) { + t.preventDefault(), t.stopPropagation(), t.cancelBubble = !0 + } : function (t) { + t.returnValue = !1, t.cancelBubble = !0 + }, Bw = function (t) { + t = t || {}, this.stage = t.stage || {}, this.onframe = t.onframe || function () { + }, this._clips = [], this._running = !1, this._time, this._pausedTime, this._pauseStart, this._paused = !1, D_.call(this) + }; + Bw.prototype = { + constructor: Bw, addClip: function (t) { + this._clips.push(t) + }, addAnimator: function (t) { + t.animation = this; + for (var e = t.getClips(), i = 0; i < e.length; i++) this.addClip(e[i]) + }, removeClip: function (t) { + var e = l(this._clips, t); + e >= 0 && this._clips.splice(e, 1) + }, removeAnimator: function (t) { + for (var e = t.getClips(), i = 0; i < e.length; i++) this.removeClip(e[i]); + t.animation = null + }, _update: function () { + for (var t = (new Date).getTime() - this._pausedTime, e = t - this._time, i = this._clips, n = i.length, o = [], a = [], r = 0; r < n; r++) { + var s = i[r], l = s.step(t, e); + l && (o.push(l), a.push(s)) + } + for (r = 0; r < n;) i[r]._needsRemove ? (i[r] = i[n - 1], i.pop(), n--) : r++; + n = o.length; + for (r = 0; r < n; r++) a[r].fire(o[r]); + this._time = t, this.onframe(e), this.trigger("frame", e), this.stage.update && this.stage.update() + }, _startLoop: function () { + function t() { + e._running && (_w(t), !e._paused && e._update()) + } + + var e = this; + this._running = !0, _w(t) + }, start: function () { + this._time = (new Date).getTime(), this._pausedTime = 0, this._startLoop() + }, stop: function () { + this._running = !1 + }, pause: function () { + this._paused || (this._pauseStart = (new Date).getTime(), this._paused = !0) + }, resume: function () { + this._paused && (this._pausedTime += (new Date).getTime() - this._pauseStart, this._paused = !1) + }, clear: function () { + this._clips = [] + }, isFinished: function () { + return !this._clips.length + }, animate: function (t, e) { + var i = new $_(t, (e = e || {}).loop, e.getter, e.setter); + return this.addAnimator(i), i + } + }, h(Bw, D_); + var Vw = function () { + this._track = [] + }; + Vw.prototype = { + constructor: Vw, recognize: function (t, e, i) { + return this._doTrack(t, e, i), this._recognize(t) + }, clear: function () { + return this._track.length = 0, this + }, _doTrack: function (t, e, i) { + var n = t.touches; + if (n) { + for (var o = {points: [], touches: [], target: e, event: t}, a = 0, r = n.length; a < r; a++) { + var s = n[a], l = ri(i, s, {}); + o.points.push([l.zrX, l.zrY]), o.touches.push(s) + } + this._track.push(o) + } + }, _recognize: function (t) { + for (var e in Gw) if (Gw.hasOwnProperty(e)) { + var i = Gw[e](this._track, t); + if (i) return i + } + } + }; + var Gw = { + pinch: function (t, e) { + var i = t.length; + if (i) { + var n = (t[i - 1] || {}).points, o = (t[i - 2] || {}).points || n; + if (o && o.length > 1 && n && n.length > 1) { + var a = di(n) / di(o); + !isFinite(a) && (a = 1), e.pinchScale = a; + var r = fi(n); + return e.pinchX = r[0], e.pinchY = r[1], {type: "pinch", target: t[0].target, event: e} + } + } + } + }, Ww = ["click", "dblclick", "mousewheel", "mouseout", "mouseup", "mousedown", "mousemove", "contextmenu"], + Fw = ["touchstart", "touchend", "touchmove"], + Hw = {pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1}, Zw = f(Ww, function (t) { + var e = t.replace("mouse", "pointer"); + return Hw[e] ? e : t + }), Uw = { + mousemove: function (t) { + t = li(this.dom, t), this.trigger("mousemove", t) + }, mouseout: function (t) { + var e = (t = li(this.dom, t)).toElement || t.relatedTarget; + if (e != this.dom) for (; e && 9 != e.nodeType;) { + if (e === this.dom) return; + e = e.parentNode + } + this.trigger("mouseout", t) + }, touchstart: function (t) { + (t = li(this.dom, t)).zrByTouch = !0, this._lastTouchMoment = new Date, gi(this, t, "start"), Uw.mousemove.call(this, t), Uw.mousedown.call(this, t), mi(this) + }, touchmove: function (t) { + (t = li(this.dom, t)).zrByTouch = !0, gi(this, t, "change"), Uw.mousemove.call(this, t), mi(this) + }, touchend: function (t) { + (t = li(this.dom, t)).zrByTouch = !0, gi(this, t, "end"), Uw.mouseup.call(this, t), +new Date - this._lastTouchMoment < 300 && Uw.click.call(this, t), mi(this) + }, pointerdown: function (t) { + Uw.mousedown.call(this, t) + }, pointermove: function (t) { + vi(t) || Uw.mousemove.call(this, t) + }, pointerup: function (t) { + Uw.mouseup.call(this, t) + }, pointerout: function (t) { + vi(t) || Uw.mouseout.call(this, t) + } + }; + d(["click", "mousedown", "mouseup", "mousewheel", "dblclick", "contextmenu"], function (t) { + Uw[t] = function (e) { + e = li(this.dom, e), this.trigger(t, e) + } + }); + var Xw = xi.prototype; + Xw.dispose = function () { + for (var t = Ww.concat(Fw), e = 0; e < t.length; e++) { + var i = t[e]; + hi(this.dom, pi(i), this._handlers[i]) + } + }, Xw.setCursor = function (t) { + this.dom.style && (this.dom.style.cursor = t || "default") + }, h(xi, D_); + var jw = !a_.canvasSupported, Yw = {canvas: Ow}, qw = {}, Kw = function (t, e, i) { + i = i || {}, this.dom = e, this.id = t; + var n = this, o = new hw, a = i.renderer; + if (jw) { + if (!Yw.vml) throw new Error("You need to require 'zrender/vml/vml' to support IE8"); + a = "vml" + } else a && Yw[a] || (a = "canvas"); + var r = new Yw[a](e, o, i, t); + this.storage = o, this.painter = r; + var s = a_.node || a_.worker ? null : new xi(r.getViewportRoot()); + this.handler = new L_(o, r, s, r.root), this.animation = new Bw({stage: {update: m(this.flush, this)}}), this.animation.start(), this._needsRefresh; + var l = o.delFromStorage, u = o.addToStorage; + o.delFromStorage = function (t) { + l.call(o, t), t && t.removeSelfFromZr(n) + }, o.addToStorage = function (t) { + u.call(o, t), t.addSelfToZr(n) + } + }; + Kw.prototype = { + constructor: Kw, getId: function () { + return this.id + }, add: function (t) { + this.storage.addRoot(t), this._needsRefresh = !0 + }, remove: function (t) { + this.storage.delRoot(t), this._needsRefresh = !0 + }, configLayer: function (t, e) { + this.painter.configLayer && this.painter.configLayer(t, e), this._needsRefresh = !0 + }, setBackgroundColor: function (t) { + this.painter.setBackgroundColor && this.painter.setBackgroundColor(t), this._needsRefresh = !0 + }, refreshImmediately: function () { + this._needsRefresh = !1, this.painter.refresh(), this._needsRefresh = !1 + }, refresh: function () { + this._needsRefresh = !0 + }, flush: function () { + var t; + this._needsRefresh && (t = !0, this.refreshImmediately()), this._needsRefreshHover && (t = !0, this.refreshHoverImmediately()), t && this.trigger("rendered") + }, addHover: function (t, e) { + this.painter.addHover && (this.painter.addHover(t, e), this.refreshHover()) + }, removeHover: function (t) { + this.painter.removeHover && (this.painter.removeHover(t), this.refreshHover()) + }, clearHover: function () { + this.painter.clearHover && (this.painter.clearHover(), this.refreshHover()) + }, refreshHover: function () { + this._needsRefreshHover = !0 + }, refreshHoverImmediately: function () { + this._needsRefreshHover = !1, this.painter.refreshHover && this.painter.refreshHover() + }, resize: function (t) { + t = t || {}, this.painter.resize(t.width, t.height), this.handler.resize() + }, clearAnimation: function () { + this.animation.clear() + }, getWidth: function () { + return this.painter.getWidth() + }, getHeight: function () { + return this.painter.getHeight() + }, pathToImage: function (t, e) { + return this.painter.pathToImage(t, e) + }, setCursorStyle: function (t) { + this.handler.setCursorStyle(t) + }, findHover: function (t, e) { + return this.handler.findHover(t, e) + }, on: function (t, e, i) { + this.handler.on(t, e, i) + }, off: function (t, e) { + this.handler.off(t, e) + }, trigger: function (t, e) { + this.handler.trigger(t, e) + }, clear: function () { + this.storage.delRoot(), this.painter.clear() + }, dispose: function () { + this.animation.stop(), this.clear(), this.storage.dispose(), this.painter.dispose(), this.handler.dispose(), this.animation = this.storage = this.painter = this.handler = null, bi(this.id) + } + }; + var $w = (Object.freeze || Object)({ + version: "4.0.4", init: _i, dispose: function (t) { + if (t) t.dispose(); else { + for (var e in qw) qw.hasOwnProperty(e) && qw[e].dispose(); + qw = {} + } + return this + }, getInstance: function (t) { + return qw[t] + }, registerPainter: wi + }), Jw = d, Qw = w, tb = y, eb = "series\0", + ib = ["fontStyle", "fontWeight", "fontSize", "fontFamily", "rich", "tag", "color", "textBorderColor", "textBorderWidth", "width", "height", "lineHeight", "align", "verticalAlign", "baseline", "shadowColor", "shadowBlur", "shadowOffsetX", "shadowOffsetY", "textShadowColor", "textShadowBlur", "textShadowOffsetX", "textShadowOffsetY", "backgroundColor", "borderColor", "borderWidth", "borderRadius", "padding"], + nb = 0, ob = ".", ab = "___EC__COMPONENT__CONTAINER___", rb = 0, sb = function (t) { + for (var e = 0; e < t.length; e++) t[e][1] || (t[e][1] = t[e][0]); + return function (e, i, n) { + for (var o = {}, a = 0; a < t.length; a++) { + var r = t[a][1]; + if (!(i && l(i, r) >= 0 || n && l(n, r) < 0)) { + var s = e.getShallow(r); + null != s && (o[t[a][0]] = s) + } + } + return o + } + }, + lb = sb([["lineWidth", "width"], ["stroke", "color"], ["opacity"], ["shadowBlur"], ["shadowOffsetX"], ["shadowOffsetY"], ["shadowColor"]]), + ub = { + getLineStyle: function (t) { + var e = lb(this, t), i = this.getLineDash(e.lineWidth); + return i && (e.lineDash = i), e + }, getLineDash: function (t) { + null == t && (t = 1); + var e = this.get("type"), i = Math.max(t, 2), n = 4 * t; + return "solid" === e || null == e ? null : "dashed" === e ? [n, n] : [i, i] + } + }, + hb = sb([["fill", "color"], ["shadowBlur"], ["shadowOffsetX"], ["shadowOffsetY"], ["opacity"], ["shadowColor"]]), + cb = { + getAreaStyle: function (t, e) { + return hb(this, t, e) + } + }, db = Math.pow, fb = Math.sqrt, pb = 1e-8, gb = 1e-4, mb = fb(3), vb = 1 / 3, yb = V(), xb = V(), _b = V(), + wb = Math.min, bb = Math.max, Sb = Math.sin, Mb = Math.cos, Ib = 2 * Math.PI, Tb = V(), Db = V(), Ab = V(), + Cb = [], Lb = [], kb = {M: 1, L: 2, C: 3, Q: 4, A: 5, Z: 6, R: 7}, Pb = [], Nb = [], Ob = [], Eb = [], + Rb = Math.min, zb = Math.max, Bb = Math.cos, Vb = Math.sin, Gb = Math.sqrt, Wb = Math.abs, + Fb = "undefined" != typeof Float32Array, Hb = function (t) { + this._saveData = !t, this._saveData && (this.data = []), this._ctx = null + }; + Hb.prototype = { + constructor: Hb, + _xi: 0, + _yi: 0, + _x0: 0, + _y0: 0, + _ux: 0, + _uy: 0, + _len: 0, + _lineDash: null, + _dashOffset: 0, + _dashIdx: 0, + _dashSum: 0, + setScale: function (t, e) { + this._ux = Wb(1 / Q_ / t) || 0, this._uy = Wb(1 / Q_ / e) || 0 + }, + getContext: function () { + return this._ctx + }, + beginPath: function (t) { + return this._ctx = t, t && t.beginPath(), t && (this.dpr = t.dpr), this._saveData && (this._len = 0), this._lineDash && (this._lineDash = null, this._dashOffset = 0), this + }, + moveTo: function (t, e) { + return this.addData(kb.M, t, e), this._ctx && this._ctx.moveTo(t, e), this._x0 = t, this._y0 = e, this._xi = t, this._yi = e, this + }, + lineTo: function (t, e) { + var i = Wb(t - this._xi) > this._ux || Wb(e - this._yi) > this._uy || this._len < 5; + return this.addData(kb.L, t, e), this._ctx && i && (this._needsDash() ? this._dashedLineTo(t, e) : this._ctx.lineTo(t, e)), i && (this._xi = t, this._yi = e), this + }, + bezierCurveTo: function (t, e, i, n, o, a) { + return this.addData(kb.C, t, e, i, n, o, a), this._ctx && (this._needsDash() ? this._dashedBezierTo(t, e, i, n, o, a) : this._ctx.bezierCurveTo(t, e, i, n, o, a)), this._xi = o, this._yi = a, this + }, + quadraticCurveTo: function (t, e, i, n) { + return this.addData(kb.Q, t, e, i, n), this._ctx && (this._needsDash() ? this._dashedQuadraticTo(t, e, i, n) : this._ctx.quadraticCurveTo(t, e, i, n)), this._xi = i, this._yi = n, this + }, + arc: function (t, e, i, n, o, a) { + return this.addData(kb.A, t, e, i, i, n, o - n, 0, a ? 0 : 1), this._ctx && this._ctx.arc(t, e, i, n, o, a), this._xi = Bb(o) * i + t, this._yi = Vb(o) * i + t, this + }, + arcTo: function (t, e, i, n, o) { + return this._ctx && this._ctx.arcTo(t, e, i, n, o), this + }, + rect: function (t, e, i, n) { + return this._ctx && this._ctx.rect(t, e, i, n), this.addData(kb.R, t, e, i, n), this + }, + closePath: function () { + this.addData(kb.Z); + var t = this._ctx, e = this._x0, i = this._y0; + return t && (this._needsDash() && this._dashedLineTo(e, i), t.closePath()), this._xi = e, this._yi = i, this + }, + fill: function (t) { + t && t.fill(), this.toStatic() + }, + stroke: function (t) { + t && t.stroke(), this.toStatic() + }, + setLineDash: function (t) { + if (t instanceof Array) { + this._lineDash = t, this._dashIdx = 0; + for (var e = 0, i = 0; i < t.length; i++) e += t[i]; + this._dashSum = e + } + return this + }, + setLineDashOffset: function (t) { + return this._dashOffset = t, this + }, + len: function () { + return this._len + }, + setData: function (t) { + var e = t.length; + this.data && this.data.length == e || !Fb || (this.data = new Float32Array(e)); + for (var i = 0; i < e; i++) this.data[i] = t[i]; + this._len = e + }, + appendPath: function (t) { + t instanceof Array || (t = [t]); + for (var e = t.length, i = 0, n = this._len, o = 0; o < e; o++) i += t[o].len(); + Fb && this.data instanceof Float32Array && (this.data = new Float32Array(n + i)); + for (o = 0; o < e; o++) for (var a = t[o].data, r = 0; r < a.length; r++) this.data[n++] = a[r]; + this._len = n + }, + addData: function (t) { + if (this._saveData) { + var e = this.data; + this._len + arguments.length > e.length && (this._expandData(), e = this.data); + for (var i = 0; i < arguments.length; i++) e[this._len++] = arguments[i]; + this._prevCmd = t + } + }, + _expandData: function () { + if (!(this.data instanceof Array)) { + for (var t = [], e = 0; e < this._len; e++) t[e] = this.data[e]; + this.data = t + } + }, + _needsDash: function () { + return this._lineDash + }, + _dashedLineTo: function (t, e) { + var i, n, o = this._dashSum, a = this._dashOffset, r = this._lineDash, s = this._ctx, l = this._xi, + u = this._yi, h = t - l, c = e - u, d = Gb(h * h + c * c), f = l, p = u, g = r.length; + for (h /= d, c /= d, a < 0 && (a = o + a), f -= (a %= o) * h, p -= a * c; h > 0 && f <= t || h < 0 && f >= t || 0 == h && (c > 0 && p <= e || c < 0 && p >= e);) f += h * (i = r[n = this._dashIdx]), p += c * i, this._dashIdx = (n + 1) % g, h > 0 && f < l || h < 0 && f > l || c > 0 && p < u || c < 0 && p > u || s[n % 2 ? "moveTo" : "lineTo"](h >= 0 ? Rb(f, t) : zb(f, t), c >= 0 ? Rb(p, e) : zb(p, e)); + h = f - t, c = p - e, this._dashOffset = -Gb(h * h + c * c) + }, + _dashedBezierTo: function (t, e, i, n, o, a) { + var r, s, l, u, h, c = this._dashSum, d = this._dashOffset, f = this._lineDash, p = this._ctx, g = this._xi, + m = this._yi, v = ji, y = 0, x = this._dashIdx, _ = f.length, w = 0; + for (d < 0 && (d = c + d), d %= c, r = 0; r < 1; r += .1) s = v(g, t, i, o, r + .1) - v(g, t, i, o, r), l = v(m, e, n, a, r + .1) - v(m, e, n, a, r), y += Gb(s * s + l * l); + for (; x < _ && !((w += f[x]) > d); x++) ; + for (r = (w - d) / y; r <= 1;) u = v(g, t, i, o, r), h = v(m, e, n, a, r), x % 2 ? p.moveTo(u, h) : p.lineTo(u, h), r += f[x] / y, x = (x + 1) % _; + x % 2 != 0 && p.lineTo(o, a), s = o - u, l = a - h, this._dashOffset = -Gb(s * s + l * l) + }, + _dashedQuadraticTo: function (t, e, i, n) { + var o = i, a = n; + i = (i + 2 * t) / 3, n = (n + 2 * e) / 3, t = (this._xi + 2 * t) / 3, e = (this._yi + 2 * e) / 3, this._dashedBezierTo(t, e, i, n, o, a) + }, + toStatic: function () { + var t = this.data; + t instanceof Array && (t.length = this._len, Fb && (this.data = new Float32Array(t))) + }, + getBoundingRect: function () { + Pb[0] = Pb[1] = Ob[0] = Ob[1] = Number.MAX_VALUE, Nb[0] = Nb[1] = Eb[0] = Eb[1] = -Number.MAX_VALUE; + for (var t = this.data, e = 0, i = 0, n = 0, o = 0, a = 0; a < t.length;) { + var r = t[a++]; + switch (1 == a && (n = e = t[a], o = i = t[a + 1]), r) { + case kb.M: + e = n = t[a++], i = o = t[a++], Ob[0] = n, Ob[1] = o, Eb[0] = n, Eb[1] = o; + break; + case kb.L: + sn(e, i, t[a], t[a + 1], Ob, Eb), e = t[a++], i = t[a++]; + break; + case kb.C: + ln(e, i, t[a++], t[a++], t[a++], t[a++], t[a], t[a + 1], Ob, Eb), e = t[a++], i = t[a++]; + break; + case kb.Q: + un(e, i, t[a++], t[a++], t[a], t[a + 1], Ob, Eb), e = t[a++], i = t[a++]; + break; + case kb.A: + var s = t[a++], l = t[a++], u = t[a++], h = t[a++], c = t[a++], d = t[a++] + c, + f = (t[a++], 1 - t[a++]); + 1 == a && (n = Bb(c) * u + s, o = Vb(c) * h + l), hn(s, l, u, h, c, d, f, Ob, Eb), e = Bb(d) * u + s, i = Vb(d) * h + l; + break; + case kb.R: + sn(n = e = t[a++], o = i = t[a++], n + t[a++], o + t[a++], Ob, Eb); + break; + case kb.Z: + e = n, i = o + } + tt(Pb, Pb, Ob), et(Nb, Nb, Eb) + } + return 0 === a && (Pb[0] = Pb[1] = Nb[0] = Nb[1] = 0), new $t(Pb[0], Pb[1], Nb[0] - Pb[0], Nb[1] - Pb[1]) + }, + rebuildPath: function (t) { + for (var e, i, n, o, a, r, s = this.data, l = this._ux, u = this._uy, h = this._len, c = 0; c < h;) { + var d = s[c++]; + switch (1 == c && (e = n = s[c], i = o = s[c + 1]), d) { + case kb.M: + e = n = s[c++], i = o = s[c++], t.moveTo(n, o); + break; + case kb.L: + a = s[c++], r = s[c++], (Wb(a - n) > l || Wb(r - o) > u || c === h - 1) && (t.lineTo(a, r), n = a, o = r); + break; + case kb.C: + t.bezierCurveTo(s[c++], s[c++], s[c++], s[c++], s[c++], s[c++]), n = s[c - 2], o = s[c - 1]; + break; + case kb.Q: + t.quadraticCurveTo(s[c++], s[c++], s[c++], s[c++]), n = s[c - 2], o = s[c - 1]; + break; + case kb.A: + var f = s[c++], p = s[c++], g = s[c++], m = s[c++], v = s[c++], y = s[c++], x = s[c++], + _ = s[c++], w = g > m ? g : m, b = g > m ? 1 : g / m, S = g > m ? m / g : 1, M = v + y; + Math.abs(g - m) > .001 ? (t.translate(f, p), t.rotate(x), t.scale(b, S), t.arc(0, 0, w, v, M, 1 - _), t.scale(1 / b, 1 / S), t.rotate(-x), t.translate(-f, -p)) : t.arc(f, p, w, v, M, 1 - _), 1 == c && (e = Bb(v) * g + f, i = Vb(v) * m + p), n = Bb(M) * g + f, o = Vb(M) * m + p; + break; + case kb.R: + e = n = s[c], i = o = s[c + 1], t.rect(s[c++], s[c++], s[c++], s[c++]); + break; + case kb.Z: + t.closePath(), n = e, o = i + } + } + } + }, Hb.CMD = kb; + var Zb = 2 * Math.PI, Ub = 2 * Math.PI, Xb = Hb.CMD, jb = 2 * Math.PI, Yb = 1e-4, qb = [-1, -1, -1], Kb = [-1, -1], + $b = yw.prototype.getCanvasPattern, Jb = Math.abs, Qb = new Hb(!0); + In.prototype = { + constructor: In, type: "path", __dirtyPath: !0, strokeContainThreshold: 5, brush: function (t, e) { + var i = this.style, n = this.path || Qb, o = i.hasStroke(), a = i.hasFill(), r = i.fill, s = i.stroke, + l = a && !!r.colorStops, u = o && !!s.colorStops, h = a && !!r.image, c = o && !!s.image; + if (i.bind(t, this, e), this.setTransform(t), this.__dirty) { + var d; + l && (d = d || this.getBoundingRect(), this._fillGradient = i.getGradient(t, r, d)), u && (d = d || this.getBoundingRect(), this._strokeGradient = i.getGradient(t, s, d)) + } + l ? t.fillStyle = this._fillGradient : h && (t.fillStyle = $b.call(r, t)), u ? t.strokeStyle = this._strokeGradient : c && (t.strokeStyle = $b.call(s, t)); + var f = i.lineDash, p = i.lineDashOffset, g = !!t.setLineDash, m = this.getGlobalScale(); + n.setScale(m[0], m[1]), this.__dirtyPath || f && !g && o ? (n.beginPath(t), f && !g && (n.setLineDash(f), n.setLineDashOffset(p)), this.buildPath(n, this.shape, !1), this.path && (this.__dirtyPath = !1)) : (t.beginPath(), this.path.rebuildPath(t)), a && n.fill(t), f && g && (t.setLineDash(f), t.lineDashOffset = p), o && n.stroke(t), f && g && t.setLineDash([]), null != i.text && (this.restoreTransform(t), this.drawRectText(t, this.getBoundingRect())) + }, buildPath: function (t, e, i) { + }, createPathProxy: function () { + this.path = new Hb + }, getBoundingRect: function () { + var t = this._rect, e = this.style, i = !t; + if (i) { + var n = this.path; + n || (n = this.path = new Hb), this.__dirtyPath && (n.beginPath(), this.buildPath(n, this.shape, !1)), t = n.getBoundingRect() + } + if (this._rect = t, e.hasStroke()) { + var o = this._rectWithStroke || (this._rectWithStroke = t.clone()); + if (this.__dirty || i) { + o.copy(t); + var a = e.lineWidth, r = e.strokeNoScale ? this.getLineScale() : 1; + e.hasFill() || (a = Math.max(a, this.strokeContainThreshold || 4)), r > 1e-10 && (o.width += a / r, o.height += a / r, o.x -= a / r / 2, o.y -= a / r / 2) + } + return o + } + return t + }, contain: function (t, e) { + var i = this.transformCoordToLocal(t, e), n = this.getBoundingRect(), o = this.style; + if (t = i[0], e = i[1], n.contain(t, e)) { + var a = this.path.data; + if (o.hasStroke()) { + var r = o.lineWidth, s = o.strokeNoScale ? this.getLineScale() : 1; + if (s > 1e-10 && (o.hasFill() || (r = Math.max(r, this.strokeContainThreshold)), Mn(a, r / s, t, e))) return !0 + } + if (o.hasFill()) return Sn(a, t, e) + } + return !1 + }, dirty: function (t) { + null == t && (t = !0), t && (this.__dirtyPath = t, this._rect = null), this.__dirty = !0, this.__zr && this.__zr.refresh(), this.__clipTarget && this.__clipTarget.dirty() + }, animateShape: function (t) { + return this.animate("shape", t) + }, attrKV: function (t, e) { + "shape" === t ? (this.setShape(e), this.__dirtyPath = !0, this._rect = null) : $e.prototype.attrKV.call(this, t, e) + }, setShape: function (t, e) { + var i = this.shape; + if (i) { + if (w(t)) for (var n in t) t.hasOwnProperty(n) && (i[n] = t[n]); else i[t] = e; + this.dirty(!0) + } + return this + }, getLineScale: function () { + var t = this.transform; + return t && Jb(t[0] - 1) > 1e-10 && Jb(t[3] - 1) > 1e-10 ? Math.sqrt(Jb(t[0] * t[3] - t[2] * t[1])) : 1 + } + }, In.extend = function (t) { + var e = function (e) { + In.call(this, e), t.style && this.style.extendFrom(t.style, !1); + var i = t.shape; + if (i) { + this.shape = this.shape || {}; + var n = this.shape; + for (var o in i) !n.hasOwnProperty(o) && i.hasOwnProperty(o) && (n[o] = i[o]) + } + t.init && t.init.call(this, e) + }; + u(e, In); + for (var i in t) "style" !== i && "shape" !== i && (e.prototype[i] = t[i]); + return e + }, u(In, $e); + var tS = Hb.CMD, eS = [[], [], []], iS = Math.sqrt, nS = Math.atan2, oS = function (t, e) { + var i, n, o, a, r, s, l = t.data, u = tS.M, h = tS.C, c = tS.L, d = tS.R, f = tS.A, p = tS.Q; + for (o = 0, a = 0; o < l.length;) { + switch (i = l[o++], a = o, n = 0, i) { + case u: + case c: + n = 1; + break; + case h: + n = 3; + break; + case p: + n = 2; + break; + case f: + var g = e[4], m = e[5], v = iS(e[0] * e[0] + e[1] * e[1]), y = iS(e[2] * e[2] + e[3] * e[3]), + x = nS(-e[1] / y, e[0] / v); + l[o] *= v, l[o++] += g, l[o] *= y, l[o++] += m, l[o++] *= v, l[o++] *= y, l[o++] += x, l[o++] += x, a = o += 2; + break; + case d: + s[0] = l[o++], s[1] = l[o++], Q(s, s, e), l[a++] = s[0], l[a++] = s[1], s[0] += l[o++], s[1] += l[o++], Q(s, s, e), l[a++] = s[0], l[a++] = s[1] + } + for (r = 0; r < n; r++) (s = eS[r])[0] = l[o++], s[1] = l[o++], Q(s, s, e), l[a++] = s[0], l[a++] = s[1] + } + }, aS = ["m", "M", "l", "L", "v", "V", "h", "H", "z", "Z", "c", "C", "q", "Q", "t", "T", "s", "S", "a", "A"], + rS = Math.sqrt, sS = Math.sin, lS = Math.cos, uS = Math.PI, hS = function (t) { + return Math.sqrt(t[0] * t[0] + t[1] * t[1]) + }, cS = function (t, e) { + return (t[0] * e[0] + t[1] * e[1]) / (hS(t) * hS(e)) + }, dS = function (t, e) { + return (t[0] * e[1] < t[1] * e[0] ? -1 : 1) * Math.acos(cS(t, e)) + }, fS = function (t) { + $e.call(this, t) + }; + fS.prototype = { + constructor: fS, type: "text", brush: function (t, e) { + var i = this.style; + this.__dirty && Ne(i), i.fill = i.stroke = i.shadowBlur = i.shadowColor = i.shadowOffsetX = i.shadowOffsetY = null; + var n = i.text; + null != n && (n += ""), i.bind(t, this, e), Ke(n, i) && (this.setTransform(t), Ee(this, t, n, i), this.restoreTransform(t)) + }, getBoundingRect: function () { + var t = this.style; + if (this.__dirty && Ne(t), !this._rect) { + var e = t.text; + null != e ? e += "" : e = ""; + var i = me(t.text + "", t.font, t.textAlign, t.textVerticalAlign, t.textPadding, t.rich); + if (i.x += t.x || 0, i.y += t.y || 0, Xe(t.textStroke, t.textStrokeWidth)) { + var n = t.textStrokeWidth; + i.x -= n / 2, i.y -= n / 2, i.width += n, i.height += n + } + this._rect = i + } + return this._rect + } + }, u(fS, $e); + var pS = In.extend({ + type: "circle", shape: {cx: 0, cy: 0, r: 0}, buildPath: function (t, e, i) { + i && t.moveTo(e.cx + e.r, e.cy), t.arc(e.cx, e.cy, e.r, 0, 2 * Math.PI, !0) + } + }), gS = [["shadowBlur", 0], ["shadowColor", "#000"], ["shadowOffsetX", 0], ["shadowOffsetY", 0]], + mS = function (t) { + return a_.browser.ie && a_.browser.version >= 11 ? function () { + var e, i = this.__clipPaths, n = this.style; + if (i) for (var o = 0; o < i.length; o++) { + var a = i[o], r = a && a.shape, s = a && a.type; + if (r && ("sector" === s && r.startAngle === r.endAngle || "rect" === s && (!r.width || !r.height))) { + for (l = 0; l < gS.length; l++) gS[l][2] = n[gS[l][0]], n[gS[l][0]] = gS[l][1]; + e = !0; + break + } + } + if (t.apply(this, arguments), e) for (var l = 0; l < gS.length; l++) n[gS[l][0]] = gS[l][2] + } : t + }, vS = In.extend({ + type: "sector", + shape: {cx: 0, cy: 0, r0: 0, r: 0, startAngle: 0, endAngle: 2 * Math.PI, clockwise: !0}, + brush: mS(In.prototype.brush), + buildPath: function (t, e) { + var i = e.cx, n = e.cy, o = Math.max(e.r0 || 0, 0), a = Math.max(e.r, 0), r = e.startAngle, s = e.endAngle, + l = e.clockwise, u = Math.cos(r), h = Math.sin(r); + t.moveTo(u * o + i, h * o + n), t.lineTo(u * a + i, h * a + n), t.arc(i, n, a, r, s, !l), t.lineTo(Math.cos(s) * o + i, Math.sin(s) * o + n), 0 !== o && t.arc(i, n, o, s, r, l), t.closePath() + } + }), yS = In.extend({ + type: "ring", shape: {cx: 0, cy: 0, r: 0, r0: 0}, buildPath: function (t, e) { + var i = e.cx, n = e.cy, o = 2 * Math.PI; + t.moveTo(i + e.r, n), t.arc(i, n, e.r, 0, o, !1), t.moveTo(i + e.r0, n), t.arc(i, n, e.r0, 0, o, !0) + } + }), xS = function (t, e) { + for (var i = t.length, n = [], o = 0, a = 1; a < i; a++) o += K(t[a - 1], t[a]); + var r = o / 2; + r = r < i ? i : r; + for (a = 0; a < r; a++) { + var s, l, u, h = a / (r - 1) * (e ? i : i - 1), c = Math.floor(h), d = h - c, f = t[c % i]; + e ? (s = t[(c - 1 + i) % i], l = t[(c + 1) % i], u = t[(c + 2) % i]) : (s = t[0 === c ? c : c - 1], l = t[c > i - 2 ? i - 1 : c + 1], u = t[c > i - 3 ? i - 1 : c + 2]); + var p = d * d, g = d * p; + n.push([kn(s[0], f[0], l[0], u[0], d, p, g), kn(s[1], f[1], l[1], u[1], d, p, g)]) + } + return n + }, _S = function (t, e, i, n) { + var o, a, r, s, l = [], u = [], h = [], c = []; + if (n) { + r = [1 / 0, 1 / 0], s = [-1 / 0, -1 / 0]; + for (var d = 0, f = t.length; d < f; d++) tt(r, r, t[d]), et(s, s, t[d]); + tt(r, r, n[0]), et(s, s, n[1]) + } + for (var d = 0, f = t.length; d < f; d++) { + var p = t[d]; + if (i) o = t[d ? d - 1 : f - 1], a = t[(d + 1) % f]; else { + if (0 === d || d === f - 1) { + l.push(W(t[d])); + continue + } + o = t[d - 1], a = t[d + 1] + } + U(u, a, o), Y(u, u, e); + var g = K(p, o), m = K(p, a), v = g + m; + 0 !== v && (g /= v, m /= v), Y(h, u, -g), Y(c, u, m); + var y = H([], p, h), x = H([], p, c); + n && (et(y, y, r), tt(y, y, s), et(x, x, r), tt(x, x, s)), l.push(y), l.push(x) + } + return i && l.push(l.shift()), l + }, wS = In.extend({ + type: "polygon", + shape: {points: null, smooth: !1, smoothConstraint: null}, + buildPath: function (t, e) { + Pn(t, e, !0) + } + }), bS = In.extend({ + type: "polyline", + shape: {points: null, smooth: !1, smoothConstraint: null}, + style: {stroke: "#000", fill: null}, + buildPath: function (t, e) { + Pn(t, e, !1) + } + }), SS = In.extend({ + type: "rect", shape: {r: 0, x: 0, y: 0, width: 0, height: 0}, buildPath: function (t, e) { + var i = e.x, n = e.y, o = e.width, a = e.height; + e.r ? Pe(t, e) : t.rect(i, n, o, a), t.closePath() + } + }), MS = In.extend({ + type: "line", + shape: {x1: 0, y1: 0, x2: 0, y2: 0, percent: 1}, + style: {stroke: "#000", fill: null}, + buildPath: function (t, e) { + var i = e.x1, n = e.y1, o = e.x2, a = e.y2, r = e.percent; + 0 !== r && (t.moveTo(i, n), r < 1 && (o = i * (1 - r) + o * r, a = n * (1 - r) + a * r), t.lineTo(o, a)) + }, + pointAt: function (t) { + var e = this.shape; + return [e.x1 * (1 - t) + e.x2 * t, e.y1 * (1 - t) + e.y2 * t] + } + }), IS = [], TS = In.extend({ + type: "bezier-curve", + shape: {x1: 0, y1: 0, x2: 0, y2: 0, cpx1: 0, cpy1: 0, percent: 1}, + style: {stroke: "#000", fill: null}, + buildPath: function (t, e) { + var i = e.x1, n = e.y1, o = e.x2, a = e.y2, r = e.cpx1, s = e.cpy1, l = e.cpx2, u = e.cpy2, h = e.percent; + 0 !== h && (t.moveTo(i, n), null == l || null == u ? (h < 1 && (on(i, r, o, h, IS), r = IS[1], o = IS[2], on(n, s, a, h, IS), s = IS[1], a = IS[2]), t.quadraticCurveTo(r, s, o, a)) : (h < 1 && ($i(i, r, l, o, h, IS), r = IS[1], l = IS[2], o = IS[3], $i(n, s, u, a, h, IS), s = IS[1], u = IS[2], a = IS[3]), t.bezierCurveTo(r, s, l, u, o, a))) + }, + pointAt: function (t) { + return Nn(this.shape, t, !1) + }, + tangentAt: function (t) { + var e = Nn(this.shape, t, !0); + return q(e, e) + } + }), DS = In.extend({ + type: "arc", + shape: {cx: 0, cy: 0, r: 0, startAngle: 0, endAngle: 2 * Math.PI, clockwise: !0}, + style: {stroke: "#000", fill: null}, + buildPath: function (t, e) { + var i = e.cx, n = e.cy, o = Math.max(e.r, 0), a = e.startAngle, r = e.endAngle, s = e.clockwise, + l = Math.cos(a), u = Math.sin(a); + t.moveTo(l * o + i, u * o + n), t.arc(i, n, o, a, r, !s) + } + }), AS = In.extend({ + type: "compound", shape: {paths: null}, _updatePathDirty: function () { + for (var t = this.__dirtyPath, e = this.shape.paths, i = 0; i < e.length; i++) t = t || e[i].__dirtyPath; + this.__dirtyPath = t, this.__dirty = this.__dirty || t + }, beforeBrush: function () { + this._updatePathDirty(); + for (var t = this.shape.paths || [], e = this.getGlobalScale(), i = 0; i < t.length; i++) t[i].path || t[i].createPathProxy(), t[i].path.setScale(e[0], e[1]) + }, buildPath: function (t, e) { + for (var i = e.paths || [], n = 0; n < i.length; n++) i[n].buildPath(t, i[n].shape, !0) + }, afterBrush: function () { + for (var t = this.shape.paths || [], e = 0; e < t.length; e++) t[e].__dirtyPath = !1 + }, getBoundingRect: function () { + return this._updatePathDirty(), In.prototype.getBoundingRect.call(this) + } + }), CS = function (t) { + this.colorStops = t || [] + }; + CS.prototype = { + constructor: CS, addColorStop: function (t, e) { + this.colorStops.push({offset: t, color: e}) + } + }; + var LS = function (t, e, i, n, o, a) { + this.x = null == t ? 0 : t, this.y = null == e ? 0 : e, this.x2 = null == i ? 1 : i, this.y2 = null == n ? 0 : n, this.type = "linear", this.global = a || !1, CS.call(this, o) + }; + LS.prototype = {constructor: LS}, u(LS, CS); + var kS = function (t, e, i, n, o) { + this.x = null == t ? .5 : t, this.y = null == e ? .5 : e, this.r = null == i ? .5 : i, this.type = "radial", this.global = o || !1, CS.call(this, n) + }; + kS.prototype = {constructor: kS}, u(kS, CS), On.prototype.incremental = !0, On.prototype.clearDisplaybles = function () { + this._displayables = [], this._temporaryDisplayables = [], this._cursor = 0, this.dirty(), this.notClear = !1 + }, On.prototype.addDisplayable = function (t, e) { + e ? this._temporaryDisplayables.push(t) : this._displayables.push(t), this.dirty() + }, On.prototype.addDisplayables = function (t, e) { + e = e || !1; + for (var i = 0; i < t.length; i++) this.addDisplayable(t[i], e) + }, On.prototype.eachPendingDisplayable = function (t) { + for (e = this._cursor; e < this._displayables.length; e++) t && t(this._displayables[e]); + for (var e = 0; e < this._temporaryDisplayables.length; e++) t && t(this._temporaryDisplayables[e]) + }, On.prototype.update = function () { + this.updateTransform(); + for (t = this._cursor; t < this._displayables.length; t++) (e = this._displayables[t]).parent = this, e.update(), e.parent = null; + for (var t = 0; t < this._temporaryDisplayables.length; t++) { + var e = this._temporaryDisplayables[t]; + e.parent = this, e.update(), e.parent = null + } + }, On.prototype.brush = function (t, e) { + for (i = this._cursor; i < this._displayables.length; i++) (n = this._displayables[i]).beforeBrush && n.beforeBrush(t), n.brush(t, i === this._cursor ? null : this._displayables[i - 1]), n.afterBrush && n.afterBrush(t); + this._cursor = i; + for (var i = 0; i < this._temporaryDisplayables.length; i++) { + var n = this._temporaryDisplayables[i]; + n.beforeBrush && n.beforeBrush(t), n.brush(t, 0 === i ? null : this._temporaryDisplayables[i - 1]), n.afterBrush && n.afterBrush(t) + } + this._temporaryDisplayables = [], this.notClear = !0 + }; + var PS = []; + On.prototype.getBoundingRect = function () { + if (!this._rect) { + for (var t = new $t(1 / 0, 1 / 0, -1 / 0, -1 / 0), e = 0; e < this._displayables.length; e++) { + var i = this._displayables[e], n = i.getBoundingRect().clone(); + i.needLocalTransform() && n.applyTransform(i.getLocalTransform(PS)), t.union(n) + } + this._rect = t + } + return this._rect + }, On.prototype.contain = function (t, e) { + var i = this.transformCoordToLocal(t, e); + if (this.getBoundingRect().contain(i[0], i[1])) for (var n = 0; n < this._displayables.length; n++) if (this._displayables[n].contain(t, e)) return !0; + return !1 + }, u(On, $e); + var NS = Math.round, OS = Math.max, ES = Math.min, RS = {}, zS = function (t, e) { + for (var i = [], n = t.length, o = 0; o < n; o++) { + var a = t[o]; + a.path || a.createPathProxy(), a.__dirtyPath && a.buildPath(a.path, a.shape, !0), i.push(a.path) + } + var r = new In(e); + return r.createPathProxy(), r.buildPath = function (t) { + t.appendPath(i); + var e = t.getContext(); + e && t.rebuildPath(e) + }, r + }, BS = (Object.freeze || Object)({ + extendShape: En, + extendPath: function (t, e) { + return Ln(t, e) + }, + makePath: Rn, + makeImage: zn, + mergePath: zS, + resizePath: Vn, + subPixelOptimizeLine: Gn, + subPixelOptimizeRect: Wn, + subPixelOptimize: Fn, + setHoverStyle: eo, + setLabelStyle: io, + setTextStyle: no, + setText: function (t, e, i) { + var n, o = {isRectText: !0}; + !1 === i ? n = !0 : o.autoColor = i, oo(t, e, o, n), t.host && t.host.dirty && t.host.dirty(!1) + }, + getFont: ho, + updateProps: fo, + initProps: po, + getTransform: go, + applyTransform: mo, + transformDirection: vo, + groupTransition: yo, + clipPointsByRect: xo, + clipRectByRect: function (t, e) { + var i = OS(t.x, e.x), n = ES(t.x + t.width, e.x + e.width), o = OS(t.y, e.y), + a = ES(t.y + t.height, e.y + e.height); + if (n >= i && a >= o) return {x: i, y: o, width: n - i, height: a - o} + }, + createIcon: _o, + Group: sw, + Image: Je, + Text: fS, + Circle: pS, + Sector: vS, + Ring: yS, + Polygon: wS, + Polyline: bS, + Rect: SS, + Line: MS, + BezierCurve: TS, + Arc: DS, + IncrementalDisplayable: On, + CompoundPath: AS, + LinearGradient: LS, + RadialGradient: kS, + BoundingRect: $t + }), VS = ["textStyle", "color"], GS = { + getTextColor: function (t) { + var e = this.ecModel; + return this.getShallow("color") || (!t && e ? e.get(VS) : null) + }, getFont: function () { + return ho({ + fontStyle: this.getShallow("fontStyle"), + fontWeight: this.getShallow("fontWeight"), + fontSize: this.getShallow("fontSize"), + fontFamily: this.getShallow("fontFamily") + }, this.ecModel) + }, getTextRect: function (t) { + return me(t, this.getFont(), this.getShallow("align"), this.getShallow("verticalAlign") || this.getShallow("baseline"), this.getShallow("padding"), this.getShallow("rich"), this.getShallow("truncateText")) + } + }, + WS = sb([["fill", "color"], ["stroke", "borderColor"], ["lineWidth", "borderWidth"], ["opacity"], ["shadowBlur"], ["shadowOffsetX"], ["shadowOffsetY"], ["shadowColor"], ["textPosition"], ["textAlign"]]), + FS = { + getItemStyle: function (t, e) { + var i = WS(this, t, e), n = this.getBorderLineDash(); + return n && (i.lineDash = n), i + }, getBorderLineDash: function () { + var t = this.get("borderType"); + return "solid" === t || null == t ? null : "dashed" === t ? [5, 5] : [1, 1] + } + }, HS = h, ZS = Ni(); + wo.prototype = { + constructor: wo, init: null, mergeOption: function (t) { + n(this.option, t, !0) + }, get: function (t, e) { + return null == t ? this.option : bo(this.option, this.parsePath(t), !e && So(this, t)) + }, getShallow: function (t, e) { + var i = this.option, n = null == i ? i : i[t], o = !e && So(this, t); + return null == n && o && (n = o.getShallow(t)), n + }, getModel: function (t, e) { + var i, n = null == t ? this.option : bo(this.option, t = this.parsePath(t)); + return e = e || (i = So(this, t)) && i.getModel(t), new wo(n, e, this.ecModel) + }, isEmpty: function () { + return null == this.option + }, restoreData: function () { + }, clone: function () { + return new (0, this.constructor)(i(this.option)) + }, setReadOnly: function (t) { + }, parsePath: function (t) { + return "string" == typeof t && (t = t.split(".")), t + }, customizeGetParent: function (t) { + ZS(this).getParent = t + }, isAnimationEnabled: function () { + if (!a_.node) { + if (null != this.option.animation) return !!this.option.animation; + if (this.parentModel) return this.parentModel.isAnimationEnabled() + } + } + }, Gi(wo), Wi(wo), HS(wo, ub), HS(wo, cb), HS(wo, GS), HS(wo, FS); + var US = 0, XS = 1e-4, jS = 9007199254740991, + YS = /^(?:(\d{4})(?:[-\/](\d{1,2})(?:[-\/](\d{1,2})(?:[T ](\d{1,2})(?::(\d\d)(?::(\d\d)(?:[.,](\d+))?)?)?(Z|[\+\-]\d\d:?\d\d)?)?)?)?)?$/, + qS = (Object.freeze || Object)({ + linearMap: To, + parsePercent: Do, + round: Ao, + asc: Co, + getPrecision: Lo, + getPrecisionSafe: ko, + getPixelPrecision: Po, + getPercentWithPrecision: No, + MAX_SAFE_INTEGER: jS, + remRadian: Oo, + isRadianAroundZero: Eo, + parseDate: Ro, + quantity: zo, + nice: Vo, + reformIntervals: Go, + isNumeric: Wo + }), KS = L, $S = /([&<>"'])/g, JS = {"&": "&", "<": "<", ">": ">", '"': """, "'": "'"}, + QS = ["a", "b", "c", "d", "e", "f", "g"], tM = function (t, e) { + return "{" + t + (null == e ? "" : e) + "}" + }, eM = be, iM = me, nM = (Object.freeze || Object)({ + addCommas: Fo, + toCamelCase: Ho, + normalizeCssArray: KS, + encodeHTML: Zo, + formatTpl: Uo, + formatTplSimple: Xo, + getTooltipMarker: jo, + formatTime: qo, + capitalFirst: Ko, + truncateText: eM, + getTextRect: iM + }), oM = d, aM = ["left", "right", "top", "bottom", "width", "height"], + rM = [["width", "left", "right"], ["height", "top", "bottom"]], sM = $o, + lM = (v($o, "vertical"), v($o, "horizontal"), { + getBoxLayoutParams: function () { + return { + left: this.get("left"), + top: this.get("top"), + right: this.get("right"), + bottom: this.get("bottom"), + width: this.get("width"), + height: this.get("height") + } + } + }), uM = Ni(), hM = wo.extend({ + type: "component", + id: "", + name: "", + mainType: "", + subType: "", + componentIndex: 0, + defaultOption: null, + ecModel: null, + dependentModels: [], + uid: null, + layoutMode: null, + $constructor: function (t, e, i, n) { + wo.call(this, t, e, i, n), this.uid = Mo("ec_cpt_model") + }, + init: function (t, e, i, n) { + this.mergeDefaultAndTheme(t, i) + }, + mergeDefaultAndTheme: function (t, e) { + var i = this.layoutMode, o = i ? na(t) : {}; + n(t, e.getTheme().get(this.mainType)), n(t, this.getDefaultOption()), i && ia(t, o, i) + }, + mergeOption: function (t, e) { + n(this.option, t, !0); + var i = this.layoutMode; + i && ia(this.option, t, i) + }, + optionUpdated: function (t, e) { + }, + getDefaultOption: function () { + var t = uM(this); + if (!t.defaultOption) { + for (var e = [], i = this.constructor; i;) { + var o = i.prototype.defaultOption; + o && e.push(o), i = i.superClass + } + for (var a = {}, r = e.length - 1; r >= 0; r--) a = n(a, e[r], !0); + t.defaultOption = a + } + return t.defaultOption + }, + getReferringComponents: function (t) { + return this.ecModel.queryComponents({ + mainType: t, + index: this.get(t + "Index", !0), + id: this.get(t + "Id", !0) + }) + } + }); + Zi(hM, {registerWhenExtend: !0}), function (t) { + var e = {}; + t.registerSubTypeDefaulter = function (t, i) { + t = Bi(t), e[t.main] = i + }, t.determineSubType = function (i, n) { + var o = n.type; + if (!o) { + var a = Bi(i).main; + t.hasSubTypes(i) && e[a] && (o = e[a](n)) + } + return o + } + }(hM), function (t, e) { + function i(t) { + var i = {}, a = []; + return d(t, function (r) { + var s = n(i, r), u = o(s.originalDeps = e(r), t); + s.entryCount = u.length, 0 === s.entryCount && a.push(r), d(u, function (t) { + l(s.predecessor, t) < 0 && s.predecessor.push(t); + var e = n(i, t); + l(e.successor, t) < 0 && e.successor.push(r) + }) + }), {graph: i, noEntryList: a} + } + + function n(t, e) { + return t[e] || (t[e] = {predecessor: [], successor: []}), t[e] + } + + function o(t, e) { + var i = []; + return d(t, function (t) { + l(e, t) >= 0 && i.push(t) + }), i + } + + t.topologicalTravel = function (t, e, n, o) { + function a(t) { + s[t].entryCount--, 0 === s[t].entryCount && l.push(t) + } + + if (t.length) { + var r = i(e), s = r.graph, l = r.noEntryList, u = {}; + for (d(t, function (t) { + u[t] = !0 + }); l.length;) { + var h = l.pop(), c = s[h], f = !!u[h]; + f && (n.call(o, h, c.originalDeps.slice()), delete u[h]), d(c.successor, f ? function (t) { + u[t] = !0, a(t) + } : a) + } + d(u, function () { + throw new Error("Circle dependency may exists") + }) + } + } + }(hM, function (t) { + var e = []; + return d(hM.getClassesByMainType(t), function (t) { + e = e.concat(t.prototype.dependencies || []) + }), e = f(e, function (t) { + return Bi(t).main + }), "dataset" !== t && l(e, "dataset") <= 0 && e.unshift("dataset"), e + }), h(hM, lM); + var cM = ""; + "undefined" != typeof navigator && (cM = navigator.platform || ""); + var dM = { + color: ["#c23531", "#2f4554", "#61a0a8", "#d48265", "#91c7ae", "#749f83", "#ca8622", "#bda29a", "#6e7074", "#546570", "#c4ccd3"], + gradientColor: ["#f6efa6", "#d88273", "#bf444c"], + textStyle: { + fontFamily: cM.match(/^Win/) ? "Microsoft YaHei" : "sans-serif", + fontSize: 12, + fontStyle: "normal", + fontWeight: "normal" + }, + blendMode: null, + animation: "auto", + animationDuration: 1e3, + animationDurationUpdate: 300, + animationEasing: "exponentialOut", + animationEasingUpdate: "cubicOut", + animationThreshold: 2e3, + progressiveThreshold: 3e3, + progressive: 400, + hoverLayerThreshold: 3e3, + useUTC: !1 + }, fM = Ni(), pM = { + clearColorPalette: function () { + fM(this).colorIdx = 0, fM(this).colorNameMap = {} + }, getColorFromPalette: function (t, e, i) { + var n = fM(e = e || this), o = n.colorIdx || 0, a = n.colorNameMap = n.colorNameMap || {}; + if (a.hasOwnProperty(t)) return a[t]; + var r = Si(this.get("color", !0)), s = this.get("colorLayer", !0), l = null != i && s ? aa(s, i) : r; + if ((l = l || r) && l.length) { + var u = l[o]; + return t && (a[t] = u), n.colorIdx = (o + 1) % l.length, u + } + } + }, gM = { + cartesian2d: function (t, e, i, n) { + var o = t.getReferringComponents("xAxis")[0], a = t.getReferringComponents("yAxis")[0]; + e.coordSysDims = ["x", "y"], i.set("x", o), i.set("y", a), sa(o) && (n.set("x", o), e.firstCategoryDimIndex = 0), sa(a) && (n.set("y", a), e.firstCategoryDimIndex = 1) + }, singleAxis: function (t, e, i, n) { + var o = t.getReferringComponents("singleAxis")[0]; + e.coordSysDims = ["single"], i.set("single", o), sa(o) && (n.set("single", o), e.firstCategoryDimIndex = 0) + }, polar: function (t, e, i, n) { + var o = t.getReferringComponents("polar")[0], a = o.findAxisModel("radiusAxis"), + r = o.findAxisModel("angleAxis"); + e.coordSysDims = ["radius", "angle"], i.set("radius", a), i.set("angle", r), sa(a) && (n.set("radius", a), e.firstCategoryDimIndex = 0), sa(r) && (n.set("angle", r), e.firstCategoryDimIndex = 1) + }, geo: function (t, e, i, n) { + e.coordSysDims = ["lng", "lat"] + }, parallel: function (t, e, i, n) { + var o = t.ecModel, a = o.getComponent("parallel", t.get("parallelIndex")), + r = e.coordSysDims = a.dimensions.slice(); + d(a.parallelAxisIndex, function (t, a) { + var s = o.getComponent("parallelAxis", t), l = r[a]; + i.set(l, s), sa(s) && null == e.firstCategoryDimIndex && (n.set(l, s), e.firstCategoryDimIndex = a) + }) + } + }, mM = "original", vM = "arrayRows", yM = "objectRows", xM = "keyedColumns", _M = "unknown", wM = "typedArray", + bM = "column", SM = "row"; + la.seriesDataToSource = function (t) { + return new la({data: t, sourceFormat: S(t) ? wM : mM, fromDataset: !1}) + }, Wi(la); + var MM = Ni(), IM = "\0_ec_inner", TM = wo.extend({ + init: function (t, e, i, n) { + i = i || {}, this.option = null, this._theme = new wo(i), this._optionManager = n + }, setOption: function (t, e) { + k(!(IM in t), "please use chart.getOption()"), this._optionManager.setOption(t, e), this.resetOption(null) + }, resetOption: function (t) { + var e = !1, i = this._optionManager; + if (!t || "recreate" === t) { + var n = i.mountOption("recreate" === t); + this.option && "recreate" !== t ? (this.restoreData(), this.mergeOption(n)) : Sa.call(this, n), e = !0 + } + if ("timeline" !== t && "media" !== t || this.restoreData(), !t || "recreate" === t || "timeline" === t) { + var o = i.getTimelineOption(this); + o && (this.mergeOption(o), e = !0) + } + if (!t || "recreate" === t || "media" === t) { + var a = i.getMediaOption(this, this._api); + a.length && d(a, function (t) { + this.mergeOption(t, e = !0) + }, this) + } + return e + }, mergeOption: function (t) { + var e = this.option, o = this._componentsMap, r = []; + ca(this), d(t, function (t, o) { + null != t && (hM.hasClass(o) ? o && r.push(o) : e[o] = null == e[o] ? i(t) : n(e[o], t, !0)) + }), hM.topologicalTravel(r, hM.getAllClassMainTypes(), function (i, n) { + var r = Si(t[i]), s = Di(o.get(i), r); + Ai(s), d(s, function (t, e) { + var n = t.option; + w(n) && (t.keyInfo.mainType = i, t.keyInfo.subType = Ia(i, n, t.exist)) + }); + var l = Ma(o, n); + e[i] = [], o.set(i, []), d(s, function (t, n) { + var r = t.exist, s = t.option; + if (k(w(s) || r, "Empty component definition"), s) { + var u = hM.getClass(i, t.keyInfo.subType, !0); + if (r && r instanceof u) r.name = t.keyInfo.name, r.mergeOption(s, this), r.optionUpdated(s, !1); else { + var h = a({dependentModels: l, componentIndex: n}, t.keyInfo); + a(r = new u(s, this, this, h), h), r.init(s, this, this, h), r.optionUpdated(null, !0) + } + } else r.mergeOption({}, this), r.optionUpdated({}, !1); + o.get(i)[n] = r, e[i][n] = r.option + }, this), "series" === i && Ta(this, o.get("series")) + }, this), this._seriesIndicesMap = R(this._seriesIndices = this._seriesIndices || []) + }, getOption: function () { + var t = i(this.option); + return d(t, function (e, i) { + if (hM.hasClass(i)) { + for (var n = (e = Si(e)).length - 1; n >= 0; n--) Li(e[n]) && e.splice(n, 1); + t[i] = e + } + }), delete t[IM], t + }, getTheme: function () { + return this._theme + }, getComponent: function (t, e) { + var i = this._componentsMap.get(t); + if (i) return i[e || 0] + }, queryComponents: function (t) { + var e = t.mainType; + if (!e) return []; + var i = t.index, n = t.id, o = t.name, a = this._componentsMap.get(e); + if (!a || !a.length) return []; + var r; + if (null != i) y(i) || (i = [i]), r = g(f(i, function (t) { + return a[t] + }), function (t) { + return !!t + }); else if (null != n) { + var s = y(n); + r = g(a, function (t) { + return s && l(n, t.id) >= 0 || !s && t.id === n + }) + } else if (null != o) { + var u = y(o); + r = g(a, function (t) { + return u && l(o, t.name) >= 0 || !u && t.name === o + }) + } else r = a.slice(); + return Da(r, t) + }, findComponents: function (t) { + var e = t.query, i = t.mainType, n = function (t) { + var e = i + "Index", n = i + "Id", o = i + "Name"; + return !t || null == t[e] && null == t[n] && null == t[o] ? null : { + mainType: i, + index: t[e], + id: t[n], + name: t[o] + } + }(e); + return function (e) { + return t.filter ? g(e, t.filter) : e + }(Da(n ? this.queryComponents(n) : this._componentsMap.get(i), t)) + }, eachComponent: function (t, e, i) { + var n = this._componentsMap; + "function" == typeof t ? (i = e, e = t, n.each(function (t, n) { + d(t, function (t, o) { + e.call(i, n, t, o) + }) + })) : _(t) ? d(n.get(t), e, i) : w(t) && d(this.findComponents(t), e, i) + }, getSeriesByName: function (t) { + return g(this._componentsMap.get("series"), function (e) { + return e.name === t + }) + }, getSeriesByIndex: function (t) { + return this._componentsMap.get("series")[t] + }, getSeriesByType: function (t) { + return g(this._componentsMap.get("series"), function (e) { + return e.subType === t + }) + }, getSeries: function () { + return this._componentsMap.get("series").slice() + }, getSeriesCount: function () { + return this._componentsMap.get("series").length + }, eachSeries: function (t, e) { + d(this._seriesIndices, function (i) { + var n = this._componentsMap.get("series")[i]; + t.call(e, n, i) + }, this) + }, eachRawSeries: function (t, e) { + d(this._componentsMap.get("series"), t, e) + }, eachSeriesByType: function (t, e, i) { + d(this._seriesIndices, function (n) { + var o = this._componentsMap.get("series")[n]; + o.subType === t && e.call(i, o, n) + }, this) + }, eachRawSeriesByType: function (t, e, i) { + return d(this.getSeriesByType(t), e, i) + }, isSeriesFiltered: function (t) { + return null == this._seriesIndicesMap.get(t.componentIndex) + }, getCurrentSeriesIndices: function () { + return (this._seriesIndices || []).slice() + }, filterSeries: function (t, e) { + Ta(this, g(this._componentsMap.get("series"), t, e)) + }, restoreData: function (t) { + var e = this._componentsMap; + Ta(this, e.get("series")); + var i = []; + e.each(function (t, e) { + i.push(e) + }), hM.topologicalTravel(i, hM.getAllClassMainTypes(), function (i, n) { + d(e.get(i), function (e) { + ("series" !== i || !wa(e, t)) && e.restoreData() + }) + }) + } + }); + h(TM, pM); + var DM = ["getDom", "getZr", "getWidth", "getHeight", "getDevicePixelRatio", "dispatchAction", "isDisposed", "on", "off", "getDataURL", "getConnectedDataURL", "getModel", "getOption", "getViewOfComponentModel", "getViewOfSeriesModel"], + AM = {}; + Ca.prototype = { + constructor: Ca, create: function (t, e) { + var i = []; + d(AM, function (n, o) { + var a = n.create(t, e); + i = i.concat(a || []) + }), this._coordinateSystems = i + }, update: function (t, e) { + d(this._coordinateSystems, function (i) { + i.update && i.update(t, e) + }) + }, getCoordinateSystems: function () { + return this._coordinateSystems.slice() + } + }, Ca.register = function (t, e) { + AM[t] = e + }, Ca.get = function (t) { + return AM[t] + }; + var CM = d, LM = i, kM = f, PM = n, NM = /^(min|max)?(.+)$/; + La.prototype = { + constructor: La, setOption: function (t, e) { + t && d(Si(t.series), function (t) { + t && t.data && S(t.data) && N(t.data) + }), t = LM(t, !0); + var i = this._optionBackup, n = ka.call(this, t, e, !i); + this._newBaseOption = n.baseOption, i ? (Ea(i.baseOption, n.baseOption), n.timelineOptions.length && (i.timelineOptions = n.timelineOptions), n.mediaList.length && (i.mediaList = n.mediaList), n.mediaDefault && (i.mediaDefault = n.mediaDefault)) : this._optionBackup = n + }, mountOption: function (t) { + var e = this._optionBackup; + return this._timelineOptions = kM(e.timelineOptions, LM), this._mediaList = kM(e.mediaList, LM), this._mediaDefault = LM(e.mediaDefault), this._currentMediaIndices = [], LM(t ? e.baseOption : this._newBaseOption) + }, getTimelineOption: function (t) { + var e, i = this._timelineOptions; + if (i.length) { + var n = t.getComponent("timeline"); + n && (e = LM(i[n.getCurrentIndex()], !0)) + } + return e + }, getMediaOption: function (t) { + var e = this._api.getWidth(), i = this._api.getHeight(), n = this._mediaList, o = this._mediaDefault, + a = [], r = []; + if (!n.length && !o) return r; + for (var s = 0, l = n.length; s < l; s++) Pa(n[s].query, e, i) && a.push(s); + return !a.length && o && (a = [-1]), a.length && !Oa(a, this._currentMediaIndices) && (r = kM(a, function (t) { + return LM(-1 === t ? o.option : n[t].option) + })), this._currentMediaIndices = a, r + } + }; + var OM = d, EM = w, RM = ["areaStyle", "lineStyle", "nodeStyle", "linkStyle", "chordStyle", "label", "labelLine"], + zM = function (t, e) { + OM(Fa(t.series), function (t) { + EM(t) && Wa(t) + }); + var i = ["xAxis", "yAxis", "radiusAxis", "angleAxis", "singleAxis", "parallelAxis", "radar"]; + e && i.push("valueAxis", "categoryAxis", "logAxis", "timeAxis"), OM(i, function (e) { + OM(Fa(t[e]), function (t) { + t && (Va(t, "axisLabel"), Va(t.axisPointer, "label")) + }) + }), OM(Fa(t.parallel), function (t) { + var e = t && t.parallelAxisDefault; + Va(e, "axisLabel"), Va(e && e.axisPointer, "label") + }), OM(Fa(t.calendar), function (t) { + za(t, "itemStyle"), Va(t, "dayLabel"), Va(t, "monthLabel"), Va(t, "yearLabel") + }), OM(Fa(t.radar), function (t) { + Va(t, "name") + }), OM(Fa(t.geo), function (t) { + EM(t) && (Ga(t), OM(Fa(t.regions), function (t) { + Ga(t) + })) + }), OM(Fa(t.timeline), function (t) { + Ga(t), za(t, "label"), za(t, "itemStyle"), za(t, "controlStyle", !0); + var e = t.data; + y(e) && d(e, function (t) { + w(t) && (za(t, "label"), za(t, "itemStyle")) + }) + }), OM(Fa(t.toolbox), function (t) { + za(t, "iconStyle"), OM(t.feature, function (t) { + za(t, "iconStyle") + }) + }), Va(Ha(t.axisPointer), "label"), Va(Ha(t.tooltip).axisPointer, "label") + }, BM = [["x", "left"], ["y", "top"], ["x2", "right"], ["y2", "bottom"]], + VM = ["grid", "geo", "parallel", "legend", "toolbox", "title", "visualMap", "dataZoom", "timeline"], + GM = function (t, e) { + zM(t, e), t.series = Si(t.series), d(t.series, function (t) { + if (w(t)) { + var e = t.type; + if ("pie" !== e && "gauge" !== e || null != t.clockWise && (t.clockwise = t.clockWise), "gauge" === e) { + var i = Za(t, "pointer.color"); + null != i && Ua(t, "itemStyle.normal.color", i) + } + Xa(t) + } + }), t.dataRange && (t.visualMap = t.dataRange), d(VM, function (e) { + var i = t[e]; + i && (y(i) || (i = [i]), d(i, function (t) { + Xa(t) + })) + }) + }, WM = Ya.prototype; + WM.pure = !1, WM.persistent = !0, WM.getSource = function () { + return this._source + }; + var FM = { + arrayRows_column: { + pure: !0, count: function () { + return Math.max(0, this._data.length - this._source.startIndex) + }, getItem: function (t) { + return this._data[t + this._source.startIndex] + }, appendData: $a + }, + arrayRows_row: { + pure: !0, count: function () { + var t = this._data[0]; + return t ? Math.max(0, t.length - this._source.startIndex) : 0 + }, getItem: function (t) { + t += this._source.startIndex; + for (var e = [], i = this._data, n = 0; n < i.length; n++) { + var o = i[n]; + e.push(o ? o[t] : null) + } + return e + }, appendData: function () { + throw new Error('Do not support appendData when set seriesLayoutBy: "row".') + } + }, + objectRows: {pure: !0, count: qa, getItem: Ka, appendData: $a}, + keyedColumns: { + pure: !0, count: function () { + var t = this._source.dimensionsDefine[0].name, e = this._data[t]; + return e ? e.length : 0 + }, getItem: function (t) { + for (var e = [], i = this._source.dimensionsDefine, n = 0; n < i.length; n++) { + var o = this._data[i[n].name]; + e.push(o ? o[t] : null) + } + return e + }, appendData: function (t) { + var e = this._data; + d(t, function (t, i) { + for (var n = e[i] || (e[i] = []), o = 0; o < (t || []).length; o++) n.push(t[o]) + }) + } + }, + original: {count: qa, getItem: Ka, appendData: $a}, + typedArray: { + persistent: !1, pure: !0, count: function () { + return this._data ? this._data.length / this._dimSize : 0 + }, getItem: function (t, e) { + t -= this._offset, e = e || []; + for (var i = this._dimSize * t, n = 0; n < this._dimSize; n++) e[n] = this._data[i + n]; + return e + }, appendData: function (t) { + this._data = t + }, clean: function () { + this._offset += this.count(), this._data = null + } + } + }, HM = { + arrayRows: Ja, objectRows: function (t, e, i, n) { + return null != i ? t[n] : t + }, keyedColumns: Ja, original: function (t, e, i, n) { + var o = Ii(t); + return null != i && o instanceof Array ? o[i] : o + }, typedArray: Ja + }, ZM = { + arrayRows: Qa, objectRows: function (t, e, i, n) { + return tr(t[e], this._dimensionInfos[e]) + }, keyedColumns: Qa, original: function (t, e, i, n) { + var o = t && (null == t.value ? t : t.value); + return !this._rawData.pure && Ti(t) && (this.hasItemOption = !0), tr(o instanceof Array ? o[n] : o, this._dimensionInfos[e]) + }, typedArray: function (t, e, i, n) { + return t[n] + } + }, UM = /\{@(.+?)\}/g, XM = { + getDataParams: function (t, e) { + var i = this.getData(e), n = this.getRawValue(t, e), o = i.getRawIndex(t), a = i.getName(t), + r = i.getRawDataItem(t), s = i.getItemVisual(t, "color"); + return { + componentType: this.mainType, + componentSubType: this.subType, + seriesType: "series" === this.mainType ? this.subType : null, + seriesIndex: this.seriesIndex, + seriesId: this.id, + seriesName: this.name, + name: a, + dataIndex: o, + data: r, + dataType: e, + value: n, + color: s, + marker: jo(s), + $vars: ["seriesName", "name", "value"] + } + }, getFormattedLabel: function (t, e, i, n, o) { + e = e || "normal"; + var a = this.getData(i), r = a.getItemModel(t), s = this.getDataParams(t, i); + null != n && s.value instanceof Array && (s.value = s.value[n]); + var l = r.get("normal" === e ? [o || "label", "formatter"] : [e, o || "label", "formatter"]); + return "function" == typeof l ? (s.status = e, l(s)) : "string" == typeof l ? Uo(l, s).replace(UM, function (e, i) { + var n = i.length; + return "[" === i.charAt(0) && "]" === i.charAt(n - 1) && (i = +i.slice(1, n - 1)), er(a, t, i) + }) : void 0 + }, getRawValue: function (t, e) { + return er(this.getData(e), t) + }, formatTooltip: function () { + } + }, jM = or.prototype; + jM.perform = function (t) { + function e(t) { + return !(t >= 1) && (t = 1), t + } + + var i = this._upstream, n = t && t.skip; + if (this._dirty && i) { + var o = this.context; + o.data = o.outputData = i.context.outputData + } + this.__pipeline && (this.__pipeline.currentTask = this); + var a; + this._plan && !n && (a = this._plan(this.context)); + var r = e(this._modBy), s = this._modDataCount || 0, l = e(t && t.modBy), u = t && t.modDataCount || 0; + r === l && s === u || (a = "reset"); + var h; + (this._dirty || "reset" === a) && (this._dirty = !1, h = rr(this, n)), this._modBy = l, this._modDataCount = u; + var c = t && t.step; + if (this._dueEnd = i ? i._outputDueEnd : this._count ? this._count(this.context) : 1 / 0, this._progress) { + var d = this._dueIndex, f = Math.min(null != c ? this._dueIndex + c : 1 / 0, this._dueEnd); + if (!n && (h || d < f)) { + var p = this._progress; + if (y(p)) for (var g = 0; g < p.length; g++) ar(this, p[g], d, f, l, u); else ar(this, p, d, f, l, u) + } + this._dueIndex = f; + var m = null != this._settedOutputEnd ? this._settedOutputEnd : f; + this._outputDueEnd = m + } else this._dueIndex = this._outputDueEnd = null != this._settedOutputEnd ? this._settedOutputEnd : this._dueEnd; + return this.unfinished() + }; + var YM = function () { + function t() { + return n < i ? n++ : null + } + + function e() { + var t = n % r * o + Math.ceil(n / r), e = n >= i ? null : t < a ? t : n; + return n++, e + } + + var i, n, o, a, r, s = { + reset: function (l, u, h, c) { + n = l, i = u, o = h, a = c, r = Math.ceil(a / o), s.next = o > 1 && a > 0 ? e : t + } + }; + return s + }(); + jM.dirty = function () { + this._dirty = !0, this._onDirty && this._onDirty(this.context) + }, jM.unfinished = function () { + return this._progress && this._dueIndex < this._dueEnd + }, jM.pipe = function (t) { + (this._downstream !== t || this._dirty) && (this._downstream = t, t._upstream = this, t.dirty()) + }, jM.dispose = function () { + this._disposed || (this._upstream && (this._upstream._downstream = null), this._downstream && (this._downstream._upstream = null), this._dirty = !1, this._disposed = !0) + }, jM.getUpstream = function () { + return this._upstream + }, jM.getDownstream = function () { + return this._downstream + }, jM.setOutputEnd = function (t) { + this._outputDueEnd = this._settedOutputEnd = t + }; + var qM = Ni(), KM = hM.extend({ + type: "series.__base__", + seriesIndex: 0, + coordinateSystem: null, + defaultOption: null, + legendDataProvider: null, + visualColorAccessPath: "itemStyle.color", + layoutMode: null, + init: function (t, e, i, n) { + this.seriesIndex = this.componentIndex, this.dataTask = nr({ + count: ur, + reset: hr + }), this.dataTask.context = {model: this}, this.mergeDefaultAndTheme(t, i), da(this); + var o = this.getInitialData(t, i); + dr(o, this), this.dataTask.context.data = o, qM(this).dataBeforeProcessed = o, sr(this) + }, + mergeDefaultAndTheme: function (t, e) { + var i = this.layoutMode, o = i ? na(t) : {}, a = this.subType; + hM.hasClass(a) && (a += "Series"), n(t, e.getTheme().get(this.subType)), n(t, this.getDefaultOption()), Mi(t, "label", ["show"]), this.fillDataTextStyle(t.data), i && ia(t, o, i) + }, + mergeOption: function (t, e) { + t = n(this.option, t, !0), this.fillDataTextStyle(t.data); + var i = this.layoutMode; + i && ia(this.option, t, i), da(this); + var o = this.getInitialData(t, e); + dr(o, this), this.dataTask.dirty(), this.dataTask.context.data = o, qM(this).dataBeforeProcessed = o, sr(this) + }, + fillDataTextStyle: function (t) { + if (t && !S(t)) for (var e = ["show"], i = 0; i < t.length; i++) t[i] && t[i].label && Mi(t[i], "label", e) + }, + getInitialData: function () { + }, + appendData: function (t) { + this.getRawData().appendData(t.data) + }, + getData: function (t) { + var e = pr(this); + if (e) { + var i = e.context.data; + return null == t ? i : i.getLinkedData(t) + } + return qM(this).data + }, + setData: function (t) { + var e = pr(this); + if (e) { + var i = e.context; + i.data !== t && e.modifyOutputEnd && e.setOutputEnd(t.count()), i.outputData = t, e !== this.dataTask && (i.data = t) + } + qM(this).data = t + }, + getSource: function () { + return ha(this) + }, + getRawData: function () { + return qM(this).dataBeforeProcessed + }, + getBaseAxis: function () { + var t = this.coordinateSystem; + return t && t.getBaseAxis && t.getBaseAxis() + }, + formatTooltip: function (t, e, i) { + function n(t) { + return Zo(Fo(t)) + } + + var o = this.getData(), a = o.mapDimension("defaultedTooltip", !0), r = a.length, s = this.getRawValue(t), + l = y(s), u = o.getItemVisual(t, "color"); + w(u) && u.colorStops && (u = (u.colorStops[0] || {}).color), u = u || "transparent"; + var h = r > 1 || l && !r ? function (i) { + function n(t, i) { + var n = o.getDimensionInfo(i); + if (n && !1 !== n.otherDims.tooltip) { + var a = n.type, l = jo({color: u, type: "subItem"}), + h = (r ? l + Zo(n.displayName || "-") + ": " : "") + Zo("ordinal" === a ? t + "" : "time" === a ? e ? "" : qo("yyyy/MM/dd hh:mm:ss", t) : Fo(t)); + h && s.push(h) + } + } + + var r = p(i, function (t, e, i) { + var n = o.getDimensionInfo(i); + return t |= n && !1 !== n.tooltip && null != n.displayName + }, 0), s = []; + return a.length ? d(a, function (e) { + n(er(o, t, e), e) + }) : d(i, n), (r ? "
" : "") + s.join(r ? "
" : ", ") + }(s) : n(r ? er(o, t, a[0]) : l ? s[0] : s), c = jo(u), f = o.getName(t), g = this.name; + return Ci(this) || (g = ""), g = g ? Zo(g) + (e ? ": " : "
") : "", e ? c + g + h : g + c + (f ? Zo(f) + ": " + h : h) + }, + isAnimationEnabled: function () { + if (a_.node) return !1; + var t = this.getShallow("animation"); + return t && this.getData().count() > this.getShallow("animationThreshold") && (t = !1), t + }, + restoreData: function () { + this.dataTask.dirty() + }, + getColorFromPalette: function (t, e, i) { + var n = this.ecModel, o = pM.getColorFromPalette.call(this, t, e, i); + return o || (o = n.getColorFromPalette(t, e, i)), o + }, + coordDimToDataDim: function (t) { + return this.getRawData().mapDimension(t, !0) + }, + getProgressive: function () { + return this.get("progressive") + }, + getProgressiveThreshold: function () { + return this.get("progressiveThreshold") + }, + getAxisTooltipData: null, + getTooltipPosition: null, + pipeTask: null, + preventIncremental: null, + pipelineContext: null + }); + h(KM, XM), h(KM, pM); + var $M = function () { + this.group = new sw, this.uid = Mo("viewComponent") + }; + $M.prototype = { + constructor: $M, init: function (t, e) { + }, render: function (t, e, i, n) { + }, dispose: function () { + } + }; + var JM = $M.prototype; + JM.updateView = JM.updateLayout = JM.updateVisual = function (t, e, i, n) { + }, Gi($M), Zi($M, {registerWhenExtend: !0}); + var QM = function () { + var t = Ni(); + return function (e) { + var i = t(e), n = e.pipelineContext, o = i.large, a = i.progressiveRender, r = i.large = n.large, + s = i.progressiveRender = n.progressiveRender; + return !!(o ^ r || a ^ s) && "reset" + } + }, tI = Ni(), eI = QM(); + gr.prototype = { + type: "chart", init: function (t, e) { + }, render: function (t, e, i, n) { + }, highlight: function (t, e, i, n) { + vr(t.getData(), n, "emphasis") + }, downplay: function (t, e, i, n) { + vr(t.getData(), n, "normal") + }, remove: function (t, e) { + this.group.removeAll() + }, dispose: function () { + }, incrementalPrepareRender: null, incrementalRender: null, updateTransform: null + }; + var iI = gr.prototype; + iI.updateView = iI.updateLayout = iI.updateVisual = function (t, e, i, n) { + this.render(t, e, i, n) + }, Gi(gr), Zi(gr, {registerWhenExtend: !0}), gr.markUpdateMethod = function (t, e) { + tI(t).updateMethod = e + }; + var nI = { + incrementalPrepareRender: { + progress: function (t, e) { + e.view.incrementalRender(t, e.model, e.ecModel, e.api, e.payload) + } + }, render: { + forceFirstProgress: !0, progress: function (t, e) { + e.view.render(e.model, e.ecModel, e.api, e.payload) + } + } + }, oI = "\0__throttleOriginMethod", aI = "\0__throttleRate", rI = "\0__throttleType", sI = { + createOnAllSeries: !0, performRawSeries: !0, reset: function (t, e) { + var i = t.getData(), n = (t.visualColorAccessPath || "itemStyle.color").split("."), + o = t.get(n) || t.getColorFromPalette(t.name, null, e.getSeriesCount()); + if (i.setVisual("color", o), !e.isSeriesFiltered(t)) { + "function" != typeof o || o instanceof CS || i.each(function (e) { + i.setItemVisual(e, "color", o(t.getDataParams(e))) + }); + return { + dataEach: i.hasItemOption ? function (t, e) { + var i = t.getItemModel(e).get(n, !0); + null != i && t.setItemVisual(e, "color", i) + } : null + } + } + } + }, lI = { + toolbox: { + brush: { + title: { + rect: "矩形选择", + polygon: "圈选", + lineX: "横向选择", + lineY: "纵向选择", + keep: "保持选择", + clear: "清除选择" + } + }, + dataView: {title: "数据视图", lang: ["数据视图", "关闭", "刷新"]}, + dataZoom: {title: {zoom: "区域缩放", back: "区域缩放还原"}}, + magicType: {title: {line: "切换为折线图", bar: "切换为柱状图", stack: "切换为堆叠", tiled: "切换为平铺"}}, + restore: {title: "还原"}, + saveAsImage: {title: "保存为图片", lang: ["右键另存为图片"]} + }, + series: { + typeNames: { + pie: "饼图", + bar: "柱状图", + line: "折线图", + scatter: "散点图", + effectScatter: "涟漪散点图", + radar: "雷达图", + tree: "树图", + treemap: "矩形树图", + boxplot: "箱型图", + candlestick: "K线图", + k: "K线图", + heatmap: "热力图", + map: "地图", + parallel: "平行坐标图", + lines: "线图", + graph: "关系图", + sankey: "桑基图", + funnel: "漏斗图", + gauge: "仪表盘图", + pictorialBar: "象形柱图", + themeRiver: "主题河流图", + sunburst: "旭日图" + } + }, + aria: { + general: {withTitle: "这是一个关于“{title}”的图表。", withoutTitle: "这是一个图表,"}, + series: { + single: { + prefix: "", + withName: "图表类型是{seriesType},表示{seriesName}。", + withoutName: "图表类型是{seriesType}。" + }, + multiple: { + prefix: "它由{seriesCount}个图表系列组成。", + withName: "第{seriesId}个系列是一个表示{seriesName}的{seriesType},", + withoutName: "第{seriesId}个系列是一个{seriesType},", + separator: {middle: ";", end: "。"} + } + }, + data: { + allData: "其数据是——", + partialData: "其中,前{displayCnt}项是——", + withName: "{name}的数据是{value}", + withoutName: "{value}", + separator: {middle: ",", end: ""} + } + } + }, uI = function (t, e) { + function i(t, e) { + if ("string" != typeof t) return t; + var i = t; + return d(e, function (t, e) { + i = i.replace(new RegExp("\\{\\s*" + e + "\\s*\\}", "g"), t) + }), i + } + + function n(t) { + var e = a.get(t); + if (null == e) { + for (var i = t.split("."), n = lI.aria, o = 0; o < i.length; ++o) n = n[i[o]]; + return n + } + return e + } + + function o(t) { + return lI.series.typeNames[t] || "自定义图" + } + + var a = e.getModel("aria"); + if (a.get("show")) if (a.get("description")) t.setAttribute("aria-label", a.get("description")); else { + var r = 0; + e.eachSeries(function (t, e) { + ++r + }, this); + var s, l = a.get("data.maxCount") || 10, u = a.get("series.maxCount") || 10, h = Math.min(r, u); + if (!(r < 1)) { + var c = function () { + var t = e.getModel("title").option; + return t && t.length && (t = t[0]), t && t.text + }(); + s = c ? i(n("general.withTitle"), {title: c}) : n("general.withoutTitle"); + var f = []; + s += i(n(r > 1 ? "series.multiple.prefix" : "series.single.prefix"), {seriesCount: r}), e.eachSeries(function (t, e) { + if (e < h) { + var a, s = t.get("name"), u = "series." + (r > 1 ? "multiple" : "single") + "."; + a = i(a = n(s ? u + "withName" : u + "withoutName"), { + seriesId: t.seriesIndex, + seriesName: t.get("name"), + seriesType: o(t.subType) + }); + var c = t.getData(); + window.data = c, c.count() > l ? a += i(n("data.partialData"), {displayCnt: l}) : a += n("data.allData"); + for (var d = [], p = 0; p < c.count(); p++) if (p < l) { + var g = c.getName(p), m = er(c, p); + d.push(i(n(g ? "data.withName" : "data.withoutName"), {name: g, value: m})) + } + a += d.join(n("data.separator.middle")) + n("data.separator.end"), f.push(a) + } + }), s += f.join(n("series.multiple.separator.middle")) + n("series.multiple.separator.end"), t.setAttribute("aria-label", s) + } + } + }, hI = Math.PI, cI = Sr.prototype; + cI.restoreData = function (t, e) { + t.restoreData(e), this._stageTaskMap.each(function (t) { + var e = t.overallTask; + e && e.dirty() + }) + }, cI.getPerformArgs = function (t, e) { + if (t.__pipeline) { + var i = this._pipelineMap.get(t.__pipeline.id), n = i.context, + o = !e && i.progressiveEnabled && (!n || n.progressiveRender) && t.__idxInPipeline > i.blockIndex ? i.step : null, + a = n && n.modDataCount; + return {step: o, modBy: null != a ? Math.ceil(a / o) : null, modDataCount: a} + } + }, cI.getPipeline = function (t) { + return this._pipelineMap.get(t) + }, cI.updateStreamModes = function (t, e) { + var i = this._pipelineMap.get(t.uid), n = t.getData().count(), + o = i.progressiveEnabled && e.incrementalPrepareRender && n >= i.threshold, + a = t.get("large") && n >= t.get("largeThreshold"), r = "mod" === t.get("progressiveChunkMode") ? n : null; + t.pipelineContext = i.context = {progressiveRender: o, modDataCount: r, large: a} + }, cI.restorePipelines = function (t) { + var e = this, i = e._pipelineMap = R(); + t.eachSeries(function (t) { + var n = t.getProgressive(), o = t.uid; + i.set(o, { + id: o, + head: null, + tail: null, + threshold: t.getProgressiveThreshold(), + progressiveEnabled: n && !(t.preventIncremental && t.preventIncremental()), + blockIndex: -1, + step: Math.round(n || 700), + count: 0 + }), Er(e, t, t.dataTask) + }) + }, cI.prepareStageTasks = function () { + var t = this._stageTaskMap, e = this.ecInstance.getModel(), i = this.api; + d(this._allHandlers, function (n) { + var o = t.get(n.uid) || t.set(n.uid, []); + n.reset && Ir(this, n, o, e, i), n.overallReset && Tr(this, n, o, e, i) + }, this) + }, cI.prepareView = function (t, e, i, n) { + var o = t.renderTask, a = o.context; + a.model = e, a.ecModel = i, a.api = n, o.__block = !t.incrementalPrepareRender, Er(this, e, o) + }, cI.performDataProcessorTasks = function (t, e) { + Mr(this, this._dataProcessorHandlers, t, e, {block: !0}) + }, cI.performVisualTasks = function (t, e, i) { + Mr(this, this._visualHandlers, t, e, i) + }, cI.performSeriesTasks = function (t) { + var e; + t.eachSeries(function (t) { + e |= t.dataTask.perform() + }), this.unfinished |= e + }, cI.plan = function () { + this._pipelineMap.each(function (t) { + var e = t.tail; + do { + if (e.__block) { + t.blockIndex = e.__idxInPipeline; + break + } + e = e.getUpstream() + } while (e) + }) + }; + var dI = cI.updatePayload = function (t, e) { + "remain" !== e && (t.context.payload = e) + }, fI = Nr(0); + Sr.wrapStageHandler = function (t, e) { + return x(t) && (t = { + overallReset: t, + seriesType: Rr(t) + }), t.uid = Mo("stageHandler"), e && (t.visualType = e), t + }; + var pI, gI = {}, mI = {}; + zr(gI, TM), zr(mI, Aa), gI.eachSeriesByType = gI.eachRawSeriesByType = function (t) { + pI = t + }, gI.eachComponent = function (t) { + "series" === t.mainType && t.subType && (pI = t.subType) + }; + var vI = ["#37A2DA", "#32C5E9", "#67E0E3", "#9FE6B8", "#FFDB5C", "#ff9f7f", "#fb7293", "#E062AE", "#E690D1", "#e7bcf3", "#9d96f5", "#8378EA", "#96BFFF"], + yI = { + color: vI, + colorLayer: [["#37A2DA", "#ffd85c", "#fd7b5f"], ["#37A2DA", "#67E0E3", "#FFDB5C", "#ff9f7f", "#E062AE", "#9d96f5"], ["#37A2DA", "#32C5E9", "#9FE6B8", "#FFDB5C", "#ff9f7f", "#fb7293", "#e7bcf3", "#8378EA", "#96BFFF"], vI] + }, + xI = ["#dd6b66", "#759aa0", "#e69d87", "#8dc1a9", "#ea7e53", "#eedd78", "#73a373", "#73b9bc", "#7289ab", "#91ca8c", "#f49f42"], + _I = { + color: xI, + backgroundColor: "#333", + tooltip: {axisPointer: {lineStyle: {color: "#eee"}, crossStyle: {color: "#eee"}}}, + legend: {textStyle: {color: "#eee"}}, + textStyle: {color: "#eee"}, + title: {textStyle: {color: "#eee"}}, + toolbox: {iconStyle: {normal: {borderColor: "#eee"}}}, + dataZoom: {textStyle: {color: "#eee"}}, + visualMap: {textStyle: {color: "#eee"}}, + timeline: { + lineStyle: {color: "#eee"}, + itemStyle: {normal: {color: xI[1]}}, + label: {normal: {textStyle: {color: "#eee"}}}, + controlStyle: {normal: {color: "#eee", borderColor: "#eee"}} + }, + timeAxis: { + axisLine: {lineStyle: {color: "#eee"}}, + axisTick: {lineStyle: {color: "#eee"}}, + axisLabel: {textStyle: {color: "#eee"}}, + splitLine: {lineStyle: {type: "dashed", color: "#aaa"}}, + splitArea: {areaStyle: {color: "#eee"}} + }, + logAxis: { + axisLine: {lineStyle: {color: "#eee"}}, + axisTick: {lineStyle: {color: "#eee"}}, + axisLabel: {textStyle: {color: "#eee"}}, + splitLine: {lineStyle: {type: "dashed", color: "#aaa"}}, + splitArea: {areaStyle: {color: "#eee"}} + }, + valueAxis: { + axisLine: {lineStyle: {color: "#eee"}}, + axisTick: {lineStyle: {color: "#eee"}}, + axisLabel: {textStyle: {color: "#eee"}}, + splitLine: {lineStyle: {type: "dashed", color: "#aaa"}}, + splitArea: {areaStyle: {color: "#eee"}} + }, + categoryAxis: { + axisLine: {lineStyle: {color: "#eee"}}, + axisTick: {lineStyle: {color: "#eee"}}, + axisLabel: {textStyle: {color: "#eee"}}, + splitLine: {lineStyle: {type: "dashed", color: "#aaa"}}, + splitArea: {areaStyle: {color: "#eee"}} + }, + line: {symbol: "circle"}, + graph: {color: xI}, + gauge: {title: {textStyle: {color: "#eee"}}}, + candlestick: { + itemStyle: { + normal: { + color: "#FD1050", + color0: "#0CF49B", + borderColor: "#FD1050", + borderColor0: "#0CF49B" + } + } + } + }; + _I.categoryAxis.splitLine.show = !1, hM.extend({ + type: "dataset", + defaultOption: {seriesLayoutBy: bM, sourceHeader: null, dimensions: null, source: null}, + optionUpdated: function () { + ua(this) + } + }), $M.extend({type: "dataset"}); + var wI = k, bI = d, SI = x, MI = w, II = hM.parseClassType, TI = {zrender: "4.0.4"}, DI = 1e3, AI = 1e3, CI = 3e3, + LI = { + PROCESSOR: {FILTER: DI, STATISTIC: 5e3}, + VISUAL: {LAYOUT: AI, GLOBAL: 2e3, CHART: CI, COMPONENT: 4e3, BRUSH: 5e3} + }, kI = "__flagInMainProcess", PI = "__optionUpdated", NI = /^[a-zA-Z0-9_]+$/; + Vr.prototype.on = Br("on"), Vr.prototype.off = Br("off"), Vr.prototype.one = Br("one"), h(Vr, D_); + var OI = Gr.prototype; + OI._onframe = function () { + if (!this._disposed) { + var t = this._scheduler; + if (this[PI]) { + var e = this[PI].silent; + this[kI] = !0, Fr(this), EI.update.call(this), this[kI] = !1, this[PI] = !1, Xr.call(this, e), jr.call(this, e) + } else if (t.unfinished) { + var i = 1, n = this._model; + this._api; + t.unfinished = !1; + do { + var o = +new Date; + t.performSeriesTasks(n), t.performDataProcessorTasks(n), Zr(this, n), t.performVisualTasks(n), Qr(this, this._model, 0, "remain"), i -= +new Date - o + } while (i > 0 && t.unfinished); + t.unfinished || this._zr.flush() + } + } + }, OI.getDom = function () { + return this._dom + }, OI.getZr = function () { + return this._zr + }, OI.setOption = function (t, e, i) { + var n; + if (MI(e) && (i = e.lazyUpdate, n = e.silent, e = e.notMerge), this[kI] = !0, !this._model || e) { + var o = new La(this._api), a = this._theme, r = this._model = new TM(null, null, a, o); + r.scheduler = this._scheduler, r.init(null, null, a, o) + } + this._model.setOption(t, GI), i ? (this[PI] = {silent: n}, this[kI] = !1) : (Fr(this), EI.update.call(this), this._zr.flush(), this[PI] = !1, this[kI] = !1, Xr.call(this, n), jr.call(this, n)) + }, OI.setTheme = function () { + console.log("ECharts#setTheme() is DEPRECATED in ECharts 3.0") + }, OI.getModel = function () { + return this._model + }, OI.getOption = function () { + return this._model && this._model.getOption() + }, OI.getWidth = function () { + return this._zr.getWidth() + }, OI.getHeight = function () { + return this._zr.getHeight() + }, OI.getDevicePixelRatio = function () { + return this._zr.painter.dpr || window.devicePixelRatio || 1 + }, OI.getRenderedCanvas = function (t) { + if (a_.canvasSupported) return (t = t || {}).pixelRatio = t.pixelRatio || 1, t.backgroundColor = t.backgroundColor || this._model.get("backgroundColor"), this._zr.painter.getRenderedCanvas(t) + }, OI.getSvgDataUrl = function () { + if (a_.svgSupported) { + var t = this._zr; + return d(t.storage.getDisplayList(), function (t) { + t.stopAnimation(!0) + }), t.painter.pathToDataUrl() + } + }, OI.getDataURL = function (t) { + var e = (t = t || {}).excludeComponents, i = this._model, n = [], o = this; + bI(e, function (t) { + i.eachComponent({mainType: t}, function (t) { + var e = o._componentsMap[t.__viewId]; + e.group.ignore || (n.push(e), e.group.ignore = !0) + }) + }); + var a = "svg" === this._zr.painter.getType() ? this.getSvgDataUrl() : this.getRenderedCanvas(t).toDataURL("image/" + (t && t.type || "png")); + return bI(n, function (t) { + t.group.ignore = !1 + }), a + }, OI.getConnectedDataURL = function (t) { + if (a_.canvasSupported) { + var e = this.group, n = Math.min, o = Math.max; + if (XI[e]) { + var a = 1 / 0, r = 1 / 0, s = -1 / 0, l = -1 / 0, u = [], h = t && t.pixelRatio || 1; + d(UI, function (h, c) { + if (h.group === e) { + var d = h.getRenderedCanvas(i(t)), f = h.getDom().getBoundingClientRect(); + a = n(f.left, a), r = n(f.top, r), s = o(f.right, s), l = o(f.bottom, l), u.push({ + dom: d, + left: f.left, + top: f.top + }) + } + }); + var c = (s *= h) - (a *= h), f = (l *= h) - (r *= h), p = m_(); + p.width = c, p.height = f; + var g = _i(p); + return bI(u, function (t) { + var e = new Je({style: {x: t.left * h - a, y: t.top * h - r, image: t.dom}}); + g.add(e) + }), g.refreshImmediately(), p.toDataURL("image/" + (t && t.type || "png")) + } + return this.getDataURL(t) + } + }, OI.convertToPixel = v(Wr, "convertToPixel"), OI.convertFromPixel = v(Wr, "convertFromPixel"), OI.containPixel = function (t, e) { + var i; + return t = Oi(this._model, t), d(t, function (t, n) { + n.indexOf("Models") >= 0 && d(t, function (t) { + var o = t.coordinateSystem; + if (o && o.containPoint) i |= !!o.containPoint(e); else if ("seriesModels" === n) { + var a = this._chartsMap[t.__viewId]; + a && a.containPoint && (i |= a.containPoint(e, t)) + } + }, this) + }, this), !!i + }, OI.getVisual = function (t, e) { + var i = (t = Oi(this._model, t, {defaultMainType: "series"})).seriesModel.getData(), + n = t.hasOwnProperty("dataIndexInside") ? t.dataIndexInside : t.hasOwnProperty("dataIndex") ? i.indexOfRawIndex(t.dataIndex) : null; + return null != n ? i.getItemVisual(n, e) : i.getVisual(e) + }, OI.getViewOfComponentModel = function (t) { + return this._componentsMap[t.__viewId] + }, OI.getViewOfSeriesModel = function (t) { + return this._chartsMap[t.__viewId] + }; + var EI = { + prepareAndUpdate: function (t) { + Fr(this), EI.update.call(this, t) + }, update: function (t) { + var e = this._model, i = this._api, n = this._zr, o = this._coordSysMgr, a = this._scheduler; + if (e) { + a.restoreData(e, t), a.performSeriesTasks(e), o.create(e, i), a.performDataProcessorTasks(e, t), Zr(this, e), o.update(e, i), Kr(e), a.performVisualTasks(e, t), $r(this, e, i, t); + var r = e.get("backgroundColor") || "transparent"; + if (a_.canvasSupported) n.setBackgroundColor(r); else { + var s = At(r); + r = zt(s, "rgb"), 0 === s[3] && (r = "transparent") + } + ts(e, i) + } + }, updateTransform: function (t) { + var e = this._model, i = this, n = this._api; + if (e) { + var o = []; + e.eachComponent(function (a, r) { + var s = i.getViewOfComponentModel(r); + if (s && s.__alive) if (s.updateTransform) { + var l = s.updateTransform(r, e, n, t); + l && l.update && o.push(s) + } else o.push(s) + }); + var a = R(); + e.eachSeries(function (o) { + var r = i._chartsMap[o.__viewId]; + if (r.updateTransform) { + var s = r.updateTransform(o, e, n, t); + s && s.update && a.set(o.uid, 1) + } else a.set(o.uid, 1) + }), Kr(e), this._scheduler.performVisualTasks(e, t, { + setDirty: !0, + dirtyMap: a + }), Qr(i, e, 0, t, a), ts(e, this._api) + } + }, updateView: function (t) { + var e = this._model; + e && (gr.markUpdateMethod(t, "updateView"), Kr(e), this._scheduler.performVisualTasks(e, t, {setDirty: !0}), $r(this, this._model, this._api, t), ts(e, this._api)) + }, updateVisual: function (t) { + EI.update.call(this, t) + }, updateLayout: function (t) { + EI.update.call(this, t) + } + }; + OI.resize = function (t) { + this._zr.resize(t); + var e = this._model; + if (this._loadingFX && this._loadingFX.resize(), e) { + var i = e.resetOption("media"), n = t && t.silent; + this[kI] = !0, i && Fr(this), EI.update.call(this), this[kI] = !1, Xr.call(this, n), jr.call(this, n) + } + }, OI.showLoading = function (t, e) { + if (MI(t) && (e = t, t = ""), t = t || "default", this.hideLoading(), ZI[t]) { + var i = ZI[t](this._api, e), n = this._zr; + this._loadingFX = i, n.add(i) + } + }, OI.hideLoading = function () { + this._loadingFX && this._zr.remove(this._loadingFX), this._loadingFX = null + }, OI.makeActionFromEvent = function (t) { + var e = a({}, t); + return e.type = BI[t.type], e + }, OI.dispatchAction = function (t, e) { + MI(e) || (e = {silent: !!e}), zI[t.type] && this._model && (this[kI] ? this._pendingActions.push(t) : (Ur.call(this, t, e.silent), e.flush ? this._zr.flush(!0) : !1 !== e.flush && a_.browser.weChat && this._throttledZrFlush(), Xr.call(this, e.silent), jr.call(this, e.silent))) + }, OI.appendData = function (t) { + var e = t.seriesIndex; + this.getModel().getSeriesByIndex(e).appendData(t), this._scheduler.unfinished = !0 + }, OI.on = Br("on"), OI.off = Br("off"), OI.one = Br("one"); + var RI = ["click", "dblclick", "mouseover", "mouseout", "mousemove", "mousedown", "mouseup", "globalout", "contextmenu"]; + OI._initEvents = function () { + bI(RI, function (t) { + this._zr.on(t, function (e) { + var i, n = this.getModel(), o = e.target; + if ("globalout" === t) i = {}; else if (o && null != o.dataIndex) { + var r = o.dataModel || n.getSeriesByIndex(o.seriesIndex); + i = r && r.getDataParams(o.dataIndex, o.dataType) || {} + } else o && o.eventData && (i = a({}, o.eventData)); + i && (i.event = e, i.type = t, this.trigger(t, i)) + }, this) + }, this), bI(BI, function (t, e) { + this._messageCenter.on(e, function (t) { + this.trigger(e, t) + }, this) + }, this) + }, OI.isDisposed = function () { + return this._disposed + }, OI.clear = function () { + this.setOption({series: []}, !0) + }, OI.dispose = function () { + if (!this._disposed) { + this._disposed = !0, Ri(this.getDom(), qI, ""); + var t = this._api, e = this._model; + bI(this._componentsViews, function (i) { + i.dispose(e, t) + }), bI(this._chartsViews, function (i) { + i.dispose(e, t) + }), this._zr.dispose(), delete UI[this.id] + } + }, h(Gr, D_); + var zI = {}, BI = {}, VI = [], GI = [], WI = [], FI = [], HI = {}, ZI = {}, UI = {}, XI = {}, jI = new Date - 0, + YI = new Date - 0, qI = "_echarts_instance_", KI = {}, $I = rs; + ps(2e3, sI), us(GM), hs(5e3, function (t) { + var e = R(); + t.eachSeries(function (t) { + var i = t.get("stack"); + if (i) { + var n = e.get(i) || e.set(i, []), o = t.getData(), a = { + stackResultDimension: o.getCalculationInfo("stackResultDimension"), + stackedOverDimension: o.getCalculationInfo("stackedOverDimension"), + stackedDimension: o.getCalculationInfo("stackedDimension"), + stackedByDimension: o.getCalculationInfo("stackedByDimension"), + isStackedByIndex: o.getCalculationInfo("isStackedByIndex"), + data: o, + seriesModel: t + }; + if (!a.stackedDimension || !a.isStackedByIndex && !a.stackedByDimension) return; + n.length && o.setCalculationInfo("stackedOnSeries", n[n.length - 1].seriesModel), n.push(a) + } + }), e.each(ja) + }), ms("default", function (t, e) { + r(e = e || {}, { + text: "loading", + color: "#c23531", + textColor: "#000", + maskColor: "rgba(255, 255, 255, 0.8)", + zlevel: 0 + }); + var i = new SS({style: {fill: e.maskColor}, zlevel: e.zlevel, z: 1e4}), n = new DS({ + shape: {startAngle: -hI / 2, endAngle: -hI / 2 + .1, r: 10}, + style: {stroke: e.color, lineCap: "round", lineWidth: 5}, + zlevel: e.zlevel, + z: 10001 + }), o = new SS({ + style: { + fill: "none", + text: e.text, + textPosition: "right", + textDistance: 10, + textFill: e.textColor + }, zlevel: e.zlevel, z: 10001 + }); + n.animateShape(!0).when(1e3, {endAngle: 3 * hI / 2}).start("circularInOut"), n.animateShape(!0).when(1e3, {startAngle: 3 * hI / 2}).delay(300).start("circularInOut"); + var a = new sw; + return a.add(n), a.add(o), a.add(i), a.resize = function () { + var e = t.getWidth() / 2, a = t.getHeight() / 2; + n.setShape({cx: e, cy: a}); + var r = n.shape.r; + o.setShape({x: e - r, y: a - r, width: 2 * r, height: 2 * r}), i.setShape({ + x: 0, + y: 0, + width: t.getWidth(), + height: t.getHeight() + }) + }, a.resize(), a + }), cs({type: "highlight", event: "highlight", update: "highlight"}, B), cs({ + type: "downplay", + event: "downplay", + update: "downplay" + }, B), ls("light", yI), ls("dark", _I); + var JI = {}; + Ss.prototype = { + constructor: Ss, add: function (t) { + return this._add = t, this + }, update: function (t) { + return this._update = t, this + }, remove: function (t) { + return this._remove = t, this + }, execute: function () { + var t = this._old, e = this._new, i = {}, n = [], o = []; + for (Ms(t, {}, n, "_oldKeyGetter", this), Ms(e, i, o, "_newKeyGetter", this), a = 0; a < t.length; a++) null != (s = i[r = n[a]]) ? ((u = s.length) ? (1 === u && (i[r] = null), s = s.unshift()) : i[r] = null, this._update && this._update(s, a)) : this._remove && this._remove(a); + for (var a = 0; a < o.length; a++) { + var r = o[a]; + if (i.hasOwnProperty(r)) { + var s = i[r]; + if (null == s) continue; + if (s.length) for (var l = 0, u = s.length; l < u; l++) this._add && this._add(s[l]); else this._add && this._add(s) + } + } + } + }; + var QI = R(["tooltip", "label", "itemName", "itemId", "seriesName"]), tT = w, eT = "e\0\0", iT = { + float: "undefined" == typeof Float64Array ? Array : Float64Array, + int: "undefined" == typeof Int32Array ? Array : Int32Array, + ordinal: Array, + number: Array, + time: Array + }, nT = "undefined" == typeof Uint32Array ? Array : Uint32Array, + oT = "undefined" == typeof Uint16Array ? Array : Uint16Array, + aT = ["hasItemOption", "_nameList", "_idList", "_invertedIndicesMap", "_rawData", "_chunkSize", "_chunkCount", "_dimValueGetter", "_count", "_rawCount", "_nameDimIdx", "_idDimIdx"], + rT = ["_extent", "_approximateExtent", "_rawExtent"], sT = function (t, e) { + t = t || ["x", "y"]; + for (var i = {}, n = [], o = {}, a = 0; a < t.length; a++) { + var r = t[a]; + _(r) && (r = {name: r}); + var s = r.name; + r.type = r.type || "float", r.coordDim || (r.coordDim = s, r.coordDimIndex = 0), r.otherDims = r.otherDims || {}, n.push(s), i[s] = r, r.index = a, r.createInvertedIndices && (o[s] = []) + } + this.dimensions = n, this._dimensionInfos = i, this.hostModel = e, this.dataType, this._indices = null, this._count = 0, this._rawCount = 0, this._storage = {}, this._nameList = [], this._idList = [], this._optionModels = [], this._visual = {}, this._layout = {}, this._itemVisuals = [], this.hasItemVisual = {}, this._itemLayouts = [], this._graphicEls = [], this._chunkSize = 1e5, this._chunkCount = 0, this._rawData, this._rawExtent = {}, this._extent = {}, this._approximateExtent = {}, this._dimensionsSummary = Is(this), this._invertedIndicesMap = o, this._calculationInfo = {} + }, lT = sT.prototype; + lT.type = "list", lT.hasItemOption = !0, lT.getDimension = function (t) { + return isNaN(t) || (t = this.dimensions[t] || t), t + }, lT.getDimensionInfo = function (t) { + return this._dimensionInfos[this.getDimension(t)] + }, lT.getDimensionsOnCoord = function () { + return this._dimensionsSummary.dataDimsOnCoord.slice() + }, lT.mapDimension = function (t, e) { + var i = this._dimensionsSummary; + if (null == e) return i.encodeFirstDimNotExtra[t]; + var n = i.encode[t]; + return !0 === e ? (n || []).slice() : n && n[e] + }, lT.initData = function (t, e, i) { + (la.isInstance(t) || c(t)) && (t = new Ya(t, this.dimensions.length)), this._rawData = t, this._storage = {}, this._indices = null, this._nameList = e || [], this._idList = [], this._nameRepeatCount = {}, i || (this.hasItemOption = !1), this.defaultDimValueGetter = ZM[this._rawData.getSource().sourceFormat], this._dimValueGetter = i = i || this.defaultDimValueGetter, this._rawExtent = {}, this._initDataFromProvider(0, t.count()), t.pure && (this.hasItemOption = !1) + }, lT.getProvider = function () { + return this._rawData + }, lT.appendData = function (t) { + var e = this._rawData, i = this.count(); + e.appendData(t); + var n = e.count(); + e.persistent || (n += i), this._initDataFromProvider(i, n) + }, lT._initDataFromProvider = function (t, e) { + if (!(t >= e)) { + for (var i, n = this._chunkSize, o = this._rawData, a = this._storage, r = this.dimensions, s = r.length, l = this._dimensionInfos, u = this._nameList, h = this._idList, c = this._rawExtent, d = this._nameRepeatCount = {}, f = this._chunkCount, p = f - 1, g = 0; g < s; g++) { + c[T = r[g]] || (c[T] = [1 / 0, -1 / 0]); + var m = l[T]; + 0 === m.otherDims.itemName && (i = this._nameDimIdx = g), 0 === m.otherDims.itemId && (this._idDimIdx = g); + var v = iT[m.type]; + a[T] || (a[T] = []); + var y = a[T][p]; + if (y && y.length < n) { + for (var x = new v(Math.min(e - p * n, n)), _ = 0; _ < y.length; _++) x[_] = y[_]; + a[T][p] = x + } + for (I = f * n; I < e; I += n) a[T].push(new v(Math.min(e - I, n))); + this._chunkCount = a[T].length + } + for (var w = new Array(s), b = t; b < e; b++) { + w = o.getItem(b, w); + for (var S = Math.floor(b / n), M = b % n, I = 0; I < s; I++) { + var T = r[I], D = a[T][S], A = this._dimValueGetter(w, T, b, I); + D[M] = A; + var C = c[T]; + A < C[0] && (C[0] = A), A > C[1] && (C[1] = A) + } + if (!o.pure) { + var L = u[b]; + if (w && null == L) if (null != w.name) u[b] = L = w.name; else if (null != i) { + var k = r[i], P = a[k][S]; + if (P) { + L = P[M]; + var N = l[k].ordinalMeta; + N && N.categories.length && (L = N.categories[L]) + } + } + var O = null == w ? null : w.id; + null == O && null != L && (d[L] = d[L] || 0, O = L, d[L] > 0 && (O += "__ec__" + d[L]), d[L]++), null != O && (h[b] = O) + } + } + !o.persistent && o.clean && o.clean(), this._rawCount = this._count = e, this._extent = {}, ks(this) + } + }, lT.count = function () { + return this._count + }, lT.getIndices = function () { + var t = this._indices; + if (t) { + var e = t.constructor, i = this._count; + if (e === Array) { + n = new e(i); + for (o = 0; o < i; o++) n[o] = t[o] + } else n = new e(t.buffer, 0, i) + } else for (var n = new (e = As(this))(this.count()), o = 0; o < n.length; o++) n[o] = o; + return n + }, lT.get = function (t, e) { + if (!(e >= 0 && e < this._count)) return NaN; + var i = this._storage; + if (!i[t]) return NaN; + e = this.getRawIndex(e); + var n = Math.floor(e / this._chunkSize), o = e % this._chunkSize; + return i[t][n][o] + }, lT.getByRawIndex = function (t, e) { + if (!(e >= 0 && e < this._rawCount)) return NaN; + var i = this._storage[t]; + if (!i) return NaN; + var n = Math.floor(e / this._chunkSize), o = e % this._chunkSize; + return i[n][o] + }, lT._getFast = function (t, e) { + var i = Math.floor(e / this._chunkSize), n = e % this._chunkSize; + return this._storage[t][i][n] + }, lT.getValues = function (t, e) { + var i = []; + y(t) || (e = t, t = this.dimensions); + for (var n = 0, o = t.length; n < o; n++) i.push(this.get(t[n], e)); + return i + }, lT.hasValue = function (t) { + for (var e = this._dimensionsSummary.dataDimsOnCoord, i = this._dimensionInfos, n = 0, o = e.length; n < o; n++) if ("ordinal" !== i[e[n]].type && isNaN(this.get(e[n], t))) return !1; + return !0 + }, lT.getDataExtent = function (t) { + t = this.getDimension(t); + var e = [1 / 0, -1 / 0]; + if (!this._storage[t]) return e; + var i, n = this.count(); + if (!this._indices) return this._rawExtent[t].slice(); + if (i = this._extent[t]) return i.slice(); + for (var o = (i = e)[0], a = i[1], r = 0; r < n; r++) { + var s = this._getFast(t, this.getRawIndex(r)); + s < o && (o = s), s > a && (a = s) + } + return i = [o, a], this._extent[t] = i, i + }, lT.getApproximateExtent = function (t) { + return t = this.getDimension(t), this._approximateExtent[t] || this.getDataExtent(t) + }, lT.setApproximateExtent = function (t, e) { + e = this.getDimension(e), this._approximateExtent[e] = t.slice() + }, lT.getCalculationInfo = function (t) { + return this._calculationInfo[t] + }, lT.setCalculationInfo = function (t, e) { + tT(t) ? a(this._calculationInfo, t) : this._calculationInfo[t] = e + }, lT.getSum = function (t) { + var e = 0; + if (this._storage[t]) for (var i = 0, n = this.count(); i < n; i++) { + var o = this.get(t, i); + isNaN(o) || (e += o) + } + return e + }, lT.getMedian = function (t) { + var e = []; + this.each(t, function (t, i) { + isNaN(t) || e.push(t) + }); + var i = [].concat(e).sort(function (t, e) { + return t - e + }), n = this.count(); + return 0 === n ? 0 : n % 2 == 1 ? i[(n - 1) / 2] : (i[n / 2] + i[n / 2 - 1]) / 2 + }, lT.rawIndexOf = function (t, e) { + var i = (t && this._invertedIndicesMap[t])[e]; + return null == i || isNaN(i) ? -1 : i + }, lT.indexOfName = function (t) { + for (var e = 0, i = this.count(); e < i; e++) if (this.getName(e) === t) return e; + return -1 + }, lT.indexOfRawIndex = function (t) { + if (!this._indices) return t; + if (t >= this._rawCount || t < 0) return -1; + var e = this._indices, i = e[t]; + if (null != i && i < this._count && i === t) return t; + for (var n = 0, o = this._count - 1; n <= o;) { + var a = (n + o) / 2 | 0; + if (e[a] < t) n = a + 1; else { + if (!(e[a] > t)) return a; + o = a - 1 + } + } + return -1 + }, lT.indicesOfNearest = function (t, e, i) { + var n = []; + if (!this._storage[t]) return n; + null == i && (i = 1 / 0); + for (var o = Number.MAX_VALUE, a = -1, r = 0, s = this.count(); r < s; r++) { + var l = e - this.get(t, r), u = Math.abs(l); + l <= i && u <= o && ((u < o || l >= 0 && a < 0) && (o = u, a = l, n.length = 0), n.push(r)) + } + return n + }, lT.getRawIndex = Ns, lT.getRawDataItem = function (t) { + if (this._rawData.persistent) return this._rawData.getItem(this.getRawIndex(t)); + for (var e = [], i = 0; i < this.dimensions.length; i++) { + var n = this.dimensions[i]; + e.push(this.get(n, t)) + } + return e + }, lT.getName = function (t) { + var e = this.getRawIndex(t); + return this._nameList[e] || Ps(this, this._nameDimIdx, e) || "" + }, lT.getId = function (t) { + return Es(this, this.getRawIndex(t)) + }, lT.each = function (t, e, i, n) { + if (this._count) { + "function" == typeof t && (n = i, i = e, e = t, t = []), i = i || n || this; + for (var o = (t = f(Rs(t), this.getDimension, this)).length, a = 0; a < this.count(); a++) switch (o) { + case 0: + e.call(i, a); + break; + case 1: + e.call(i, this.get(t[0], a), a); + break; + case 2: + e.call(i, this.get(t[0], a), this.get(t[1], a), a); + break; + default: + for (var r = 0, s = []; r < o; r++) s[r] = this.get(t[r], a); + s[r] = a, e.apply(i, s) + } + } + }, lT.filterSelf = function (t, e, i, n) { + if (this._count) { + "function" == typeof t && (n = i, i = e, e = t, t = []), i = i || n || this, t = f(Rs(t), this.getDimension, this); + for (var o = this.count(), a = new (As(this))(o), r = [], s = t.length, l = 0, u = t[0], h = 0; h < o; h++) { + var c, d = this.getRawIndex(h); + if (0 === s) c = e.call(i, h); else if (1 === s) { + var p = this._getFast(u, d); + c = e.call(i, p, h) + } else { + for (var g = 0; g < s; g++) r[g] = this._getFast(u, d); + r[g] = h, c = e.apply(i, r) + } + c && (a[l++] = d) + } + return l < o && (this._indices = a), this._count = l, this._extent = {}, this.getRawIndex = this._indices ? Os : Ns, this + } + }, lT.selectRange = function (t) { + if (this._count) { + var e = []; + for (var i in t) t.hasOwnProperty(i) && e.push(i); + var n = e.length; + if (n) { + var o = this.count(), a = new (As(this))(o), r = 0, s = e[0], l = t[s][0], u = t[s][1], h = !1; + if (!this._indices) { + var c = 0; + if (1 === n) { + for (var d = this._storage[e[0]], f = 0; f < this._chunkCount; f++) for (var p = d[f], g = Math.min(this._count - f * this._chunkSize, this._chunkSize), m = 0; m < g; m++) ((w = p[m]) >= l && w <= u || isNaN(w)) && (a[r++] = c), c++; + h = !0 + } else if (2 === n) { + for (var d = this._storage[s], v = this._storage[e[1]], y = t[e[1]][0], x = t[e[1]][1], f = 0; f < this._chunkCount; f++) for (var p = d[f], _ = v[f], g = Math.min(this._count - f * this._chunkSize, this._chunkSize), m = 0; m < g; m++) { + var w = p[m], b = _[m]; + (w >= l && w <= u || isNaN(w)) && (b >= y && b <= x || isNaN(b)) && (a[r++] = c), c++ + } + h = !0 + } + } + if (!h) if (1 === n) for (m = 0; m < o; m++) { + M = this.getRawIndex(m); + ((w = this._getFast(s, M)) >= l && w <= u || isNaN(w)) && (a[r++] = M) + } else for (m = 0; m < o; m++) { + for (var S = !0, M = this.getRawIndex(m), f = 0; f < n; f++) { + var I = e[f]; + ((w = this._getFast(i, M)) < t[I][0] || w > t[I][1]) && (S = !1) + } + S && (a[r++] = this.getRawIndex(m)) + } + return r < o && (this._indices = a), this._count = r, this._extent = {}, this.getRawIndex = this._indices ? Os : Ns, this + } + } + }, lT.mapArray = function (t, e, i, n) { + "function" == typeof t && (n = i, i = e, e = t, t = []), i = i || n || this; + var o = []; + return this.each(t, function () { + o.push(e && e.apply(this, arguments)) + }, i), o + }, lT.map = function (t, e, i, n) { + i = i || n || this; + var o = zs(this, t = f(Rs(t), this.getDimension, this)); + o._indices = this._indices, o.getRawIndex = o._indices ? Os : Ns; + for (var a = o._storage, r = [], s = this._chunkSize, l = t.length, u = this.count(), h = [], c = o._rawExtent, d = 0; d < u; d++) { + for (var p = 0; p < l; p++) h[p] = this.get(t[p], d); + h[l] = d; + var g = e && e.apply(i, h); + if (null != g) { + "object" != typeof g && (r[0] = g, g = r); + for (var m = this.getRawIndex(d), v = Math.floor(m / s), y = m % s, x = 0; x < g.length; x++) { + var _ = t[x], w = g[x], b = c[_], S = a[_]; + S && (S[v][y] = w), w < b[0] && (b[0] = w), w > b[1] && (b[1] = w) + } + } + } + return o + }, lT.downSample = function (t, e, i, n) { + for (var o = zs(this, [t]), a = o._storage, r = [], s = Math.floor(1 / e), l = a[t], u = this.count(), h = this._chunkSize, c = o._rawExtent[t], d = new (As(this))(u), f = 0, p = 0; p < u; p += s) { + s > u - p && (s = u - p, r.length = s); + for (var g = 0; g < s; g++) { + var m = this.getRawIndex(p + g), v = Math.floor(m / h), y = m % h; + r[g] = l[v][y] + } + var x = i(r), _ = this.getRawIndex(Math.min(p + n(r, x) || 0, u - 1)), w = _ % h; + l[Math.floor(_ / h)][w] = x, x < c[0] && (c[0] = x), x > c[1] && (c[1] = x), d[f++] = _ + } + return o._count = f, o._indices = d, o.getRawIndex = Os, o + }, lT.getItemModel = function (t) { + var e = this.hostModel; + return new wo(this.getRawDataItem(t), e, e && e.ecModel) + }, lT.diff = function (t) { + var e = this; + return new Ss(t ? t.getIndices() : [], this.getIndices(), function (e) { + return Es(t, e) + }, function (t) { + return Es(e, t) + }) + }, lT.getVisual = function (t) { + var e = this._visual; + return e && e[t] + }, lT.setVisual = function (t, e) { + if (tT(t)) for (var i in t) t.hasOwnProperty(i) && this.setVisual(i, t[i]); else this._visual = this._visual || {}, this._visual[t] = e + }, lT.setLayout = function (t, e) { + if (tT(t)) for (var i in t) t.hasOwnProperty(i) && this.setLayout(i, t[i]); else this._layout[t] = e + }, lT.getLayout = function (t) { + return this._layout[t] + }, lT.getItemLayout = function (t) { + return this._itemLayouts[t] + }, lT.setItemLayout = function (t, e, i) { + this._itemLayouts[t] = i ? a(this._itemLayouts[t] || {}, e) : e + }, lT.clearItemLayouts = function () { + this._itemLayouts.length = 0 + }, lT.getItemVisual = function (t, e, i) { + var n = this._itemVisuals[t], o = n && n[e]; + return null != o || i ? o : this.getVisual(e) + }, lT.setItemVisual = function (t, e, i) { + var n = this._itemVisuals[t] || {}, o = this.hasItemVisual; + if (this._itemVisuals[t] = n, tT(e)) for (var a in e) e.hasOwnProperty(a) && (n[a] = e[a], o[a] = !0); else n[e] = i, o[e] = !0 + }, lT.clearAllVisual = function () { + this._visual = {}, this._itemVisuals = [], this.hasItemVisual = {} + }; + var uT = function (t) { + t.seriesIndex = this.seriesIndex, t.dataIndex = this.dataIndex, t.dataType = this.dataType + }; + lT.setItemGraphicEl = function (t, e) { + var i = this.hostModel; + e && (e.dataIndex = t, e.dataType = this.dataType, e.seriesIndex = i && i.seriesIndex, "group" === e.type && e.traverse(uT, e)), this._graphicEls[t] = e + }, lT.getItemGraphicEl = function (t) { + return this._graphicEls[t] + }, lT.eachItemGraphicEl = function (t, e) { + d(this._graphicEls, function (i, n) { + i && t && t.call(e, i, n) + }) + }, lT.cloneShallow = function (t) { + if (!t) { + var e = f(this.dimensions, this.getDimensionInfo, this); + t = new sT(e, this.hostModel) + } + if (t._storage = this._storage, Ls(t, this), this._indices) { + var i = this._indices.constructor; + t._indices = new i(this._indices) + } else t._indices = null; + return t.getRawIndex = t._indices ? Os : Ns, t + }, lT.wrapMethod = function (t, e) { + var i = this[t]; + "function" == typeof i && (this.__wrappedMethods = this.__wrappedMethods || [], this.__wrappedMethods.push(t), this[t] = function () { + var t = i.apply(this, arguments); + return e.apply(this, [t].concat(C(arguments))) + }) + }, lT.TRANSFERABLE_METHODS = ["cloneShallow", "downSample", "map"], lT.CHANGABLE_METHODS = ["filterSelf", "selectRange"]; + var hT = function (t, e) { + return e = e || {}, Gs(e.coordDimensions || [], t, { + dimsDef: e.dimensionsDefine || t.dimensionsDefine, + encodeDef: e.encodeDefine || t.encodeDefine, + dimCount: e.dimensionsCount, + generateCoord: e.generateCoord, + generateCoordCount: e.generateCoordCount + }) + }; + qs.prototype.parse = function (t) { + return t + }, qs.prototype.getSetting = function (t) { + return this._setting[t] + }, qs.prototype.contain = function (t) { + var e = this._extent; + return t >= e[0] && t <= e[1] + }, qs.prototype.normalize = function (t) { + var e = this._extent; + return e[1] === e[0] ? .5 : (t - e[0]) / (e[1] - e[0]) + }, qs.prototype.scale = function (t) { + var e = this._extent; + return t * (e[1] - e[0]) + e[0] + }, qs.prototype.unionExtent = function (t) { + var e = this._extent; + t[0] < e[0] && (e[0] = t[0]), t[1] > e[1] && (e[1] = t[1]) + }, qs.prototype.unionExtentFromData = function (t, e) { + this.unionExtent(t.getApproximateExtent(e)) + }, qs.prototype.getExtent = function () { + return this._extent.slice() + }, qs.prototype.setExtent = function (t, e) { + var i = this._extent; + isNaN(t) || (i[0] = t), isNaN(e) || (i[1] = e) + }, qs.prototype.isBlank = function () { + return this._isBlank + }, qs.prototype.setBlank = function (t) { + this._isBlank = t + }, qs.prototype.getLabel = null, Gi(qs), Zi(qs, {registerWhenExtend: !0}), Ks.createByAxisModel = function (t) { + var e = t.option, i = e.data, n = i && f(i, Js); + return new Ks({categories: n, needCollect: !n, deduplication: !1 !== e.dedplication}) + }; + var cT = Ks.prototype; + cT.getOrdinal = function (t) { + return $s(this).get(t) + }, cT.parseAndCollect = function (t) { + var e, i = this._needCollect; + if ("string" != typeof t && !i) return t; + if (i && !this._deduplication) return e = this.categories.length, this.categories[e] = t, e; + var n = $s(this); + return null == (e = n.get(t)) && (i ? (e = this.categories.length, this.categories[e] = t, n.set(t, e)) : e = NaN), e + }; + var dT = qs.prototype, fT = qs.extend({ + type: "ordinal", init: function (t, e) { + t && !y(t) || (t = new Ks({categories: t})), this._ordinalMeta = t, this._extent = e || [0, t.categories.length - 1] + }, parse: function (t) { + return "string" == typeof t ? this._ordinalMeta.getOrdinal(t) : Math.round(t) + }, contain: function (t) { + return t = this.parse(t), dT.contain.call(this, t) && null != this._ordinalMeta.categories[t] + }, normalize: function (t) { + return dT.normalize.call(this, this.parse(t)) + }, scale: function (t) { + return Math.round(dT.scale.call(this, t)) + }, getTicks: function () { + for (var t = [], e = this._extent, i = e[0]; i <= e[1];) t.push(i), i++; + return t + }, getLabel: function (t) { + if (!this.isBlank()) return this._ordinalMeta.categories[t] + }, count: function () { + return this._extent[1] - this._extent[0] + 1 + }, unionExtentFromData: function (t, e) { + this.unionExtent(t.getApproximateExtent(e)) + }, getOrdinalMeta: function () { + return this._ordinalMeta + }, niceTicks: B, niceExtent: B + }); + fT.create = function () { + return new fT + }; + var pT = Ao, gT = Ao, mT = qs.extend({ + type: "interval", _interval: 0, _intervalPrecision: 2, setExtent: function (t, e) { + var i = this._extent; + isNaN(t) || (i[0] = parseFloat(t)), isNaN(e) || (i[1] = parseFloat(e)) + }, unionExtent: function (t) { + var e = this._extent; + t[0] < e[0] && (e[0] = t[0]), t[1] > e[1] && (e[1] = t[1]), mT.prototype.setExtent.call(this, e[0], e[1]) + }, getInterval: function () { + return this._interval + }, setInterval: function (t) { + this._interval = t, this._niceExtent = this._extent.slice(), this._intervalPrecision = tl(t) + }, getTicks: function () { + return nl(this._interval, this._extent, this._niceExtent, this._intervalPrecision) + }, getLabel: function (t, e) { + if (null == t) return ""; + var i = e && e.precision; + return null == i ? i = ko(t) || 0 : "auto" === i && (i = this._intervalPrecision), t = gT(t, i, !0), Fo(t) + }, niceTicks: function (t, e, i) { + t = t || 5; + var n = this._extent, o = n[1] - n[0]; + if (isFinite(o)) { + o < 0 && (o = -o, n.reverse()); + var a = Qs(n, t, e, i); + this._intervalPrecision = a.intervalPrecision, this._interval = a.interval, this._niceExtent = a.niceTickExtent + } + }, niceExtent: function (t) { + var e = this._extent; + if (e[0] === e[1]) if (0 !== e[0]) { + var i = e[0]; + t.fixMax ? e[0] -= i / 2 : (e[1] += i / 2, e[0] -= i / 2) + } else e[1] = 1; + var n = e[1] - e[0]; + isFinite(n) || (e[0] = 0, e[1] = 1), this.niceTicks(t.splitNumber, t.minInterval, t.maxInterval); + var o = this._interval; + t.fixMin || (e[0] = gT(Math.floor(e[0] / o) * o)), t.fixMax || (e[1] = gT(Math.ceil(e[1] / o) * o)) + } + }); + mT.create = function () { + return new mT + }; + var vT = "__ec_stack_", yT = "undefined" != typeof Float32Array ? Float32Array : Array, xT = { + seriesType: "bar", plan: QM(), reset: function (t) { + if (dl(t) && fl(t)) { + var e = t.getData(), i = t.coordinateSystem, n = i.getBaseAxis(), o = i.getOtherAxis(n), + a = e.mapDimension(o.dim), r = e.mapDimension(n.dim), s = o.isHorizontal(), l = s ? 0 : 1, + u = hl(ll([t]), n, t).width; + return u > .5 || (u = .5), { + progress: function (t, e) { + for (var h, c = new yT(2 * t.count), d = [], f = [], p = 0; null != (h = t.next());) f[l] = e.get(a, h), f[1 - l] = e.get(r, h), d = i.dataToPoint(f, null, d), c[p++] = d[0], c[p++] = d[1]; + e.setLayout({ + largePoints: c, + barWidth: u, + valueAxisStart: pl(n, o, !1), + valueAxisHorizontal: s + }) + } + } + } + } + }, _T = mT.prototype, wT = Math.ceil, bT = Math.floor, ST = function (t, e, i, n) { + for (; i < n;) { + var o = i + n >>> 1; + t[o][1] < e ? i = o + 1 : n = o + } + return i + }, MT = mT.extend({ + type: "time", getLabel: function (t) { + var e = this._stepLvl, i = new Date(t); + return qo(e[0], i, this.getSetting("useUTC")) + }, niceExtent: function (t) { + var e = this._extent; + if (e[0] === e[1] && (e[0] -= 864e5, e[1] += 864e5), e[1] === -1 / 0 && e[0] === 1 / 0) { + var i = new Date; + e[1] = +new Date(i.getFullYear(), i.getMonth(), i.getDate()), e[0] = e[1] - 864e5 + } + this.niceTicks(t.splitNumber, t.minInterval, t.maxInterval); + var n = this._interval; + t.fixMin || (e[0] = Ao(bT(e[0] / n) * n)), t.fixMax || (e[1] = Ao(wT(e[1] / n) * n)) + }, niceTicks: function (t, e, i) { + t = t || 10; + var n = this._extent, o = n[1] - n[0], a = o / t; + null != e && a < e && (a = e), null != i && a > i && (a = i); + var r = IT.length, s = ST(IT, a, 0, r), l = IT[Math.min(s, r - 1)], u = l[1]; + "year" === l[0] && (u *= Vo(o / u / t, !0)); + var h = this.getSetting("useUTC") ? 0 : 60 * new Date(+n[0] || +n[1]).getTimezoneOffset() * 1e3, + c = [Math.round(wT((n[0] - h) / u) * u + h), Math.round(bT((n[1] - h) / u) * u + h)]; + il(c, n), this._stepLvl = l, this._interval = u, this._niceExtent = c + }, parse: function (t) { + return +Ro(t) + } + }); + d(["contain", "normalize"], function (t) { + MT.prototype[t] = function (e) { + return _T[t].call(this, this.parse(e)) + } + }); + var IT = [["hh:mm:ss", 1e3], ["hh:mm:ss", 5e3], ["hh:mm:ss", 1e4], ["hh:mm:ss", 15e3], ["hh:mm:ss", 3e4], ["hh:mm\nMM-dd", 6e4], ["hh:mm\nMM-dd", 3e5], ["hh:mm\nMM-dd", 6e5], ["hh:mm\nMM-dd", 9e5], ["hh:mm\nMM-dd", 18e5], ["hh:mm\nMM-dd", 36e5], ["hh:mm\nMM-dd", 72e5], ["hh:mm\nMM-dd", 216e5], ["hh:mm\nMM-dd", 432e5], ["MM-dd\nyyyy", 864e5], ["MM-dd\nyyyy", 1728e5], ["MM-dd\nyyyy", 2592e5], ["MM-dd\nyyyy", 3456e5], ["MM-dd\nyyyy", 432e6], ["MM-dd\nyyyy", 5184e5], ["week", 6048e5], ["MM-dd\nyyyy", 864e6], ["week", 12096e5], ["week", 18144e5], ["month", 26784e5], ["week", 36288e5], ["month", 53568e5], ["week", 36288e5], ["quarter", 8208e6], ["month", 107136e5], ["month", 13392e6], ["half-year", 16416e6], ["month", 214272e5], ["month", 26784e6], ["year", 32832e6]]; + MT.create = function (t) { + return new MT({useUTC: t.ecModel.get("useUTC")}) + }; + var TT = qs.prototype, DT = mT.prototype, AT = ko, CT = Ao, LT = Math.floor, kT = Math.ceil, PT = Math.pow, + NT = Math.log, OT = qs.extend({ + type: "log", base: 10, $constructor: function () { + qs.apply(this, arguments), this._originalScale = new mT + }, getTicks: function () { + var t = this._originalScale, e = this._extent, i = t.getExtent(); + return f(DT.getTicks.call(this), function (n) { + var o = Ao(PT(this.base, n)); + return o = n === e[0] && t.__fixMin ? gl(o, i[0]) : o, o = n === e[1] && t.__fixMax ? gl(o, i[1]) : o + }, this) + }, getLabel: DT.getLabel, scale: function (t) { + return t = TT.scale.call(this, t), PT(this.base, t) + }, setExtent: function (t, e) { + var i = this.base; + t = NT(t) / NT(i), e = NT(e) / NT(i), DT.setExtent.call(this, t, e) + }, getExtent: function () { + var t = this.base, e = TT.getExtent.call(this); + e[0] = PT(t, e[0]), e[1] = PT(t, e[1]); + var i = this._originalScale, n = i.getExtent(); + return i.__fixMin && (e[0] = gl(e[0], n[0])), i.__fixMax && (e[1] = gl(e[1], n[1])), e + }, unionExtent: function (t) { + this._originalScale.unionExtent(t); + var e = this.base; + t[0] = NT(t[0]) / NT(e), t[1] = NT(t[1]) / NT(e), TT.unionExtent.call(this, t) + }, unionExtentFromData: function (t, e) { + this.unionExtent(t.getApproximateExtent(e)) + }, niceTicks: function (t) { + t = t || 10; + var e = this._extent, i = e[1] - e[0]; + if (!(i === 1 / 0 || i <= 0)) { + var n = zo(i); + for (t / i * n <= .5 && (n *= 10); !isNaN(n) && Math.abs(n) < 1 && Math.abs(n) > 0;) n *= 10; + var o = [Ao(kT(e[0] / n) * n), Ao(LT(e[1] / n) * n)]; + this._interval = n, this._niceExtent = o + } + }, niceExtent: function (t) { + DT.niceExtent.call(this, t); + var e = this._originalScale; + e.__fixMin = t.fixMin, e.__fixMax = t.fixMax + } + }); + d(["contain", "normalize"], function (t) { + OT.prototype[t] = function (e) { + return e = NT(e) / NT(this.base), TT[t].call(this, e) + } + }), OT.create = function () { + return new OT + }; + var ET = { + getMin: function (t) { + var e = this.option, i = t || null == e.rangeStart ? e.min : e.rangeStart; + return this.axis && null != i && "dataMin" !== i && "function" != typeof i && !I(i) && (i = this.axis.scale.parse(i)), i + }, getMax: function (t) { + var e = this.option, i = t || null == e.rangeEnd ? e.max : e.rangeEnd; + return this.axis && null != i && "dataMax" !== i && "function" != typeof i && !I(i) && (i = this.axis.scale.parse(i)), i + }, getNeedCrossZero: function () { + var t = this.option; + return null == t.rangeStart && null == t.rangeEnd && !t.scale + }, getCoordSysModel: B, setRange: function (t, e) { + this.option.rangeStart = t, this.option.rangeEnd = e + }, resetRange: function () { + this.option.rangeStart = this.option.rangeEnd = null + } + }, RT = En({ + type: "triangle", shape: {cx: 0, cy: 0, width: 0, height: 0}, buildPath: function (t, e) { + var i = e.cx, n = e.cy, o = e.width / 2, a = e.height / 2; + t.moveTo(i, n - a), t.lineTo(i + o, n + a), t.lineTo(i - o, n + a), t.closePath() + } + }), zT = En({ + type: "diamond", shape: {cx: 0, cy: 0, width: 0, height: 0}, buildPath: function (t, e) { + var i = e.cx, n = e.cy, o = e.width / 2, a = e.height / 2; + t.moveTo(i, n - a), t.lineTo(i + o, n), t.lineTo(i, n + a), t.lineTo(i - o, n), t.closePath() + } + }), BT = En({ + type: "pin", shape: {x: 0, y: 0, width: 0, height: 0}, buildPath: function (t, e) { + var i = e.x, n = e.y, o = e.width / 5 * 3, a = Math.max(o, e.height), r = o / 2, s = r * r / (a - r), + l = n - a + r + s, u = Math.asin(s / r), h = Math.cos(u) * r, c = Math.sin(u), d = Math.cos(u), + f = .6 * r, p = .7 * r; + t.moveTo(i - h, l + s), t.arc(i, l, r, Math.PI - u, 2 * Math.PI + u), t.bezierCurveTo(i + h - c * f, l + s + d * f, i, n - p, i, n), t.bezierCurveTo(i, n - p, i - h + c * f, l + s + d * f, i - h, l + s), t.closePath() + } + }), VT = En({ + type: "arrow", shape: {x: 0, y: 0, width: 0, height: 0}, buildPath: function (t, e) { + var i = e.height, n = e.width, o = e.x, a = e.y, r = n / 3 * 2; + t.moveTo(o, a), t.lineTo(o + r, a + i), t.lineTo(o, a + i / 4 * 3), t.lineTo(o - r, a + i), t.lineTo(o, a), t.closePath() + } + }), GT = { + line: function (t, e, i, n, o) { + o.x1 = t, o.y1 = e + n / 2, o.x2 = t + i, o.y2 = e + n / 2 + }, rect: function (t, e, i, n, o) { + o.x = t, o.y = e, o.width = i, o.height = n + }, roundRect: function (t, e, i, n, o) { + o.x = t, o.y = e, o.width = i, o.height = n, o.r = Math.min(i, n) / 4 + }, square: function (t, e, i, n, o) { + var a = Math.min(i, n); + o.x = t, o.y = e, o.width = a, o.height = a + }, circle: function (t, e, i, n, o) { + o.cx = t + i / 2, o.cy = e + n / 2, o.r = Math.min(i, n) / 2 + }, diamond: function (t, e, i, n, o) { + o.cx = t + i / 2, o.cy = e + n / 2, o.width = i, o.height = n + }, pin: function (t, e, i, n, o) { + o.x = t + i / 2, o.y = e + n / 2, o.width = i, o.height = n + }, arrow: function (t, e, i, n, o) { + o.x = t + i / 2, o.y = e + n / 2, o.width = i, o.height = n + }, triangle: function (t, e, i, n, o) { + o.cx = t + i / 2, o.cy = e + n / 2, o.width = i, o.height = n + } + }, WT = {}; + d({ + line: MS, + rect: SS, + roundRect: SS, + square: SS, + circle: pS, + diamond: zT, + pin: BT, + arrow: VT, + triangle: RT + }, function (t, e) { + WT[e] = new t + }); + var FT = En({ + type: "symbol", shape: {symbolType: "", x: 0, y: 0, width: 0, height: 0}, beforeBrush: function () { + var t = this.style; + "pin" === this.shape.symbolType && "inside" === t.textPosition && (t.textPosition = ["50%", "40%"], t.textAlign = "center", t.textVerticalAlign = "middle") + }, buildPath: function (t, e, i) { + var n = e.symbolType, o = WT[n]; + "none" !== e.symbolType && (o || (o = WT[n = "rect"]), GT[n](e.x, e.y, e.width, e.height, o.shape), o.buildPath(t, o.shape, i)) + } + }), HT = {isDimensionStacked: Zs, enableDataStack: Hs, getStackedDimension: Us}, ZT = (Object.freeze || Object)({ + createList: function (t) { + return Xs(t.getSource(), t) + }, getLayoutRect: Qo, dataStack: HT, createScale: function (t, e) { + var i = e; + wo.isInstance(e) || h(i = new wo(e), ET); + var n = xl(i); + return n.setExtent(t[0], t[1]), yl(n, i), n + }, mixinAxisModelCommonMethods: function (t) { + h(t, ET) + }, completeDimensions: Gs, createDimensions: hT, createSymbol: Tl + }), UT = 1e-8; + Cl.prototype = { + constructor: Cl, properties: null, getBoundingRect: function () { + var t = this._rect; + if (t) return t; + for (var e = Number.MAX_VALUE, i = [e, e], n = [-e, -e], o = [], a = [], r = this.geometries, s = 0; s < r.length; s++) "polygon" === r[s].type && (rn(r[s].exterior, o, a), tt(i, i, o), et(n, n, a)); + return 0 === s && (i[0] = i[1] = n[0] = n[1] = 0), this._rect = new $t(i[0], i[1], n[0] - i[0], n[1] - i[1]) + }, contain: function (t) { + var e = this.getBoundingRect(), i = this.geometries; + if (!e.contain(t[0], t[1])) return !1; + t:for (var n = 0, o = i.length; n < o; n++) if ("polygon" === i[n].type) { + var a = i[n].exterior, r = i[n].interiors; + if (Al(a, t[0], t[1])) { + for (var s = 0; s < (r ? r.length : 0); s++) if (Al(r[s])) continue t; + return !0 + } + } + return !1 + }, transformTo: function (t, e, i, n) { + var o = this.getBoundingRect(), a = o.width / o.height; + i ? n || (n = i / a) : i = a * n; + for (var r = new $t(t, e, i, n), s = o.calculateTransform(r), l = this.geometries, u = 0; u < l.length; u++) if ("polygon" === l[u].type) { + for (var h = l[u].exterior, c = l[u].interiors, d = 0; d < h.length; d++) Q(h[d], h[d], s); + for (var f = 0; f < (c ? c.length : 0); f++) for (d = 0; d < c[f].length; d++) Q(c[f][d], c[f][d], s) + } + (o = this._rect).copy(r), this.center = [o.x + o.width / 2, o.y + o.height / 2] + } + }; + var XT = function (t) { + return Ll(t), f(g(t.features, function (t) { + return t.geometry && t.properties && t.geometry.coordinates.length > 0 + }), function (t) { + var e = t.properties, i = t.geometry, n = i.coordinates, o = []; + "Polygon" === i.type && o.push({ + type: "polygon", + exterior: n[0], + interiors: n.slice(1) + }), "MultiPolygon" === i.type && d(n, function (t) { + t[0] && o.push({type: "polygon", exterior: t[0], interiors: t.slice(1)}) + }); + var a = new Cl(e.name, o, e.cp); + return a.properties = e, a + }) + }, jT = Ni(), YT = [0, 1], qT = function (t, e, i) { + this.dim = t, this.scale = e, this._extent = i || [0, 0], this.inverse = !1, this.onBand = !1 + }; + qT.prototype = { + constructor: qT, contain: function (t) { + var e = this._extent, i = Math.min(e[0], e[1]), n = Math.max(e[0], e[1]); + return t >= i && t <= n + }, containData: function (t) { + return this.contain(this.dataToCoord(t)) + }, getExtent: function () { + return this._extent.slice() + }, getPixelPrecision: function (t) { + return Po(t || this.scale.getExtent(), this._extent) + }, setExtent: function (t, e) { + var i = this._extent; + i[0] = t, i[1] = e + }, dataToCoord: function (t, e) { + var i = this._extent, n = this.scale; + return t = n.normalize(t), this.onBand && "ordinal" === n.type && jl(i = i.slice(), n.count()), To(t, YT, i, e) + }, coordToData: function (t, e) { + var i = this._extent, n = this.scale; + this.onBand && "ordinal" === n.type && jl(i = i.slice(), n.count()); + var o = To(t, i, YT, e); + return this.scale.scale(o) + }, pointToData: function (t, e) { + }, getTicksCoords: function (t) { + var e = (t = t || {}).tickModel || this.getTickModel(), i = Nl(this, e), n = f(i.ticks, function (t) { + return {coord: this.dataToCoord(t), tickValue: t} + }, this), o = e.get("alignWithLabel"); + return Yl(this, n, i.tickCategoryInterval, o, t.clamp), n + }, getViewLabels: function () { + return Pl(this).labels + }, getLabelModel: function () { + return this.model.getModel("axisLabel") + }, getTickModel: function () { + return this.model.getModel("axisTick") + }, getBandWidth: function () { + var t = this._extent, e = this.scale.getExtent(), i = e[1] - e[0] + (this.onBand ? 1 : 0); + 0 === i && (i = 1); + var n = Math.abs(t[1] - t[0]); + return Math.abs(n) / i + }, isHorizontal: null, getRotate: null, calculateCategoryInterval: function () { + return Fl(this) + } + }; + var KT = XT, $T = {}; + d(["map", "each", "filter", "indexOf", "inherits", "reduce", "filter", "bind", "curry", "isArray", "isString", "isObject", "isFunction", "extend", "defaults", "clone", "merge"], function (t) { + $T[t] = x_[t] + }), KM.extend({ + type: "series.line", + dependencies: ["grid", "polar"], + getInitialData: function (t, e) { + return Xs(this.getSource(), this) + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "cartesian2d", + legendHoverLink: !0, + hoverAnimation: !0, + clipOverflow: !0, + label: {position: "top"}, + lineStyle: {width: 2, type: "solid"}, + step: !1, + smooth: !1, + smoothMonotone: null, + symbol: "emptyCircle", + symbolSize: 4, + symbolRotate: null, + showSymbol: !0, + showAllSymbol: "auto", + connectNulls: !1, + sampling: "none", + animationEasing: "linear", + progressive: 0, + hoverLayerThreshold: 1 / 0 + } + }); + var JT = Kl.prototype, QT = Kl.getSymbolSize = function (t, e) { + var i = t.getItemVisual(e, "symbolSize"); + return i instanceof Array ? i.slice() : [+i, +i] + }; + JT._createSymbol = function (t, e, i, n, o) { + this.removeAll(); + var a = Tl(t, -1, -1, 2, 2, e.getItemVisual(i, "color"), o); + a.attr({z2: 100, culling: !0, scale: $l(n)}), a.drift = Jl, this._symbolType = t, this.add(a) + }, JT.stopSymbolAnimation = function (t) { + this.childAt(0).stopAnimation(t) + }, JT.getSymbolPath = function () { + return this.childAt(0) + }, JT.getScale = function () { + return this.childAt(0).scale + }, JT.highlight = function () { + this.childAt(0).trigger("emphasis") + }, JT.downplay = function () { + this.childAt(0).trigger("normal") + }, JT.setZ = function (t, e) { + var i = this.childAt(0); + i.zlevel = t, i.z = e + }, JT.setDraggable = function (t) { + var e = this.childAt(0); + e.draggable = t, e.cursor = t ? "move" : "pointer" + }, JT.updateData = function (t, e, i) { + this.silent = !1; + var n = t.getItemVisual(e, "symbol") || "circle", o = t.hostModel, a = QT(t, e), r = n !== this._symbolType; + if (r) { + var s = t.getItemVisual(e, "symbolKeepAspect"); + this._createSymbol(n, t, e, a, s) + } else (l = this.childAt(0)).silent = !1, fo(l, {scale: $l(a)}, o, e); + if (this._updateCommon(t, e, a, i), r) { + var l = this.childAt(0), u = i && i.fadeIn, h = {scale: l.scale.slice()}; + u && (h.style = {opacity: l.style.opacity}), l.scale = [0, 0], u && (l.style.opacity = 0), po(l, h, o, e) + } + this._seriesModel = o + }; + var tD = ["itemStyle"], eD = ["emphasis", "itemStyle"], iD = ["label"], nD = ["emphasis", "label"]; + JT._updateCommon = function (t, e, i, n) { + var o = this.childAt(0), r = t.hostModel, s = t.getItemVisual(e, "color"); + "image" !== o.type && o.useStyle({strokeNoScale: !0}); + var l = n && n.itemStyle, u = n && n.hoverItemStyle, h = n && n.symbolRotate, c = n && n.symbolOffset, + d = n && n.labelModel, f = n && n.hoverLabelModel, p = n && n.hoverAnimation, g = n && n.cursorStyle; + if (!n || t.hasItemOption) { + var m = n && n.itemModel ? n.itemModel : t.getItemModel(e); + l = m.getModel(tD).getItemStyle(["color"]), u = m.getModel(eD).getItemStyle(), h = m.getShallow("symbolRotate"), c = m.getShallow("symbolOffset"), d = m.getModel(iD), f = m.getModel(nD), p = m.getShallow("hoverAnimation"), g = m.getShallow("cursor") + } else u = a({}, u); + var v = o.style; + o.attr("rotation", (h || 0) * Math.PI / 180 || 0), c && o.attr("position", [Do(c[0], i[0]), Do(c[1], i[1])]), g && o.attr("cursor", g), o.setColor(s, n && n.symbolInnerColor), o.setStyle(l); + var y = t.getItemVisual(e, "opacity"); + null != y && (v.opacity = y); + var x = t.getItemVisual(e, "liftZ"), _ = o.__z2Origin; + null != x ? null == _ && (o.__z2Origin = o.z2, o.z2 += x) : null != _ && (o.z2 = _, o.__z2Origin = null); + var w = n && n.useNameLabel; + io(v, u, d, f, { + labelFetcher: r, labelDataIndex: e, defaultText: function (e, i) { + return w ? t.getName(e) : ql(t, e) + }, isRectText: !0, autoColor: s + }), o.off("mouseover").off("mouseout").off("emphasis").off("normal"), o.hoverStyle = u, eo(o); + var b = $l(i); + if (p && r.isAnimationEnabled()) { + var S = function () { + if (!this.incremental) { + var t = b[1] / b[0]; + this.animateTo({scale: [Math.max(1.1 * b[0], b[0] + 3), Math.max(1.1 * b[1], b[1] + 3 * t)]}, 400, "elasticOut") + } + }, M = function () { + this.incremental || this.animateTo({scale: b}, 400, "elasticOut") + }; + o.on("mouseover", S).on("mouseout", M).on("emphasis", S).on("normal", M) + } + }, JT.fadeOut = function (t, e) { + var i = this.childAt(0); + this.silent = i.silent = !0, !(e && e.keepLabel) && (i.style.text = null), fo(i, { + style: {opacity: 0}, + scale: [0, 0] + }, this._seriesModel, this.dataIndex, t) + }, u(Kl, sw); + var oD = Ql.prototype; + oD.updateData = function (t, e) { + e = eu(e); + var i = this.group, n = t.hostModel, o = this._data, a = this._symbolCtor, r = iu(t); + o || i.removeAll(), t.diff(o).add(function (n) { + var o = t.getItemLayout(n); + if (tu(t, o, n, e)) { + var s = new a(t, n, r); + s.attr("position", o), t.setItemGraphicEl(n, s), i.add(s) + } + }).update(function (s, l) { + var u = o.getItemGraphicEl(l), h = t.getItemLayout(s); + tu(t, h, s, e) ? (u ? (u.updateData(t, s, r), fo(u, {position: h}, n)) : (u = new a(t, s)).attr("position", h), i.add(u), t.setItemGraphicEl(s, u)) : i.remove(u) + }).remove(function (t) { + var e = o.getItemGraphicEl(t); + e && e.fadeOut(function () { + i.remove(e) + }) + }).execute(), this._data = t + }, oD.isPersistent = function () { + return !0 + }, oD.updateLayout = function () { + var t = this._data; + t && t.eachItemGraphicEl(function (e, i) { + var n = t.getItemLayout(i); + e.attr("position", n) + }) + }, oD.incrementalPrepareUpdate = function (t) { + this._seriesScope = iu(t), this._data = null, this.group.removeAll() + }, oD.incrementalUpdate = function (t, e, i) { + i = eu(i); + for (var n = t.start; n < t.end; n++) { + var o = e.getItemLayout(n); + if (tu(e, o, n, i)) { + var a = new this._symbolCtor(e, n, this._seriesScope); + a.traverse(function (t) { + t.isGroup || (t.incremental = t.useHoverLayer = !0) + }), a.attr("position", o), this.group.add(a), e.setItemGraphicEl(n, a) + } + } + }, oD.remove = function (t) { + var e = this.group, i = this._data; + i && t ? i.eachItemGraphicEl(function (t) { + t.fadeOut(function () { + e.remove(t) + }) + }) : e.removeAll() + }; + var aD = function (t, e, i, n, o, a, r, s) { + for (var l = ru(t, e), u = [], h = [], c = [], d = [], f = [], p = [], g = [], m = nu(o, e, r), v = nu(a, t, s), y = 0; y < l.length; y++) { + var x = l[y], _ = !0; + switch (x.cmd) { + case"=": + var w = t.getItemLayout(x.idx), b = e.getItemLayout(x.idx1); + (isNaN(w[0]) || isNaN(w[1])) && (w = b.slice()), u.push(w), h.push(b), c.push(i[x.idx]), d.push(n[x.idx1]), g.push(e.getRawIndex(x.idx1)); + break; + case"+": + S = x.idx; + u.push(o.dataToPoint([e.get(m.dataDimsForPoint[0], S), e.get(m.dataDimsForPoint[1], S)])), h.push(e.getItemLayout(S).slice()), c.push(au(m, o, e, S)), d.push(n[S]), g.push(e.getRawIndex(S)); + break; + case"-": + var S = x.idx, M = t.getRawIndex(S); + M !== S ? (u.push(t.getItemLayout(S)), h.push(a.dataToPoint([t.get(v.dataDimsForPoint[0], S), t.get(v.dataDimsForPoint[1], S)])), c.push(i[S]), d.push(au(v, a, t, S)), g.push(M)) : _ = !1 + } + _ && (f.push(x), p.push(p.length)) + } + p.sort(function (t, e) { + return g[t] - g[e] + }); + for (var I = [], T = [], D = [], A = [], C = [], y = 0; y < p.length; y++) { + S = p[y]; + I[y] = u[S], T[y] = h[S], D[y] = c[S], A[y] = d[S], C[y] = f[S] + } + return {current: I, next: T, stackedOnCurrent: D, stackedOnNext: A, status: C} + }, rD = tt, sD = et, lD = Z, uD = G, hD = [], cD = [], dD = [], fD = In.extend({ + type: "ec-polyline", + shape: {points: [], smooth: 0, smoothConstraint: !0, smoothMonotone: null, connectNulls: !1}, + style: {fill: null, stroke: "#000"}, + brush: mS(In.prototype.brush), + buildPath: function (t, e) { + var i = e.points, n = 0, o = i.length, a = cu(i, e.smoothConstraint); + if (e.connectNulls) { + for (; o > 0 && su(i[o - 1]); o--) ; + for (; n < o && su(i[n]); n++) ; + } + for (; n < o;) n += lu(t, i, n, o, o, 1, a.min, a.max, e.smooth, e.smoothMonotone, e.connectNulls) + 1 + } + }), pD = In.extend({ + type: "ec-polygon", + shape: { + points: [], + stackedOnPoints: [], + smooth: 0, + stackedOnSmooth: 0, + smoothConstraint: !0, + smoothMonotone: null, + connectNulls: !1 + }, + brush: mS(In.prototype.brush), + buildPath: function (t, e) { + var i = e.points, n = e.stackedOnPoints, o = 0, a = i.length, r = e.smoothMonotone, + s = cu(i, e.smoothConstraint), l = cu(n, e.smoothConstraint); + if (e.connectNulls) { + for (; a > 0 && su(i[a - 1]); a--) ; + for (; o < a && su(i[o]); o++) ; + } + for (; o < a;) { + var u = lu(t, i, o, a, a, 1, s.min, s.max, e.smooth, r, e.connectNulls); + lu(t, n, o + u - 1, u, a, -1, l.min, l.max, e.stackedOnSmooth, r, e.connectNulls), o += u + 1, t.closePath() + } + } + }); + gr.extend({ + type: "line", init: function () { + var t = new sw, e = new Ql; + this.group.add(e.group), this._symbolDraw = e, this._lineGroup = t + }, render: function (t, e, i) { + var n = t.coordinateSystem, o = this.group, a = t.getData(), s = t.getModel("lineStyle"), + l = t.getModel("areaStyle"), u = a.mapArray(a.getItemLayout), h = "polar" === n.type, + c = this._coordSys, d = this._symbolDraw, f = this._polyline, p = this._polygon, g = this._lineGroup, + m = t.get("animation"), v = !l.isEmpty(), y = l.get("origin"), x = gu(n, a, nu(n, a, y)), + _ = t.get("showSymbol"), w = _ && !h && wu(t, a, n), b = this._data; + b && b.eachItemGraphicEl(function (t, e) { + t.__temp && (o.remove(t), b.setItemGraphicEl(e, null)) + }), _ || d.remove(), o.add(g); + var S = !h && t.get("step"); + f && c.type === n.type && S === this._step ? (v && !p ? p = this._newPolygon(u, x, n, m) : p && !v && (g.remove(p), p = this._polygon = null), g.setClipPath(yu(n, !1, !1, t)), _ && d.updateData(a, { + isIgnore: w, + clipShape: yu(n, !1, !0, t) + }), a.eachItemGraphicEl(function (t) { + t.stopAnimation(!0) + }), du(this._stackedOnPoints, x) && du(this._points, u) || (m ? this._updateAnimation(a, x, n, i, S, y) : (S && (u = xu(u, n, S), x = xu(x, n, S)), f.setShape({points: u}), p && p.setShape({ + points: u, + stackedOnPoints: x + })))) : (_ && d.updateData(a, { + isIgnore: w, + clipShape: yu(n, !1, !0, t) + }), S && (u = xu(u, n, S), x = xu(x, n, S)), f = this._newPolyline(u, n, m), v && (p = this._newPolygon(u, x, n, m)), g.setClipPath(yu(n, !0, !1, t))); + var M = _u(a, n) || a.getVisual("color"); + f.useStyle(r(s.getLineStyle(), {fill: "none", stroke: M, lineJoin: "bevel"})); + var I = t.get("smooth"); + if (I = fu(t.get("smooth")), f.setShape({ + smooth: I, + smoothMonotone: t.get("smoothMonotone"), + connectNulls: t.get("connectNulls") + }), p) { + var T = a.getCalculationInfo("stackedOnSeries"), D = 0; + p.useStyle(r(l.getAreaStyle(), { + fill: M, + opacity: .7, + lineJoin: "bevel" + })), T && (D = fu(T.get("smooth"))), p.setShape({ + smooth: I, + stackedOnSmooth: D, + smoothMonotone: t.get("smoothMonotone"), + connectNulls: t.get("connectNulls") + }) + } + this._data = a, this._coordSys = n, this._stackedOnPoints = x, this._points = u, this._step = S, this._valueOrigin = y + }, dispose: function () { + }, highlight: function (t, e, i, n) { + var o = t.getData(), a = Pi(o, n); + if (!(a instanceof Array) && null != a && a >= 0) { + var r = o.getItemGraphicEl(a); + if (!r) { + var s = o.getItemLayout(a); + if (!s) return; + (r = new Kl(o, a)).position = s, r.setZ(t.get("zlevel"), t.get("z")), r.ignore = isNaN(s[0]) || isNaN(s[1]), r.__temp = !0, o.setItemGraphicEl(a, r), r.stopSymbolAnimation(!0), this.group.add(r) + } + r.highlight() + } else gr.prototype.highlight.call(this, t, e, i, n) + }, downplay: function (t, e, i, n) { + var o = t.getData(), a = Pi(o, n); + if (null != a && a >= 0) { + var r = o.getItemGraphicEl(a); + r && (r.__temp ? (o.setItemGraphicEl(a, null), this.group.remove(r)) : r.downplay()) + } else gr.prototype.downplay.call(this, t, e, i, n) + }, _newPolyline: function (t) { + var e = this._polyline; + return e && this._lineGroup.remove(e), e = new fD({ + shape: {points: t}, + silent: !0, + z2: 10 + }), this._lineGroup.add(e), this._polyline = e, e + }, _newPolygon: function (t, e) { + var i = this._polygon; + return i && this._lineGroup.remove(i), i = new pD({ + shape: {points: t, stackedOnPoints: e}, + silent: !0 + }), this._lineGroup.add(i), this._polygon = i, i + }, _updateAnimation: function (t, e, i, n, o, a) { + var r = this._polyline, s = this._polygon, l = t.hostModel, + u = aD(this._data, t, this._stackedOnPoints, e, this._coordSys, i, this._valueOrigin, a), h = u.current, + c = u.stackedOnCurrent, d = u.next, f = u.stackedOnNext; + o && (h = xu(u.current, i, o), c = xu(u.stackedOnCurrent, i, o), d = xu(u.next, i, o), f = xu(u.stackedOnNext, i, o)), r.shape.__points = u.current, r.shape.points = h, fo(r, {shape: {points: d}}, l), s && (s.setShape({ + points: h, + stackedOnPoints: c + }), fo(s, {shape: {points: d, stackedOnPoints: f}}, l)); + for (var p = [], g = u.status, m = 0; m < g.length; m++) if ("=" === g[m].cmd) { + var v = t.getItemGraphicEl(g[m].idx1); + v && p.push({el: v, ptIdx: m}) + } + r.animators && r.animators.length && r.animators[0].during(function () { + for (var t = 0; t < p.length; t++) p[t].el.attr("position", r.shape.__points[p[t].ptIdx]) + }) + }, remove: function (t) { + var e = this.group, i = this._data; + this._lineGroup.removeAll(), this._symbolDraw.remove(!0), i && i.eachItemGraphicEl(function (t, n) { + t.__temp && (e.remove(t), i.setItemGraphicEl(n, null)) + }), this._polyline = this._polygon = this._coordSys = this._points = this._stackedOnPoints = this._data = null + } + }); + var gD = function (t, e, i) { + return { + seriesType: t, performRawSeries: !0, reset: function (t, n, o) { + var a = t.getData(), r = t.get("symbol") || e, s = t.get("symbolSize"), l = t.get("symbolKeepAspect"); + if (a.setVisual({ + legendSymbol: i || r, + symbol: r, + symbolSize: s, + symbolKeepAspect: l + }), !n.isSeriesFiltered(t)) { + var u = "function" == typeof s; + return { + dataEach: a.hasItemOption || u ? function (e, i) { + if ("function" == typeof s) { + var n = t.getRawValue(i), o = t.getDataParams(i); + e.setItemVisual(i, "symbolSize", s(n, o)) + } + if (e.hasItemOption) { + var a = e.getItemModel(i), r = a.getShallow("symbol", !0), + l = a.getShallow("symbolSize", !0), u = a.getShallow("symbolKeepAspect", !0); + null != r && e.setItemVisual(i, "symbol", r), null != l && e.setItemVisual(i, "symbolSize", l), null != u && e.setItemVisual(i, "symbolKeepAspect", u) + } + } : null + } + } + } + } + }, mD = function (t) { + return { + seriesType: t, plan: QM(), reset: function (t) { + var e = t.getData(), i = t.coordinateSystem, n = t.pipelineContext.large; + if (i) { + var o = f(i.dimensions, function (t) { + return e.mapDimension(t) + }).slice(0, 2), a = o.length, r = e.getCalculationInfo("stackResultDimension"); + return Zs(e, o[0]) && (o[0] = r), Zs(e, o[1]) && (o[1] = r), a && { + progress: function (t, e) { + for (var r = t.end - t.start, s = n && new Float32Array(r * a), l = t.start, u = 0, h = [], c = []; l < t.end; l++) { + var d; + if (1 === a) f = e.get(o[0], l), d = !isNaN(f) && i.dataToPoint(f, null, c); else { + var f = h[0] = e.get(o[0], l), p = h[1] = e.get(o[1], l); + d = !isNaN(f) && !isNaN(p) && i.dataToPoint(h, null, c) + } + n ? (s[u++] = d ? d[0] : NaN, s[u++] = d ? d[1] : NaN) : e.setItemLayout(l, d && d.slice() || [NaN, NaN]) + } + n && e.setLayout("symbolPoints", s) + } + } + } + } + } + }, vD = { + average: function (t) { + for (var e = 0, i = 0, n = 0; n < t.length; n++) isNaN(t[n]) || (e += t[n], i++); + return 0 === i ? NaN : e / i + }, sum: function (t) { + for (var e = 0, i = 0; i < t.length; i++) e += t[i] || 0; + return e + }, max: function (t) { + for (var e = -1 / 0, i = 0; i < t.length; i++) t[i] > e && (e = t[i]); + return isFinite(e) ? e : NaN + }, min: function (t) { + for (var e = 1 / 0, i = 0; i < t.length; i++) t[i] < e && (e = t[i]); + return isFinite(e) ? e : NaN + }, nearest: function (t) { + return t[0] + } + }, yD = function (t, e) { + return Math.round(t.length / 2) + }, xD = function (t) { + this._axes = {}, this._dimList = [], this.name = t || "" + }; + xD.prototype = { + constructor: xD, type: "cartesian", getAxis: function (t) { + return this._axes[t] + }, getAxes: function () { + return f(this._dimList, Su, this) + }, getAxesByScale: function (t) { + return t = t.toLowerCase(), g(this.getAxes(), function (e) { + return e.scale.type === t + }) + }, addAxis: function (t) { + var e = t.dim; + this._axes[e] = t, this._dimList.push(e) + }, dataToCoord: function (t) { + return this._dataCoordConvert(t, "dataToCoord") + }, coordToData: function (t) { + return this._dataCoordConvert(t, "coordToData") + }, _dataCoordConvert: function (t, e) { + for (var i = this._dimList, n = t instanceof Array ? [] : {}, o = 0; o < i.length; o++) { + var a = i[o], r = this._axes[a]; + n[a] = r[e](t[a]) + } + return n + } + }, Mu.prototype = { + constructor: Mu, type: "cartesian2d", dimensions: ["x", "y"], getBaseAxis: function () { + return this.getAxesByScale("ordinal")[0] || this.getAxesByScale("time")[0] || this.getAxis("x") + }, containPoint: function (t) { + var e = this.getAxis("x"), i = this.getAxis("y"); + return e.contain(e.toLocalCoord(t[0])) && i.contain(i.toLocalCoord(t[1])) + }, containData: function (t) { + return this.getAxis("x").containData(t[0]) && this.getAxis("y").containData(t[1]) + }, dataToPoint: function (t, e, i) { + var n = this.getAxis("x"), o = this.getAxis("y"); + return i = i || [], i[0] = n.toGlobalCoord(n.dataToCoord(t[0])), i[1] = o.toGlobalCoord(o.dataToCoord(t[1])), i + }, clampData: function (t, e) { + var i = this.getAxis("x").scale, n = this.getAxis("y").scale, o = i.getExtent(), a = n.getExtent(), + r = i.parse(t[0]), s = n.parse(t[1]); + return e = e || [], e[0] = Math.min(Math.max(Math.min(o[0], o[1]), r), Math.max(o[0], o[1])), e[1] = Math.min(Math.max(Math.min(a[0], a[1]), s), Math.max(a[0], a[1])), e + }, pointToData: function (t, e) { + var i = this.getAxis("x"), n = this.getAxis("y"); + return e = e || [], e[0] = i.coordToData(i.toLocalCoord(t[0])), e[1] = n.coordToData(n.toLocalCoord(t[1])), e + }, getOtherAxis: function (t) { + return this.getAxis("x" === t.dim ? "y" : "x") + } + }, u(Mu, xD); + var _D = function (t, e, i, n, o) { + qT.call(this, t, e, i), this.type = n || "value", this.position = o || "bottom" + }; + _D.prototype = { + constructor: _D, index: 0, getAxesOnZeroOf: null, model: null, isHorizontal: function () { + var t = this.position; + return "top" === t || "bottom" === t + }, getGlobalExtent: function (t) { + var e = this.getExtent(); + return e[0] = this.toGlobalCoord(e[0]), e[1] = this.toGlobalCoord(e[1]), t && e[0] > e[1] && e.reverse(), e + }, getOtherAxis: function () { + this.grid.getOtherAxis() + }, pointToData: function (t, e) { + return this.coordToData(this.toLocalCoord(t["x" === this.dim ? 0 : 1]), e) + }, toLocalCoord: null, toGlobalCoord: null + }, u(_D, qT); + var wD = { + show: !0, + zlevel: 0, + z: 0, + inverse: !1, + name: "", + nameLocation: "end", + nameRotate: null, + nameTruncate: {maxWidth: null, ellipsis: "...", placeholder: "."}, + nameTextStyle: {}, + nameGap: 15, + silent: !1, + triggerEvent: !1, + tooltip: {show: !1}, + axisPointer: {}, + axisLine: { + show: !0, + onZero: !0, + onZeroAxisIndex: null, + lineStyle: {color: "#333", width: 1, type: "solid"}, + symbol: ["none", "none"], + symbolSize: [10, 15] + }, + axisTick: {show: !0, inside: !1, length: 5, lineStyle: {width: 1}}, + axisLabel: {show: !0, inside: !1, rotate: 0, showMinLabel: null, showMaxLabel: null, margin: 8, fontSize: 12}, + splitLine: {show: !0, lineStyle: {color: ["#ccc"], width: 1, type: "solid"}}, + splitArea: {show: !1, areaStyle: {color: ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"]}} + }, bD = {}; + bD.categoryAxis = n({ + boundaryGap: !0, + deduplication: null, + splitLine: {show: !1}, + axisTick: {alignWithLabel: !1, interval: "auto"}, + axisLabel: {interval: "auto"} + }, wD), bD.valueAxis = n({boundaryGap: [0, 0], splitNumber: 5}, wD), bD.timeAxis = r({ + scale: !0, + min: "dataMin", + max: "dataMax" + }, bD.valueAxis), bD.logAxis = r({scale: !0, logBase: 10}, bD.valueAxis); + var SD = ["value", "category", "time", "log"], MD = function (t, e, i, a) { + d(SD, function (r) { + e.extend({ + type: t + "Axis." + r, mergeDefaultAndTheme: function (e, o) { + var a = this.layoutMode, s = a ? na(e) : {}; + n(e, o.getTheme().get(r + "Axis")), n(e, this.getDefaultOption()), e.type = i(t, e), a && ia(e, s, a) + }, optionUpdated: function () { + "category" === this.option.type && (this.__ordinalMeta = Ks.createByAxisModel(this)) + }, getCategories: function (t) { + var e = this.option; + if ("category" === e.type) return t ? e.data : this.__ordinalMeta.categories + }, getOrdinalMeta: function () { + return this.__ordinalMeta + }, defaultOption: o([{}, bD[r + "Axis"], a], !0) + }) + }), hM.registerSubTypeDefaulter(t + "Axis", v(i, t)) + }, ID = hM.extend({ + type: "cartesian2dAxis", axis: null, init: function () { + ID.superApply(this, "init", arguments), this.resetRange() + }, mergeOption: function () { + ID.superApply(this, "mergeOption", arguments), this.resetRange() + }, restoreData: function () { + ID.superApply(this, "restoreData", arguments), this.resetRange() + }, getCoordSysModel: function () { + return this.ecModel.queryComponents({ + mainType: "grid", + index: this.option.gridIndex, + id: this.option.gridId + })[0] + } + }); + n(ID.prototype, ET); + var TD = {offset: 0}; + MD("x", ID, Iu, TD), MD("y", ID, Iu, TD), hM.extend({ + type: "grid", + dependencies: ["xAxis", "yAxis"], + layoutMode: "box", + coordinateSystem: null, + defaultOption: { + show: !1, + zlevel: 0, + z: 0, + left: "10%", + top: 60, + right: "10%", + bottom: 60, + containLabel: !1, + backgroundColor: "rgba(0,0,0,0)", + borderWidth: 1, + borderColor: "#ccc" + } + }); + var DD = Du.prototype; + DD.type = "grid", DD.axisPointerEnabled = !0, DD.getRect = function () { + return this._rect + }, DD.update = function (t, e) { + var i = this._axesMap; + this._updateScale(t, this.model), d(i.x, function (t) { + yl(t.scale, t.model) + }), d(i.y, function (t) { + yl(t.scale, t.model) + }), d(i.x, function (t) { + Au(i, "y", t) + }), d(i.y, function (t) { + Au(i, "x", t) + }), this.resize(this.model, e) + }, DD.resize = function (t, e, i) { + function n() { + d(a, function (t) { + var e = t.isHorizontal(), i = e ? [0, o.width] : [0, o.height], n = t.inverse ? 1 : 0; + t.setExtent(i[n], i[1 - n]), Lu(t, e ? o.x : o.y) + }) + } + + var o = Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}); + this._rect = o; + var a = this._axesList; + n(), !i && t.get("containLabel") && (d(a, function (t) { + if (!t.model.get("axisLabel.inside")) { + var e = Sl(t); + if (e) { + var i = t.isHorizontal() ? "height" : "width", n = t.model.get("axisLabel.margin"); + o[i] -= e[i] + n, "top" === t.position ? o.y += e.height + n : "left" === t.position && (o.x += e.width + n) + } + } + }), n()) + }, DD.getAxis = function (t, e) { + var i = this._axesMap[t]; + if (null != i) { + if (null == e) for (var n in i) if (i.hasOwnProperty(n)) return i[n]; + return i[e] + } + }, DD.getAxes = function () { + return this._axesList.slice() + }, DD.getCartesian = function (t, e) { + if (null != t && null != e) { + var i = "x" + t + "y" + e; + return this._coordsMap[i] + } + w(t) && (e = t.yAxisIndex, t = t.xAxisIndex); + for (var n = 0, o = this._coordsList; n < o.length; n++) if (o[n].getAxis("x").index === t || o[n].getAxis("y").index === e) return o[n] + }, DD.getCartesians = function () { + return this._coordsList.slice() + }, DD.convertToPixel = function (t, e, i) { + var n = this._findConvertTarget(t, e); + return n.cartesian ? n.cartesian.dataToPoint(i) : n.axis ? n.axis.toGlobalCoord(n.axis.dataToCoord(i)) : null + }, DD.convertFromPixel = function (t, e, i) { + var n = this._findConvertTarget(t, e); + return n.cartesian ? n.cartesian.pointToData(i) : n.axis ? n.axis.coordToData(n.axis.toLocalCoord(i)) : null + }, DD._findConvertTarget = function (t, e) { + var i, n, o = e.seriesModel, a = e.xAxisModel || o && o.getReferringComponents("xAxis")[0], + r = e.yAxisModel || o && o.getReferringComponents("yAxis")[0], s = e.gridModel, u = this._coordsList; + return o ? l(u, i = o.coordinateSystem) < 0 && (i = null) : a && r ? i = this.getCartesian(a.componentIndex, r.componentIndex) : a ? n = this.getAxis("x", a.componentIndex) : r ? n = this.getAxis("y", r.componentIndex) : s && s.coordinateSystem === this && (i = this._coordsList[0]), { + cartesian: i, + axis: n + } + }, DD.containPoint = function (t) { + var e = this._coordsList[0]; + if (e) return e.containPoint(t) + }, DD._initCartesian = function (t, e, i) { + function n(i) { + return function (n, s) { + if (Tu(n, t, e)) { + var l = n.get("position"); + "x" === i ? "top" !== l && "bottom" !== l && o[l = "bottom"] && (l = "top" === l ? "bottom" : "top") : "left" !== l && "right" !== l && o[l = "left"] && (l = "left" === l ? "right" : "left"), o[l] = !0; + var u = new _D(i, xl(n), [0, 0], n.get("type"), l), h = "category" === u.type; + u.onBand = h && n.get("boundaryGap"), u.inverse = n.get("inverse"), n.axis = u, u.model = n, u.grid = this, u.index = s, this._axesList.push(u), a[i][s] = u, r[i]++ + } + } + } + + var o = {left: !1, right: !1, top: !1, bottom: !1}, a = {x: {}, y: {}}, r = {x: 0, y: 0}; + if (e.eachComponent("xAxis", n("x"), this), e.eachComponent("yAxis", n("y"), this), !r.x || !r.y) return this._axesMap = {}, void (this._axesList = []); + this._axesMap = a, d(a.x, function (e, i) { + d(a.y, function (n, o) { + var a = "x" + i + "y" + o, r = new Mu(a); + r.grid = this, r.model = t, this._coordsMap[a] = r, this._coordsList.push(r), r.addAxis(e), r.addAxis(n) + }, this) + }, this) + }, DD._updateScale = function (t, e) { + function i(t, e, i) { + d(t.mapDimension(e.dim, !0), function (i) { + e.scale.unionExtentFromData(t, Us(t, i)) + }) + } + + d(this._axesList, function (t) { + t.scale.setExtent(1 / 0, -1 / 0) + }), t.eachSeries(function (n) { + if (Pu(n)) { + var o = ku(n), a = o[0], r = o[1]; + if (!Tu(a, e, t) || !Tu(r, e, t)) return; + var s = this.getCartesian(a.componentIndex, r.componentIndex), l = n.getData(), u = s.getAxis("x"), + h = s.getAxis("y"); + "list" === l.type && (i(l, u), i(l, h)) + } + }, this) + }, DD.getTooltipAxes = function (t) { + var e = [], i = []; + return d(this.getCartesians(), function (n) { + var o = null != t && "auto" !== t ? n.getAxis(t) : n.getBaseAxis(), a = n.getOtherAxis(o); + l(e, o) < 0 && e.push(o), l(i, a) < 0 && i.push(a) + }), {baseAxes: e, otherAxes: i} + }; + var AD = ["xAxis", "yAxis"]; + Du.create = function (t, e) { + var i = []; + return t.eachComponent("grid", function (n, o) { + var a = new Du(n, t, e); + a.name = "grid_" + o, a.resize(n, e, !0), n.coordinateSystem = a, i.push(a) + }), t.eachSeries(function (t) { + if (Pu(t)) { + var e = ku(t), i = e[0], n = e[1], o = i.getCoordSysModel().coordinateSystem; + t.coordinateSystem = o.getCartesian(i.componentIndex, n.componentIndex) + } + }), i + }, Du.dimensions = Du.prototype.dimensions = Mu.prototype.dimensions, Ca.register("cartesian2d", Du); + var CD = Math.PI, LD = function (t, e) { + this.opt = e, this.axisModel = t, r(e, { + labelOffset: 0, + nameDirection: 1, + tickDirection: 1, + labelDirection: 1, + silent: !0 + }), this.group = new sw; + var i = new sw({position: e.position.slice(), rotation: e.rotation}); + i.updateTransform(), this._transform = i.transform, this._dumbGroup = i + }; + LD.prototype = { + constructor: LD, hasBuilder: function (t) { + return !!kD[t] + }, add: function (t) { + kD[t].call(this) + }, getGroup: function () { + return this.group + } + }; + var kD = { + axisLine: function () { + var t = this.opt, e = this.axisModel; + if (e.get("axisLine.show")) { + var i = this.axisModel.axis.getExtent(), n = this._transform, o = [i[0], 0], r = [i[1], 0]; + n && (Q(o, o, n), Q(r, r, n)); + var s = a({lineCap: "round"}, e.getModel("axisLine.lineStyle").getLineStyle()); + this.group.add(new MS(Gn({ + anid: "line", + shape: {x1: o[0], y1: o[1], x2: r[0], y2: r[1]}, + style: s, + strokeContainThreshold: t.strokeContainThreshold || 5, + silent: !0, + z2: 1 + }))); + var l = e.get("axisLine.symbol"), u = e.get("axisLine.symbolSize"), + h = e.get("axisLine.symbolOffset") || 0; + if ("number" == typeof h && (h = [h, h]), null != l) { + "string" == typeof l && (l = [l, l]), "string" != typeof u && "number" != typeof u || (u = [u, u]); + var c = u[0], f = u[1]; + d([{rotate: t.rotation + Math.PI / 2, offset: h[0], r: 0}, { + rotate: t.rotation - Math.PI / 2, + offset: h[1], + r: Math.sqrt((o[0] - r[0]) * (o[0] - r[0]) + (o[1] - r[1]) * (o[1] - r[1])) + }], function (e, i) { + if ("none" !== l[i] && null != l[i]) { + var n = Tl(l[i], -c / 2, -f / 2, c, f, s.stroke, !0), a = e.r + e.offset, + r = [o[0] + a * Math.cos(t.rotation), o[1] - a * Math.sin(t.rotation)]; + n.attr({rotation: e.rotate, position: r, silent: !0}), this.group.add(n) + } + }, this) + } + } + }, axisTickLabel: function () { + var t = this.axisModel, e = this.opt, i = Gu(this, t, e); + Ru(t, Wu(this, t, e), i) + }, axisName: function () { + var t = this.opt, e = this.axisModel, i = T(t.axisName, e.get("name")); + if (i) { + var n, o = e.get("nameLocation"), r = t.nameDirection, s = e.getModel("nameTextStyle"), + l = e.get("nameGap") || 0, u = this.axisModel.axis.getExtent(), h = u[0] > u[1] ? -1 : 1, + c = ["start" === o ? u[0] - h * l : "end" === o ? u[1] + h * l : (u[0] + u[1]) / 2, Vu(o) ? t.labelOffset + r * l : 0], + d = e.get("nameRotate"); + null != d && (d = d * CD / 180); + var f; + Vu(o) ? n = PD(t.rotation, null != d ? d : t.rotation, r) : (n = Ou(t, o, d || 0, u), null != (f = t.axisNameAvailableWidth) && (f = Math.abs(f / Math.sin(n.rotation)), !isFinite(f) && (f = null))); + var p = s.getFont(), g = e.get("nameTruncate", !0) || {}, m = g.ellipsis, + v = T(t.nameTruncateMaxWidth, g.maxWidth, f), + y = null != m && null != v ? eM(i, v, p, m, {minChar: 2, placeholder: g.placeholder}) : i, + x = e.get("tooltip", !0), _ = e.mainType, w = {componentType: _, name: i, $vars: ["name"]}; + w[_ + "Index"] = e.componentIndex; + var b = new fS({ + anid: "name", + __fullText: i, + __truncatedText: y, + position: c, + rotation: n.rotation, + silent: Eu(e), + z2: 1, + tooltip: x && x.show ? a({ + content: i, formatter: function () { + return i + }, formatterParams: w + }, x) : null + }); + no(b.style, s, { + text: y, + textFont: p, + textFill: s.getTextColor() || e.get("axisLine.lineStyle.color"), + textAlign: n.textAlign, + textVerticalAlign: n.textVerticalAlign + }), e.get("triggerEvent") && (b.eventData = Nu(e), b.eventData.targetType = "axisName", b.eventData.name = i), this._dumbGroup.add(b), b.updateTransform(), this.group.add(b), b.decomposeTransform() + } + } + }, PD = LD.innerTextLayout = function (t, e, i) { + var n, o, a = Oo(e - t); + return Eo(a) ? (o = i > 0 ? "top" : "bottom", n = "center") : Eo(a - CD) ? (o = i > 0 ? "bottom" : "top", n = "center") : (o = "middle", n = a > 0 && a < CD ? i > 0 ? "right" : "left" : i > 0 ? "left" : "right"), { + rotation: a, + textAlign: n, + textVerticalAlign: o + } + }, ND = d, OD = v, ED = ys({ + type: "axis", _axisPointer: null, axisPointerClass: null, render: function (t, e, i, n) { + this.axisPointerClass && Yu(t), ED.superApply(this, "render", arguments), Qu(this, t, 0, i, 0, !0) + }, updateAxisPointer: function (t, e, i, n, o) { + Qu(this, t, 0, i, 0, !1) + }, remove: function (t, e) { + var i = this._axisPointer; + i && i.remove(e), ED.superApply(this, "remove", arguments) + }, dispose: function (t, e) { + th(this, e), ED.superApply(this, "dispose", arguments) + } + }), RD = []; + ED.registerAxisPointerClass = function (t, e) { + RD[t] = e + }, ED.getAxisPointerClass = function (t) { + return t && RD[t] + }; + var zD = ["axisLine", "axisTickLabel", "axisName"], BD = ["splitArea", "splitLine"], VD = ED.extend({ + type: "cartesianAxis", axisPointerClass: "CartesianAxisPointer", render: function (t, e, i, n) { + this.group.removeAll(); + var o = this._axisGroup; + if (this._axisGroup = new sw, this.group.add(this._axisGroup), t.get("show")) { + var a = t.getCoordSysModel(), r = eh(a, t), s = new LD(t, r); + d(zD, s.add, s), this._axisGroup.add(s.getGroup()), d(BD, function (e) { + t.get(e + ".show") && this["_" + e](t, a) + }, this), yo(o, this._axisGroup, t), VD.superCall(this, "render", t, e, i, n) + } + }, remove: function () { + this._splitAreaColors = null + }, _splitLine: function (t, e) { + var i = t.axis; + if (!i.scale.isBlank()) { + var n = t.getModel("splitLine"), o = n.getModel("lineStyle"), a = o.get("color"); + a = y(a) ? a : [a]; + for (var s = e.coordinateSystem.getRect(), l = i.isHorizontal(), u = 0, h = i.getTicksCoords({tickModel: n}), c = [], d = [], f = o.getLineStyle(), p = 0; p < h.length; p++) { + var g = i.toGlobalCoord(h[p].coord); + l ? (c[0] = g, c[1] = s.y, d[0] = g, d[1] = s.y + s.height) : (c[0] = s.x, c[1] = g, d[0] = s.x + s.width, d[1] = g); + var m = u++ % a.length, v = h[p].tickValue; + this._axisGroup.add(new MS(Gn({ + anid: null != v ? "line_" + h[p].tickValue : null, + shape: {x1: c[0], y1: c[1], x2: d[0], y2: d[1]}, + style: r({stroke: a[m]}, f), + silent: !0 + }))) + } + } + }, _splitArea: function (t, e) { + var i = t.axis; + if (!i.scale.isBlank()) { + var n = t.getModel("splitArea"), o = n.getModel("areaStyle"), a = o.get("color"), + s = e.coordinateSystem.getRect(), l = i.getTicksCoords({tickModel: n, clamp: !0}); + if (l.length) { + var u = a.length, h = this._splitAreaColors, c = R(), d = 0; + if (h) for (m = 0; m < l.length; m++) { + var f = h.get(l[m].tickValue); + if (null != f) { + d = (f + (u - 1) * m) % u; + break + } + } + var p = i.toGlobalCoord(l[0].coord), g = o.getAreaStyle(); + a = y(a) ? a : [a]; + for (var m = 1; m < l.length; m++) { + var v, x, _, w, b = i.toGlobalCoord(l[m].coord); + i.isHorizontal() ? (v = p, x = s.y, _ = b - v, w = s.height, p = v + _) : (v = s.x, x = p, _ = s.width, p = x + (w = b - x)); + var S = l[m - 1].tickValue; + null != S && c.set(S, d), this._axisGroup.add(new SS({ + anid: null != S ? "area_" + S : null, + shape: {x: v, y: x, width: _, height: w}, + style: r({fill: a[d]}, g), + silent: !0 + })), d = (d + 1) % u + } + this._splitAreaColors = c + } + } + } + }); + VD.extend({type: "xAxis"}), VD.extend({type: "yAxis"}), ys({ + type: "grid", render: function (t, e) { + this.group.removeAll(), t.get("show") && this.group.add(new SS({ + shape: t.coordinateSystem.getRect(), + style: r({fill: t.get("backgroundColor")}, t.getItemStyle()), + silent: !0, + z2: -1 + })) + } + }), us(function (t) { + t.xAxis && t.yAxis && !t.grid && (t.grid = {}) + }), ps(gD("line", "circle", "line")), fs(mD("line")), hs(LI.PROCESSOR.STATISTIC, function (t) { + return { + seriesType: t, modifyOutputEnd: !0, reset: function (t, e, i) { + var n = t.getData(), o = t.get("sampling"), a = t.coordinateSystem; + if ("cartesian2d" === a.type && o) { + var r = a.getBaseAxis(), s = a.getOtherAxis(r), l = r.getExtent(), u = l[1] - l[0], + h = Math.round(n.count() / u); + if (h > 1) { + var c; + "string" == typeof o ? c = vD[o] : "function" == typeof o && (c = o), c && t.setData(n.downSample(n.mapDimension(s.dim), 1 / h, c, yD)) + } + } + } + } + }("line")); + var GD = KM.extend({ + type: "series.__base_bar__", + getInitialData: function (t, e) { + return Xs(this.getSource(), this) + }, + getMarkerPosition: function (t) { + var e = this.coordinateSystem; + if (e) { + var i = e.dataToPoint(e.clampData(t)), n = this.getData(), o = n.getLayout("offset"), + a = n.getLayout("size"); + return i[e.getBaseAxis().isHorizontal() ? 0 : 1] += o + a / 2, i + } + return [NaN, NaN] + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "cartesian2d", + legendHoverLink: !0, + barMinHeight: 0, + barMinAngle: 0, + large: !1, + largeThreshold: 400, + progressive: 3e3, + progressiveChunkMode: "mod", + itemStyle: {}, + emphasis: {} + } + }); + GD.extend({ + type: "series.bar", dependencies: ["grid", "polar"], brushSelector: "rect", getProgressive: function () { + return !!this.get("large") && this.get("progressive") + }, getProgressiveThreshold: function () { + var t = this.get("progressiveThreshold"), e = this.get("largeThreshold"); + return e > t && (t = e), t + } + }); + var WD = sb([["fill", "color"], ["stroke", "borderColor"], ["lineWidth", "borderWidth"], ["stroke", "barBorderColor"], ["lineWidth", "barBorderWidth"], ["opacity"], ["shadowBlur"], ["shadowOffsetX"], ["shadowOffsetY"], ["shadowColor"]]), + FD = { + getBarItemStyle: function (t) { + var e = WD(this, t); + if (this.getBorderLineDash) { + var i = this.getBorderLineDash(); + i && (e.lineDash = i) + } + return e + } + }, HD = ["itemStyle", "barBorderWidth"]; + a(wo.prototype, FD), _s({ + type: "bar", render: function (t, e, i) { + this._updateDrawMode(t); + var n = t.get("coordinateSystem"); + return "cartesian2d" !== n && "polar" !== n || (this._isLargeDraw ? this._renderLarge(t, e, i) : this._renderNormal(t, e, i)), this.group + }, incrementalPrepareRender: function (t, e, i) { + this._clear(), this._updateDrawMode(t) + }, incrementalRender: function (t, e, i, n) { + this._incrementalRenderLarge(t, e) + }, _updateDrawMode: function (t) { + var e = t.pipelineContext.large; + (null == this._isLargeDraw || e ^ this._isLargeDraw) && (this._isLargeDraw = e, this._clear()) + }, _renderNormal: function (t, e, i) { + var n, o = this.group, a = t.getData(), r = this._data, s = t.coordinateSystem, l = s.getBaseAxis(); + "cartesian2d" === s.type ? n = l.isHorizontal() : "polar" === s.type && (n = "angle" === l.dim); + var u = t.isAnimationEnabled() ? t : null; + a.diff(r).add(function (e) { + if (a.hasValue(e)) { + var i = a.getItemModel(e), r = UD[s.type](a, e, i), l = ZD[s.type](a, e, i, r, n, u); + a.setItemGraphicEl(e, l), o.add(l), rh(l, a, e, i, r, t, n, "polar" === s.type) + } + }).update(function (e, i) { + var l = r.getItemGraphicEl(i); + if (a.hasValue(e)) { + var h = a.getItemModel(e), c = UD[s.type](a, e, h); + l ? fo(l, {shape: c}, u, e) : l = ZD[s.type](a, e, h, c, n, u, !0), a.setItemGraphicEl(e, l), o.add(l), rh(l, a, e, h, c, t, n, "polar" === s.type) + } else o.remove(l) + }).remove(function (t) { + var e = r.getItemGraphicEl(t); + "cartesian2d" === s.type ? e && oh(t, u, e) : e && ah(t, u, e) + }).execute(), this._data = a + }, _renderLarge: function (t, e, i) { + this._clear(), lh(t, this.group) + }, _incrementalRenderLarge: function (t, e) { + lh(e, this.group, !0) + }, dispose: B, remove: function (t) { + this._clear(t) + }, _clear: function (t) { + var e = this.group, i = this._data; + t && t.get("animation") && i && !this._isLargeDraw ? i.eachItemGraphicEl(function (e) { + "sector" === e.type ? ah(e.dataIndex, t, e) : oh(e.dataIndex, t, e) + }) : e.removeAll(), this._data = null + } + }); + var ZD = { + cartesian2d: function (t, e, i, n, o, r, s) { + var l = new SS({shape: a({}, n)}); + if (r) { + var u = l.shape, h = o ? "height" : "width", c = {}; + u[h] = 0, c[h] = n[h], BS[s ? "updateProps" : "initProps"](l, {shape: c}, r, e) + } + return l + }, polar: function (t, e, i, n, o, a, s) { + var l = n.startAngle < n.endAngle, u = new vS({shape: r({clockwise: l}, n)}); + if (a) { + var h = u.shape, c = o ? "r" : "endAngle", d = {}; + h[c] = o ? 0 : n.startAngle, d[c] = n[c], BS[s ? "updateProps" : "initProps"](u, {shape: d}, a, e) + } + return u + } + }, UD = { + cartesian2d: function (t, e, i) { + var n = t.getItemLayout(e), o = sh(i, n), a = n.width > 0 ? 1 : -1, r = n.height > 0 ? 1 : -1; + return {x: n.x + a * o / 2, y: n.y + r * o / 2, width: n.width - a * o, height: n.height - r * o} + }, polar: function (t, e, i) { + var n = t.getItemLayout(e); + return {cx: n.cx, cy: n.cy, r0: n.r0, r: n.r, startAngle: n.startAngle, endAngle: n.endAngle} + } + }, XD = In.extend({ + type: "largeBar", shape: {points: []}, buildPath: function (t, e) { + for (var i = e.points, n = this.__startPoint, o = this.__valueIdx, a = 0; a < i.length; a += 2) n[this.__valueIdx] = i[a + o], t.moveTo(n[0], n[1]), t.lineTo(i[a], i[a + 1]) + } + }); + fs(v(cl, "bar")), fs(xT), ps({ + seriesType: "bar", reset: function (t) { + t.getData().setVisual("legendSymbol", "roundRect") + } + }); + var jD = function (t, e, i) { + e = y(e) && {coordDimensions: e} || a({}, e); + var n = t.getSource(), o = hT(n, e), r = new sT(o, t); + return r.initData(n, i), r + }, YD = { + updateSelectedMap: function (t) { + this._targetList = y(t) ? t.slice() : [], this._selectTargetMap = p(t || [], function (t, e) { + return t.set(e.name, e), t + }, R()) + }, select: function (t, e) { + var i = null != e ? this._targetList[e] : this._selectTargetMap.get(t); + "single" === this.get("selectedMode") && this._selectTargetMap.each(function (t) { + t.selected = !1 + }), i && (i.selected = !0) + }, unSelect: function (t, e) { + var i = null != e ? this._targetList[e] : this._selectTargetMap.get(t); + i && (i.selected = !1) + }, toggleSelected: function (t, e) { + var i = null != e ? this._targetList[e] : this._selectTargetMap.get(t); + if (null != i) return this[i.selected ? "unSelect" : "select"](t, e), i.selected + }, isSelected: function (t, e) { + var i = null != e ? this._targetList[e] : this._selectTargetMap.get(t); + return i && i.selected + } + }, qD = xs({ + type: "series.pie", + init: function (t) { + qD.superApply(this, "init", arguments), this.legendDataProvider = function () { + return this.getRawData() + }, this.updateSelectedMap(this._createSelectableList()), this._defaultLabelLine(t) + }, + mergeOption: function (t) { + qD.superCall(this, "mergeOption", t), this.updateSelectedMap(this._createSelectableList()) + }, + getInitialData: function (t, e) { + return jD(this, ["value"]) + }, + _createSelectableList: function () { + for (var t = this.getRawData(), e = t.mapDimension("value"), i = [], n = 0, o = t.count(); n < o; n++) i.push({ + name: t.getName(n), + value: t.get(e, n), + selected: ir(t, n, "selected") + }); + return i + }, + getDataParams: function (t) { + var e = this.getData(), i = qD.superCall(this, "getDataParams", t), n = []; + return e.each(e.mapDimension("value"), function (t) { + n.push(t) + }), i.percent = No(n, t, e.hostModel.get("percentPrecision")), i.$vars.push("percent"), i + }, + _defaultLabelLine: function (t) { + Mi(t, "labelLine", ["show"]); + var e = t.labelLine, i = t.emphasis.labelLine; + e.show = e.show && t.label.show, i.show = i.show && t.emphasis.label.show + }, + defaultOption: { + zlevel: 0, + z: 2, + legendHoverLink: !0, + hoverAnimation: !0, + center: ["50%", "50%"], + radius: [0, "75%"], + clockwise: !0, + startAngle: 90, + minAngle: 0, + selectedOffset: 10, + hoverOffset: 10, + avoidLabelOverlap: !0, + percentPrecision: 2, + stillShowZeroSum: !0, + label: {rotate: !1, show: !0, position: "outer"}, + labelLine: {show: !0, length: 15, length2: 15, smooth: !1, lineStyle: {width: 1, type: "solid"}}, + itemStyle: {borderWidth: 1}, + animationType: "expansion", + animationEasing: "cubicOut" + } + }); + h(qD, YD); + var KD = dh.prototype; + KD.updateData = function (t, e, i) { + function n() { + s.stopAnimation(!0), s.animateTo({shape: {r: h.r + l.get("hoverOffset")}}, 300, "elasticOut") + } + + function o() { + s.stopAnimation(!0), s.animateTo({shape: {r: h.r}}, 300, "elasticOut") + } + + var s = this.childAt(0), l = t.hostModel, u = t.getItemModel(e), h = t.getItemLayout(e), c = a({}, h); + c.label = null, i ? (s.setShape(c), "scale" === l.getShallow("animationType") ? (s.shape.r = h.r0, po(s, {shape: {r: h.r}}, l, e)) : (s.shape.endAngle = h.startAngle, fo(s, {shape: {endAngle: h.endAngle}}, l, e))) : fo(s, {shape: c}, l, e); + var d = t.getItemVisual(e, "color"); + s.useStyle(r({ + lineJoin: "bevel", + fill: d + }, u.getModel("itemStyle").getItemStyle())), s.hoverStyle = u.getModel("emphasis.itemStyle").getItemStyle(); + var f = u.getShallow("cursor"); + f && s.attr("cursor", f), ch(this, t.getItemLayout(e), l.isSelected(null, e), l.get("selectedOffset"), l.get("animation")), s.off("mouseover").off("mouseout").off("emphasis").off("normal"), u.get("hoverAnimation") && l.isAnimationEnabled() && s.on("mouseover", n).on("mouseout", o).on("emphasis", n).on("normal", o), this._updateLabel(t, e), eo(this) + }, KD._updateLabel = function (t, e) { + var i = this.childAt(1), n = this.childAt(2), o = t.hostModel, a = t.getItemModel(e), + r = t.getItemLayout(e).label, s = t.getItemVisual(e, "color"); + fo(i, {shape: {points: r.linePoints || [[r.x, r.y], [r.x, r.y], [r.x, r.y]]}}, o, e), fo(n, { + style: { + x: r.x, + y: r.y + } + }, o, e), n.attr({rotation: r.rotation, origin: [r.x, r.y], z2: 10}); + var l = a.getModel("label"), u = a.getModel("emphasis.label"), h = a.getModel("labelLine"), + c = a.getModel("emphasis.labelLine"), s = t.getItemVisual(e, "color"); + io(n.style, n.hoverStyle = {}, l, u, { + labelFetcher: t.hostModel, + labelDataIndex: e, + defaultText: t.getName(e), + autoColor: s, + useInsideStyle: !!r.inside + }, { + textAlign: r.textAlign, + textVerticalAlign: r.verticalAlign, + opacity: t.getItemVisual(e, "opacity") + }), n.ignore = n.normalIgnore = !l.get("show"), n.hoverIgnore = !u.get("show"), i.ignore = i.normalIgnore = !h.get("show"), i.hoverIgnore = !c.get("show"), i.setStyle({ + stroke: s, + opacity: t.getItemVisual(e, "opacity") + }), i.setStyle(h.getModel("lineStyle").getLineStyle()), i.hoverStyle = c.getModel("lineStyle").getLineStyle(); + var d = h.get("smooth"); + d && !0 === d && (d = .4), i.setShape({smooth: d}) + }, u(dh, sw); + gr.extend({ + type: "pie", init: function () { + var t = new sw; + this._sectorGroup = t + }, render: function (t, e, i, n) { + if (!n || n.from !== this.uid) { + var o = t.getData(), a = this._data, r = this.group, s = e.get("animation"), l = !a, + u = t.get("animationType"), h = v(hh, this.uid, t, s, i), c = t.get("selectedMode"); + if (o.diff(a).add(function (t) { + var e = new dh(o, t); + l && "scale" !== u && e.eachChild(function (t) { + t.stopAnimation(!0) + }), c && e.on("click", h), o.setItemGraphicEl(t, e), r.add(e) + }).update(function (t, e) { + var i = a.getItemGraphicEl(e); + i.updateData(o, t), i.off("click"), c && i.on("click", h), r.add(i), o.setItemGraphicEl(t, i) + }).remove(function (t) { + var e = a.getItemGraphicEl(t); + r.remove(e) + }).execute(), s && l && o.count() > 0 && "scale" !== u) { + var d = o.getItemLayout(0), f = Math.max(i.getWidth(), i.getHeight()) / 2, + p = m(r.removeClipPath, r); + r.setClipPath(this._createClipPath(d.cx, d.cy, f, d.startAngle, d.clockwise, p, t)) + } + this._data = o + } + }, dispose: function () { + }, _createClipPath: function (t, e, i, n, o, a, r) { + var s = new vS({shape: {cx: t, cy: e, r0: 0, r: i, startAngle: n, endAngle: n, clockwise: o}}); + return po(s, {shape: {endAngle: n + (o ? 1 : -1) * Math.PI * 2}}, r, a), s + }, containPoint: function (t, e) { + var i = e.getData().getItemLayout(0); + if (i) { + var n = t[0] - i.cx, o = t[1] - i.cy, a = Math.sqrt(n * n + o * o); + return a <= i.r && a >= i.r0 + } + } + }); + var $D = function (t, e) { + d(e, function (e) { + e.update = "updateView", cs(e, function (i, n) { + var o = {}; + return n.eachComponent({mainType: "series", subType: t, query: i}, function (t) { + t[e.method] && t[e.method](i.name, i.dataIndex); + var n = t.getData(); + n.each(function (e) { + var i = n.getName(e); + o[i] = t.isSelected(i) || !1 + }) + }), {name: i.name, selected: o} + }) + }) + }, JD = function (t) { + return { + getTargetSeries: function (e) { + var i = {}, n = R(); + return e.eachSeriesByType(t, function (t) { + t.__paletteScope = i, n.set(t.uid, t) + }), n + }, reset: function (t, e) { + var i = t.getRawData(), n = {}, o = t.getData(); + o.each(function (t) { + var e = o.getRawIndex(t); + n[e] = t + }), i.each(function (e) { + var a = n[e], r = null != a && o.getItemVisual(a, "color", !0); + if (r) i.setItemVisual(e, "color", r); else { + var s = i.getItemModel(e).get("itemStyle.color") || t.getColorFromPalette(i.getName(e) || e + "", t.__paletteScope, i.count()); + i.setItemVisual(e, "color", s), null != a && o.setItemVisual(a, "color", s) + } + }) + } + } + }, QD = function (t, e, i, n) { + var o, a, r = t.getData(), s = [], l = !1; + r.each(function (i) { + var n, u, h, c, d = r.getItemLayout(i), f = r.getItemModel(i), p = f.getModel("label"), + g = p.get("position") || f.get("emphasis.label.position"), m = f.getModel("labelLine"), + v = m.get("length"), y = m.get("length2"), x = (d.startAngle + d.endAngle) / 2, _ = Math.cos(x), + w = Math.sin(x); + o = d.cx, a = d.cy; + var b = "inside" === g || "inner" === g; + if ("center" === g) n = d.cx, u = d.cy, c = "center"; else { + var S = (b ? (d.r + d.r0) / 2 * _ : d.r * _) + o, M = (b ? (d.r + d.r0) / 2 * w : d.r * w) + a; + if (n = S + 3 * _, u = M + 3 * w, !b) { + var I = S + _ * (v + e - d.r), T = M + w * (v + e - d.r), D = I + (_ < 0 ? -1 : 1) * y, A = T; + n = D + (_ < 0 ? -5 : 5), u = A, h = [[S, M], [I, T], [D, A]] + } + c = b ? "center" : _ > 0 ? "left" : "right" + } + var C = p.getFont(), L = p.get("rotate") ? _ < 0 ? -x + Math.PI : -x : 0, + k = me(t.getFormattedLabel(i, "normal") || r.getName(i), C, c, "top"); + l = !!L, d.label = { + x: n, + y: u, + position: g, + height: k.height, + len: v, + len2: y, + linePoints: h, + textAlign: c, + verticalAlign: "middle", + rotation: L, + inside: b + }, b || s.push(d.label) + }), !l && t.get("avoidLabelOverlap") && ph(s, o, a, e, i, n) + }, tA = 2 * Math.PI, eA = Math.PI / 180, iA = function (t) { + return { + seriesType: t, reset: function (t, e) { + var i = e.findComponents({mainType: "legend"}); + if (i && i.length) { + var n = t.getData(); + n.filterSelf(function (t) { + for (var e = n.getName(t), o = 0; o < i.length; o++) if (!i[o].isSelected(e)) return !1; + return !0 + }) + } + } + } + }; + $D("pie", [{type: "pieToggleSelect", event: "pieselectchanged", method: "toggleSelected"}, { + type: "pieSelect", + event: "pieselected", + method: "select" + }, {type: "pieUnSelect", event: "pieunselected", method: "unSelect"}]), ps(JD("pie")), fs(v(function (t, e, i, n) { + e.eachSeriesByType(t, function (t) { + var e = t.getData(), n = e.mapDimension("value"), o = t.get("center"), a = t.get("radius"); + y(a) || (a = [0, a]), y(o) || (o = [o, o]); + var r = i.getWidth(), s = i.getHeight(), l = Math.min(r, s), u = Do(o[0], r), h = Do(o[1], s), + c = Do(a[0], l / 2), d = Do(a[1], l / 2), f = -t.get("startAngle") * eA, p = t.get("minAngle") * eA, + g = 0; + e.each(n, function (t) { + !isNaN(t) && g++ + }); + var m = e.getSum(n), v = Math.PI / (m || g) * 2, x = t.get("clockwise"), _ = t.get("roseType"), + w = t.get("stillShowZeroSum"), b = e.getDataExtent(n); + b[0] = 0; + var S = tA, M = 0, I = f, T = x ? 1 : -1; + if (e.each(n, function (t, i) { + var n; + if (isNaN(t)) e.setItemLayout(i, { + angle: NaN, + startAngle: NaN, + endAngle: NaN, + clockwise: x, + cx: u, + cy: h, + r0: c, + r: _ ? NaN : d + }); else { + (n = "area" !== _ ? 0 === m && w ? v : t * v : tA / g) < p ? (n = p, S -= p) : M += t; + var o = I + T * n; + e.setItemLayout(i, { + angle: n, + startAngle: I, + endAngle: o, + clockwise: x, + cx: u, + cy: h, + r0: c, + r: _ ? To(t, b, [c, d]) : d + }), I = o + } + }), S < tA && g) if (S <= .001) { + var D = tA / g; + e.each(n, function (t, i) { + if (!isNaN(t)) { + var n = e.getItemLayout(i); + n.angle = D, n.startAngle = f + T * i * D, n.endAngle = f + T * (i + 1) * D + } + }) + } else v = S / M, I = f, e.each(n, function (t, i) { + if (!isNaN(t)) { + var n = e.getItemLayout(i), o = n.angle === p ? p : t * v; + n.startAngle = I, n.endAngle = I + T * o, I += T * o + } + }); + QD(t, d, r, s) + }) + }, "pie")), hs(iA("pie")), KM.extend({ + type: "series.scatter", + dependencies: ["grid", "polar", "geo", "singleAxis", "calendar"], + getInitialData: function (t, e) { + return Xs(this.getSource(), this) + }, + brushSelector: "point", + getProgressive: function () { + var t = this.option.progressive; + return null == t ? this.option.large ? 5e3 : this.get("progressive") : t + }, + getProgressiveThreshold: function () { + var t = this.option.progressiveThreshold; + return null == t ? this.option.large ? 1e4 : this.get("progressiveThreshold") : t + }, + defaultOption: { + coordinateSystem: "cartesian2d", + zlevel: 0, + z: 2, + legendHoverLink: !0, + hoverAnimation: !0, + symbolSize: 10, + large: !1, + largeThreshold: 2e3, + itemStyle: {opacity: .8} + } + }); + var nA = En({ + shape: {points: null}, symbolProxy: null, buildPath: function (t, e) { + var i = e.points, n = e.size, o = this.symbolProxy, a = o.shape; + if (!((t.getContext ? t.getContext() : t) && n[0] < 4)) for (var r = 0; r < i.length;) { + var s = i[r++], l = i[r++]; + isNaN(s) || isNaN(l) || (a.x = s - n[0] / 2, a.y = l - n[1] / 2, a.width = n[0], a.height = n[1], o.buildPath(t, a, !0)) + } + }, afterBrush: function (t) { + var e = this.shape, i = e.points, n = e.size; + if (n[0] < 4) { + this.setTransform(t); + for (var o = 0; o < i.length;) { + var a = i[o++], r = i[o++]; + isNaN(a) || isNaN(r) || t.fillRect(a - n[0] / 2, r - n[1] / 2, n[0], n[1]) + } + this.restoreTransform(t) + } + }, findDataIndex: function (t, e) { + for (var i = this.shape, n = i.points, o = i.size, a = Math.max(o[0], 4), r = Math.max(o[1], 4), s = n.length / 2 - 1; s >= 0; s--) { + var l = 2 * s, u = n[l] - a / 2, h = n[l + 1] - r / 2; + if (t >= u && e >= h && t <= u + a && e <= h + r) return s + } + return -1 + } + }), oA = gh.prototype; + oA.isPersistent = function () { + return !this._incremental + }, oA.updateData = function (t) { + this.group.removeAll(); + var e = new nA({rectHover: !0, cursor: "default"}); + e.setShape({points: t.getLayout("symbolPoints")}), this._setCommon(e, t), this.group.add(e), this._incremental = null + }, oA.updateLayout = function (t) { + if (!this._incremental) { + var e = t.getLayout("symbolPoints"); + this.group.eachChild(function (t) { + if (null != t.startIndex) { + var i = 2 * (t.endIndex - t.startIndex), n = 4 * t.startIndex * 2; + e = new Float32Array(e.buffer, n, i) + } + t.setShape("points", e) + }) + } + }, oA.incrementalPrepareUpdate = function (t) { + this.group.removeAll(), this._clearIncremental(), t.count() > 2e6 ? (this._incremental || (this._incremental = new On({silent: !0})), this.group.add(this._incremental)) : this._incremental = null + }, oA.incrementalUpdate = function (t, e) { + var i; + this._incremental ? (i = new nA, this._incremental.addDisplayable(i, !0)) : ((i = new nA({ + rectHover: !0, + cursor: "default", + startIndex: t.start, + endIndex: t.end + })).incremental = !0, this.group.add(i)), i.setShape({points: e.getLayout("symbolPoints")}), this._setCommon(i, e, !!this._incremental) + }, oA._setCommon = function (t, e, i) { + var n = e.hostModel, o = e.getVisual("symbolSize"); + t.setShape("size", o instanceof Array ? o : [o, o]), t.symbolProxy = Tl(e.getVisual("symbol"), 0, 0, 0, 0), t.setColor = t.symbolProxy.setColor; + var a = t.shape.size[0] < 4; + t.useStyle(n.getModel("itemStyle").getItemStyle(a ? ["color", "shadowBlur", "shadowColor"] : ["color"])); + var r = e.getVisual("color"); + r && t.setColor(r), i || (t.seriesIndex = n.seriesIndex, t.on("mousemove", function (e) { + t.dataIndex = null; + var i = t.findDataIndex(e.offsetX, e.offsetY); + i >= 0 && (t.dataIndex = i + (t.startIndex || 0)) + })) + }, oA.remove = function () { + this._clearIncremental(), this._incremental = null, this.group.removeAll() + }, oA._clearIncremental = function () { + var t = this._incremental; + t && t.clearDisplaybles() + }, _s({ + type: "scatter", render: function (t, e, i) { + var n = t.getData(); + this._updateSymbolDraw(n, t).updateData(n), this._finished = !0 + }, incrementalPrepareRender: function (t, e, i) { + var n = t.getData(); + this._updateSymbolDraw(n, t).incrementalPrepareUpdate(n), this._finished = !1 + }, incrementalRender: function (t, e, i) { + this._symbolDraw.incrementalUpdate(t, e.getData()), this._finished = t.end === e.getData().count() + }, updateTransform: function (t, e, i) { + var n = t.getData(); + if (this.group.dirty(), !this._finished || n.count() > 1e4 || !this._symbolDraw.isPersistent()) return {update: !0}; + var o = mD().reset(t); + o.progress && o.progress({start: 0, end: n.count()}, n), this._symbolDraw.updateLayout(n) + }, _updateSymbolDraw: function (t, e) { + var i = this._symbolDraw, n = e.pipelineContext.large; + return i && n === this._isLargeDraw || (i && i.remove(), i = this._symbolDraw = n ? new gh : new Ql, this._isLargeDraw = n, this.group.removeAll()), this.group.add(i.group), i + }, remove: function (t, e) { + this._symbolDraw && this._symbolDraw.remove(!0), this._symbolDraw = null + }, dispose: function () { + } + }), ps(gD("scatter", "circle")), fs(mD("scatter")), u(mh, qT), vh.prototype.getIndicatorAxes = function () { + return this._indicatorAxes + }, vh.prototype.dataToPoint = function (t, e) { + var i = this._indicatorAxes[e]; + return this.coordToPoint(i.dataToCoord(t), e) + }, vh.prototype.coordToPoint = function (t, e) { + var i = this._indicatorAxes[e].angle; + return [this.cx + t * Math.cos(i), this.cy - t * Math.sin(i)] + }, vh.prototype.pointToData = function (t) { + var e = t[0] - this.cx, i = t[1] - this.cy, n = Math.sqrt(e * e + i * i); + e /= n, i /= n; + for (var o, a = Math.atan2(-i, e), r = 1 / 0, s = -1, l = 0; l < this._indicatorAxes.length; l++) { + var u = this._indicatorAxes[l], h = Math.abs(a - u.angle); + h < r && (o = u, s = l, r = h) + } + return [s, +(o && o.coodToData(n))] + }, vh.prototype.resize = function (t, e) { + var i = t.get("center"), n = e.getWidth(), o = e.getHeight(), a = Math.min(n, o) / 2; + this.cx = Do(i[0], n), this.cy = Do(i[1], o), this.startAngle = t.get("startAngle") * Math.PI / 180, this.r = Do(t.get("radius"), a), d(this._indicatorAxes, function (t, e) { + t.setExtent(0, this.r); + var i = this.startAngle + e * Math.PI * 2 / this._indicatorAxes.length; + i = Math.atan2(Math.sin(i), Math.cos(i)), t.angle = i + }, this) + }, vh.prototype.update = function (t, e) { + function i(t) { + var e = Math.pow(10, Math.floor(Math.log(t) / Math.LN10)), i = t / e; + return 2 === i ? i = 5 : i *= 2, i * e + } + + var n = this._indicatorAxes, o = this._model; + d(n, function (t) { + t.scale.setExtent(1 / 0, -1 / 0) + }), t.eachSeriesByType("radar", function (e, i) { + if ("radar" === e.get("coordinateSystem") && t.getComponent("radar", e.get("radarIndex")) === o) { + var a = e.getData(); + d(n, function (t) { + t.scale.unionExtentFromData(a, a.mapDimension(t.dim)) + }) + } + }, this); + var a = o.get("splitNumber"); + d(n, function (t, e) { + var n = ml(t.scale, t.model); + yl(t.scale, t.model); + var o = t.model, r = t.scale, s = o.getMin(), l = o.getMax(), u = r.getInterval(); + if (null != s && null != l) r.setExtent(+s, +l), r.setInterval((l - s) / a); else if (null != s) { + var h; + do { + h = s + u * a, r.setExtent(+s, h), r.setInterval(u), u = i(u) + } while (h < n[1] && isFinite(h) && isFinite(n[1])) + } else if (null != l) { + var c; + do { + c = l - u * a, r.setExtent(c, +l), r.setInterval(u), u = i(u) + } while (c > n[0] && isFinite(c) && isFinite(n[0])) + } else { + r.getTicks().length - 1 > a && (u = i(u)); + var d = Math.round((n[0] + n[1]) / 2 / u) * u, f = Math.round(a / 2); + r.setExtent(Ao(d - f * u), Ao(d + (a - f) * u)), r.setInterval(u) + } + }) + }, vh.dimensions = [], vh.create = function (t, e) { + var i = []; + return t.eachComponent("radar", function (n) { + var o = new vh(n, t, e); + i.push(o), n.coordinateSystem = o + }), t.eachSeriesByType("radar", function (t) { + "radar" === t.get("coordinateSystem") && (t.coordinateSystem = i[t.get("radarIndex") || 0]) + }), i + }, Ca.register("radar", vh); + var aA = bD.valueAxis, rA = (vs({ + type: "radar", + optionUpdated: function () { + var t = this.get("boundaryGap"), e = this.get("splitNumber"), o = this.get("scale"), + s = this.get("axisLine"), l = this.get("axisTick"), u = this.get("axisLabel"), h = this.get("name"), + c = this.get("name.show"), d = this.get("name.formatter"), p = this.get("nameGap"), + g = this.get("triggerEvent"), m = f(this.get("indicator") || [], function (f) { + null != f.max && f.max > 0 && !f.min ? f.min = 0 : null != f.min && f.min < 0 && !f.max && (f.max = 0); + var m = h; + if (null != f.color && (m = r({color: f.color}, h)), f = n(i(f), { + boundaryGap: t, + splitNumber: e, + scale: o, + axisLine: s, + axisTick: l, + axisLabel: u, + name: f.text, + nameLocation: "end", + nameGap: p, + nameTextStyle: m, + triggerEvent: g + }, !1), c || (f.name = ""), "string" == typeof d) { + var v = f.name; + f.name = d.replace("{value}", null != v ? v : "") + } else "function" == typeof d && (f.name = d(f.name, f)); + var y = a(new wo(f, null, this.ecModel), ET); + return y.mainType = "radar", y.componentIndex = this.componentIndex, y + }, this); + this.getIndicatorModels = function () { + return m + } + }, + defaultOption: { + zlevel: 0, + z: 0, + center: ["50%", "50%"], + radius: "75%", + startAngle: 90, + name: {show: !0}, + boundaryGap: [0, 0], + splitNumber: 5, + nameGap: 15, + scale: !1, + shape: "polygon", + axisLine: n({lineStyle: {color: "#bbb"}}, aA.axisLine), + axisLabel: yh(aA.axisLabel, !1), + axisTick: yh(aA.axisTick, !1), + splitLine: yh(aA.splitLine, !0), + splitArea: yh(aA.splitArea, !0), + indicator: [] + } + }), ["axisLine", "axisTickLabel", "axisName"]); + ys({ + type: "radar", render: function (t, e, i) { + this.group.removeAll(), this._buildAxes(t), this._buildSplitLineAndArea(t) + }, _buildAxes: function (t) { + var e = t.coordinateSystem; + d(f(e.getIndicatorAxes(), function (t) { + return new LD(t.model, { + position: [e.cx, e.cy], + rotation: t.angle, + labelDirection: -1, + tickDirection: -1, + nameDirection: 1 + }) + }), function (t) { + d(rA, t.add, t), this.group.add(t.getGroup()) + }, this) + }, _buildSplitLineAndArea: function (t) { + function e(t, e, i) { + var n = i % e.length; + return t[n] = t[n] || [], n + } + + var i = t.coordinateSystem, n = i.getIndicatorAxes(); + if (n.length) { + var o = t.get("shape"), a = t.getModel("splitLine"), s = t.getModel("splitArea"), + l = a.getModel("lineStyle"), u = s.getModel("areaStyle"), h = a.get("show"), c = s.get("show"), + p = l.get("color"), g = u.get("color"); + p = y(p) ? p : [p], g = y(g) ? g : [g]; + var m = [], v = []; + if ("circle" === o) for (var x = n[0].getTicksCoords(), _ = i.cx, w = i.cy, b = 0; b < x.length; b++) h && m[A = e(m, p, b)].push(new pS({ + shape: { + cx: _, + cy: w, + r: x[b].coord + } + })), c && b < x.length - 1 && v[A = e(v, g, b)].push(new yS({ + shape: { + cx: _, + cy: w, + r0: x[b].coord, + r: x[b + 1].coord + } + })); else for (var S, M = f(n, function (t, e) { + var n = t.getTicksCoords(); + return S = null == S ? n.length - 1 : Math.min(n.length - 1, S), f(n, function (t) { + return i.coordToPoint(t.coord, e) + }) + }), I = [], b = 0; b <= S; b++) { + for (var T = [], D = 0; D < n.length; D++) T.push(M[D][b]); + if (T[0] && T.push(T[0].slice()), h && m[A = e(m, p, b)].push(new bS({shape: {points: T}})), c && I) { + var A = e(v, g, b - 1); + v[A].push(new wS({shape: {points: T.concat(I)}})) + } + I = T.slice().reverse() + } + var C = l.getLineStyle(), L = u.getAreaStyle(); + d(v, function (t, e) { + this.group.add(zS(t, {style: r({stroke: "none", fill: g[e % g.length]}, L), silent: !0})) + }, this), d(m, function (t, e) { + this.group.add(zS(t, {style: r({fill: "none", stroke: p[e % p.length]}, C), silent: !0})) + }, this) + } + } + }); + var sA = KM.extend({ + type: "series.radar", + dependencies: ["radar"], + init: function (t) { + sA.superApply(this, "init", arguments), this.legendDataProvider = function () { + return this.getRawData() + } + }, + getInitialData: function (t, e) { + return jD(this, {generateCoord: "indicator_", generateCoordCount: 1 / 0}) + }, + formatTooltip: function (t) { + var e = this.getData(), i = this.coordinateSystem.getIndicatorAxes(), n = this.getData().getName(t); + return Zo("" === n ? this.name : n) + "
" + f(i, function (i, n) { + var o = e.get(e.mapDimension(i.dim), t); + return Zo(i.name + " : " + o) + }).join("
") + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "radar", + legendHoverLink: !0, + radarIndex: 0, + lineStyle: {width: 2, type: "solid"}, + label: {position: "top"}, + symbol: "emptyCircle", + symbolSize: 4 + } + }); + _s({ + type: "radar", render: function (t, e, n) { + function o(t, e) { + var i = t.getItemVisual(e, "symbol") || "circle", n = t.getItemVisual(e, "color"); + if ("none" !== i) { + var o = xh(t.getItemVisual(e, "symbolSize")), a = Tl(i, -1, -1, 2, 2, n); + return a.attr({style: {strokeNoScale: !0}, z2: 100, scale: [o[0] / 2, o[1] / 2]}), a + } + } + + function a(e, i, n, a, r, s) { + n.removeAll(); + for (var l = 0; l < i.length - 1; l++) { + var u = o(a, r); + u && (u.__dimIdx = l, e[l] ? (u.attr("position", e[l]), BS[s ? "initProps" : "updateProps"](u, {position: i[l]}, t, r)) : u.attr("position", i[l]), n.add(u)) + } + } + + function s(t) { + return f(t, function (t) { + return [l.cx, l.cy] + }) + } + + var l = t.coordinateSystem, u = this.group, h = t.getData(), c = this._data; + h.diff(c).add(function (e) { + var i = h.getItemLayout(e); + if (i) { + var n = new wS, o = new bS, r = {shape: {points: i}}; + n.shape.points = s(i), o.shape.points = s(i), po(n, r, t, e), po(o, r, t, e); + var l = new sw, u = new sw; + l.add(o), l.add(n), l.add(u), a(o.shape.points, i, u, h, e, !0), h.setItemGraphicEl(e, l) + } + }).update(function (e, i) { + var n = c.getItemGraphicEl(i), o = n.childAt(0), r = n.childAt(1), s = n.childAt(2), + l = {shape: {points: h.getItemLayout(e)}}; + l.shape.points && (a(o.shape.points, l.shape.points, s, h, e, !1), fo(o, l, t), fo(r, l, t), h.setItemGraphicEl(e, n)) + }).remove(function (t) { + u.remove(c.getItemGraphicEl(t)) + }).execute(), h.eachItemGraphicEl(function (t, e) { + function n() { + l.attr("ignore", m) + } + + function o() { + l.attr("ignore", g) + } + + var a = h.getItemModel(e), s = t.childAt(0), l = t.childAt(1), c = t.childAt(2), + d = h.getItemVisual(e, "color"); + u.add(t), s.useStyle(r(a.getModel("lineStyle").getLineStyle(), { + fill: "none", + stroke: d + })), s.hoverStyle = a.getModel("emphasis.lineStyle").getLineStyle(); + var f = a.getModel("areaStyle"), p = a.getModel("emphasis.areaStyle"), + g = f.isEmpty() && f.parentModel.isEmpty(), m = p.isEmpty() && p.parentModel.isEmpty(); + m = m && g, l.ignore = g, l.useStyle(r(f.getAreaStyle(), { + fill: d, + opacity: .7 + })), l.hoverStyle = p.getAreaStyle(); + var v = a.getModel("itemStyle").getItemStyle(["color"]), + y = a.getModel("emphasis.itemStyle").getItemStyle(), x = a.getModel("label"), + _ = a.getModel("emphasis.label"); + c.eachChild(function (t) { + t.setStyle(v), t.hoverStyle = i(y), io(t.style, t.hoverStyle, x, _, { + labelFetcher: h.hostModel, + labelDataIndex: e, + labelDimIndex: t.__dimIdx, + defaultText: h.get(h.dimensions[t.__dimIdx], e), + autoColor: d, + isRectText: !0 + }) + }), t.off("mouseover").off("mouseout").off("normal").off("emphasis"), t.on("emphasis", n).on("mouseover", n).on("normal", o).on("mouseout", o), eo(t) + }), this._data = h + }, remove: function () { + this.group.removeAll(), this._data = null + }, dispose: function () { + } + }); + ps(JD("radar")), ps(gD("radar", "circle")), fs(function (t) { + t.eachSeriesByType("radar", function (t) { + var e = t.getData(), i = [], n = t.coordinateSystem; + if (n) { + for (var o = n.getIndicatorAxes(), a = 0; a < o.length; a++) e.each(e.mapDimension(o[a].dim), function (t, e) { + i[e] = i[e] || [], i[e][a] = n.dataToPoint(t, a) + }); + e.each(function (t) { + i[t][0] && i[t].push(i[t][0].slice()), e.setItemLayout(t, i[t]) + }) + } + }) + }), hs(iA("radar")), us(function (t) { + var e = t.polar; + if (e) { + y(e) || (e = [e]); + var i = []; + d(e, function (e, n) { + e.indicator ? (e.type && !e.shape && (e.shape = e.type), t.radar = t.radar || [], y(t.radar) || (t.radar = [t.radar]), t.radar.push(e)) : i.push(e) + }), t.polar = i + } + d(t.series, function (t) { + t && "radar" === t.type && t.polarIndex && (t.radarIndex = t.polarIndex) + }) + }); + var lA = Q; + h(_h, E_), wh.prototype = { + constructor: wh, type: "view", dimensions: ["x", "y"], setBoundingRect: function (t, e, i, n) { + return this._rect = new $t(t, e, i, n), this._rect + }, getBoundingRect: function () { + return this._rect + }, setViewRect: function (t, e, i, n) { + this.transformTo(t, e, i, n), this._viewRect = new $t(t, e, i, n) + }, transformTo: function (t, e, i, n) { + var o = this.getBoundingRect(), a = this._rawTransformable; + a.transform = o.calculateTransform(new $t(t, e, i, n)), a.decomposeTransform(), this._updateTransform() + }, setCenter: function (t) { + t && (this._center = t, this._updateCenterAndZoom()) + }, setZoom: function (t) { + t = t || 1; + var e = this.zoomLimit; + e && (null != e.max && (t = Math.min(e.max, t)), null != e.min && (t = Math.max(e.min, t))), this._zoom = t, this._updateCenterAndZoom() + }, getDefaultCenter: function () { + var t = this.getBoundingRect(); + return [t.x + t.width / 2, t.y + t.height / 2] + }, getCenter: function () { + return this._center || this.getDefaultCenter() + }, getZoom: function () { + return this._zoom || 1 + }, getRoamTransform: function () { + return this._roamTransformable.getLocalTransform() + }, _updateCenterAndZoom: function () { + var t = this._rawTransformable.getLocalTransform(), e = this._roamTransformable, + i = this.getDefaultCenter(), n = this.getCenter(), o = this.getZoom(); + n = Q([], n, t), i = Q([], i, t), e.origin = n, e.position = [i[0] - n[0], i[1] - n[1]], e.scale = [o, o], this._updateTransform() + }, _updateTransform: function () { + var t = this._roamTransformable, e = this._rawTransformable; + e.parent = t, t.updateTransform(), e.updateTransform(), ut(this.transform || (this.transform = []), e.transform || st()), this._rawTransform = e.getLocalTransform(), this.invTransform = this.invTransform || [], pt(this.invTransform, this.transform), this.decomposeTransform() + }, getViewRect: function () { + return this._viewRect + }, getViewRectAfterRoam: function () { + var t = this.getBoundingRect().clone(); + return t.applyTransform(this.transform), t + }, dataToPoint: function (t, e, i) { + var n = e ? this._rawTransform : this.transform; + return i = i || [], n ? lA(i, t, n) : G(i, t) + }, pointToData: function (t) { + var e = this.invTransform; + return e ? lA([], t, e) : [t[0], t[1]] + }, convertToPixel: v(bh, "dataToPoint"), convertFromPixel: v(bh, "pointToData"), containPoint: function (t) { + return this.getViewRectAfterRoam().contain(t[0], t[1]) + } + }, h(wh, E_); + for (var uA = [126, 25], hA = [[[0, 3.5], [7, 11.2], [15, 11.9], [30, 7], [42, .7], [52, .7], [56, 7.7], [59, .7], [64, .7], [64, 0], [5, 0], [0, 3.5]], [[13, 16.1], [19, 14.7], [16, 21.7], [11, 23.1], [13, 16.1]], [[12, 32.2], [14, 38.5], [15, 38.5], [13, 32.2], [12, 32.2]], [[16, 47.6], [12, 53.2], [13, 53.2], [18, 47.6], [16, 47.6]], [[6, 64.4], [8, 70], [9, 70], [8, 64.4], [6, 64.4]], [[23, 82.6], [29, 79.8], [30, 79.8], [25, 82.6], [23, 82.6]], [[37, 70.7], [43, 62.3], [44, 62.3], [39, 70.7], [37, 70.7]], [[48, 51.1], [51, 45.5], [53, 45.5], [50, 51.1], [48, 51.1]], [[51, 35], [51, 28.7], [53, 28.7], [53, 35], [51, 35]], [[52, 22.4], [55, 17.5], [56, 17.5], [53, 22.4], [52, 22.4]], [[58, 12.6], [62, 7], [63, 7], [60, 12.6], [58, 12.6]], [[0, 3.5], [0, 93.1], [64, 93.1], [64, 0], [63, 0], [63, 92.4], [1, 92.4], [1, 3.5], [0, 3.5]]], cA = 0; cA < hA.length; cA++) for (var dA = 0; dA < hA[cA].length; dA++) hA[cA][dA][0] /= 10.5, hA[cA][dA][1] /= -14, hA[cA][dA][0] += uA[0], hA[cA][dA][1] += uA[1]; + var fA = {"南海诸岛": [32, 80], "广东": [0, -10], "香港": [10, 5], "澳门": [-10, 10], "天津": [5, 5]}, + pA = {Russia: [100, 60], "United States": [-99, 38], "United States of America": [-99, 38]}, + gA = [[[123.45165252685547, 25.73527164402261], [123.49731445312499, 25.73527164402261], [123.49731445312499, 25.750734064600884], [123.45165252685547, 25.750734064600884], [123.45165252685547, 25.73527164402261]]], + mA = [function (t) { + "china" === t.map && t.regions.push(new Cl("南海诸岛", f(hA, function (t) { + return {type: "polygon", exterior: t} + }), uA)) + }, function (t) { + d(t.regions, function (t) { + var e = fA[t.name]; + if (e) { + var i = t.center; + i[0] += e[0] / 10.5, i[1] += -e[1] / 14 + } + }) + }, function (t) { + d(t.regions, function (t) { + var e = pA[t.name]; + if (e) { + var i = t.center; + i[0] = e[0], i[1] = e[1] + } + }) + }, function (t) { + if ("china" === t.map) for (var e = 0, i = t.regions.length; e < i; ++e) "台湾" === t.regions[e].name && t.regions[e].geometries.push({ + type: "polygon", + exterior: gA[0] + }) + }]; + Sh.prototype = { + constructor: Sh, type: "geo", dimensions: ["lng", "lat"], containCoord: function (t) { + for (var e = this.regions, i = 0; i < e.length; i++) if (e[i].contain(t)) return !0; + return !1 + }, loadGeoJson: function (t, e, i) { + try { + this.regions = t ? XT(t) : [] + } catch (t) { + throw"Invalid geoJson format\n" + t.message + } + e = e || {}, i = i || {}; + for (var n = this.regions, o = R(), a = 0; a < n.length; a++) { + var r = n[a].name; + r = i.hasOwnProperty(r) ? i[r] : r, n[a].name = r, o.set(r, n[a]), this.addGeoCoord(r, n[a].center); + var s = e[r]; + s && n[a].transformTo(s.left, s.top, s.width, s.height) + } + this._regionsMap = o, this._rect = null, d(mA, function (t) { + t(this) + }, this) + }, transformTo: function (t, e, i, n) { + var o = this.getBoundingRect(); + (o = o.clone()).y = -o.y - o.height; + var a = this._rawTransformable; + a.transform = o.calculateTransform(new $t(t, e, i, n)), a.decomposeTransform(); + var r = a.scale; + r[1] = -r[1], a.updateTransform(), this._updateTransform() + }, getRegion: function (t) { + return this._regionsMap.get(t) + }, getRegionByCoord: function (t) { + for (var e = this.regions, i = 0; i < e.length; i++) if (e[i].contain(t)) return e[i] + }, addGeoCoord: function (t, e) { + this._nameCoordMap.set(t, e) + }, getGeoCoord: function (t) { + return this._nameCoordMap.get(t) + }, getBoundingRect: function () { + if (this._rect) return this._rect; + for (var t, e = this.regions, i = 0; i < e.length; i++) { + var n = e[i].getBoundingRect(); + (t = t || n.clone()).union(n) + } + return this._rect = t || new $t(0, 0, 0, 0) + }, dataToPoint: function (t, e, i) { + if ("string" == typeof t && (t = this.getGeoCoord(t)), t) return wh.prototype.dataToPoint.call(this, t, e, i) + }, convertToPixel: v(Mh, "dataToPoint"), convertFromPixel: v(Mh, "pointToData") + }, h(Sh, wh); + var vA = { + dimensions: Sh.prototype.dimensions, create: function (t, e) { + var i = []; + t.eachComponent("geo", function (t, n) { + var o = t.get("map"), a = ws(o), + r = new Sh(o + n, o, a && a.geoJson, a && a.specialAreas, t.get("nameMap")); + r.zoomLimit = t.get("scaleLimit"), i.push(r), Th(r, t), t.coordinateSystem = r, r.model = t, r.resize = Ih, r.resize(t, e) + }), t.eachSeries(function (t) { + if ("geo" === t.get("coordinateSystem")) { + var e = t.get("geoIndex") || 0; + t.coordinateSystem = i[e] + } + }); + var n = {}; + return t.eachSeriesByType("map", function (t) { + if (!t.getHostGeoModel()) { + var e = t.getMapType(); + n[e] = n[e] || [], n[e].push(t) + } + }), d(n, function (t, n) { + var a = ws(n), r = f(t, function (t) { + return t.get("nameMap") + }), s = new Sh(n, n, a && a.geoJson, a && a.specialAreas, o(r)); + s.zoomLimit = T.apply(null, f(t, function (t) { + return t.get("scaleLimit") + })), i.push(s), s.resize = Ih, s.resize(t[0], e), d(t, function (t) { + t.coordinateSystem = s, Th(s, t) + }) + }), i + }, getFilledRegions: function (t, e, i) { + var n = (t || []).slice(); + i = i || {}; + var o = ws(e), a = o && o.geoJson; + if (!a) return t; + for (var r = R(), s = a.features, l = 0; l < n.length; l++) r.set(n[l].name, n[l]); + for (l = 0; l < s.length; l++) { + var u = s[l].properties.name; + r.get(u) || (i.hasOwnProperty(u) && (u = i[u]), n.push({name: u})) + } + return n + } + }; + ds("geo", vA); + var yA = KM.extend({ + type: "series.map", + dependencies: ["geo"], + layoutMode: "box", + needsDrawMap: !1, + seriesGroup: [], + init: function (t) { + yA.superApply(this, "init", arguments), this.updateSelectedMap(this._createSelectableList()) + }, + getInitialData: function (t) { + return jD(this, ["value"]) + }, + mergeOption: function (t) { + yA.superApply(this, "mergeOption", arguments), this.updateSelectedMap(this._createSelectableList()) + }, + _createSelectableList: function () { + for (var t = this.getRawData(), e = t.mapDimension("value"), i = [], n = 0, o = t.count(); n < o; n++) i.push({ + name: t.getName(n), + value: t.get(e, n), + selected: ir(t, n, "selected") + }); + return i = vA.getFilledRegions(i, this.getMapType(), this.option.nameMap) + }, + getHostGeoModel: function () { + var t = this.option.geoIndex; + return null != t ? this.dependentModels.geo[t] : null + }, + getMapType: function () { + return (this.getHostGeoModel() || this).option.map + }, + _fillOption: function (t, e) { + }, + getRawValue: function (t) { + var e = this.getData(); + return e.get(e.mapDimension("value"), t) + }, + getRegionModel: function (t) { + var e = this.getData(); + return e.getItemModel(e.indexOfName(t)) + }, + formatTooltip: function (t) { + for (var e = this.getData(), i = Fo(this.getRawValue(t)), n = e.getName(t), o = this.seriesGroup, a = [], r = 0; r < o.length; r++) { + var s = o[r].originalData.indexOfName(n), l = e.mapDimension("value"); + isNaN(o[r].originalData.get(l, s)) || a.push(Zo(o[r].name)) + } + return a.join(", ") + "
" + Zo(n + " : " + i) + }, + getTooltipPosition: function (t) { + if (null != t) { + var e = this.getData().getName(t), i = this.coordinateSystem, n = i.getRegion(e); + return n && i.dataToPoint(n.center) + } + }, + setZoom: function (t) { + this.option.zoom = t + }, + setCenter: function (t) { + this.option.center = t + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "geo", + map: "", + left: "center", + top: "center", + aspectScale: .75, + showLegendSymbol: !0, + dataRangeHoverLink: !0, + boundingCoords: null, + center: null, + zoom: 1, + scaleLimit: null, + label: {show: !1, color: "#000"}, + itemStyle: {borderWidth: .5, borderColor: "#444", areaColor: "#eee"}, + emphasis: {label: {show: !0, color: "rgb(100,0,0)"}, itemStyle: {areaColor: "rgba(255,215,0,0.8)"}} + } + }); + h(yA, YD); + var xA = "\0_ec_interaction_mutex"; + cs({type: "takeGlobalCursor", event: "globalCursorTaken", update: "update"}, function () { + }), h(kh, D_); + var _A = {axisPointer: 1, tooltip: 1, brush: 1}; + Uh.prototype = { + constructor: Uh, draw: function (t, e, i, n, o) { + var a = "geo" === t.mainType, r = t.getData && t.getData(); + a && e.eachComponent({mainType: "series", subType: "map"}, function (e) { + r || e.getHostGeoModel() !== t || (r = e.getData()) + }); + var s = t.coordinateSystem, l = this.group, u = s.scale, h = {position: s.position, scale: u}; + !l.childAt(0) || o ? l.attr(h) : fo(l, h, t), l.removeAll(); + var c = ["itemStyle"], f = ["emphasis", "itemStyle"], p = ["label"], g = ["emphasis", "label"], m = R(); + d(s.regions, function (e) { + var i = m.get(e.name) || m.set(e.name, new sw), n = new AS({shape: {paths: []}}); + i.add(n); + var o, s = (C = t.getRegionModel(e.name) || t).getModel(c), h = C.getModel(f), v = Fh(s), y = Fh(h), + x = C.getModel(p), _ = C.getModel(g); + if (r) { + o = r.indexOfName(e.name); + var w = r.getItemVisual(o, "color", !0); + w && (v.fill = w) + } + d(e.geometries, function (t) { + if ("polygon" === t.type) { + n.shape.paths.push(new wS({shape: {points: t.exterior}})); + for (var e = 0; e < (t.interiors ? t.interiors.length : 0); e++) n.shape.paths.push(new wS({shape: {points: t.interiors[e]}})) + } + }), n.setStyle(v), n.style.strokeNoScale = !0, n.culling = !0; + var b = x.get("show"), S = _.get("show"), M = r && isNaN(r.get(r.mapDimension("value"), o)), + I = r && r.getItemLayout(o); + if (a || M && (b || S) || I && I.showLabel) { + var T, D = a ? e.name : o; + (!r || o >= 0) && (T = t); + var A = new fS({position: e.center.slice(), scale: [1 / u[0], 1 / u[1]], z2: 10, silent: !0}); + io(A.style, A.hoverStyle = {}, x, _, { + labelFetcher: T, + labelDataIndex: D, + defaultText: e.name, + useInsideStyle: !1 + }, {textAlign: "center", textVerticalAlign: "middle"}), i.add(A) + } + if (r) r.setItemGraphicEl(o, i); else { + var C = t.getRegionModel(e.name); + n.eventData = { + componentType: "geo", + geoIndex: t.componentIndex, + name: e.name, + region: C && C.option || {} + } + } + (i.__regions || (i.__regions = [])).push(e), eo(i, y, {hoverSilentOnTouch: !!t.get("selectedMode")}), l.add(i) + }), this._updateController(t, e, i), Hh(this, t, l, i, n), Zh(t, l) + }, remove: function () { + this.group.removeAll(), this._controller.dispose(), this._controllerHost = {} + }, _updateController: function (t, e, i) { + function n() { + var e = {type: "geoRoam", componentType: l}; + return e[l + "Id"] = t.id, e + } + + var o = t.coordinateSystem, r = this._controller, s = this._controllerHost; + s.zoomLimit = t.get("scaleLimit"), s.zoom = o.getZoom(), r.enable(t.get("roam") || !1); + var l = t.mainType; + r.off("pan").on("pan", function (t, e) { + this._mouseDownFlag = !1, Vh(s, t, e), i.dispatchAction(a(n(), {dx: t, dy: e})) + }, this), r.off("zoom").on("zoom", function (t, e, o) { + if (this._mouseDownFlag = !1, Gh(s, t, e, o), i.dispatchAction(a(n(), { + zoom: t, + originX: e, + originY: o + })), this._updateGroup) { + var r = this.group, l = r.scale; + r.traverse(function (t) { + "text" === t.type && t.attr("scale", [1 / l[0], 1 / l[1]]) + }) + } + }, this), r.setPointerChecker(function (e, n, a) { + return o.getViewRectAfterRoam().contain(n, a) && !Wh(e, i, t) + }) + } + }, _s({ + type: "map", render: function (t, e, i, n) { + if (!n || "mapToggleSelect" !== n.type || n.from !== this.uid) { + var o = this.group; + if (o.removeAll(), !t.getHostGeoModel()) { + if (n && "geoRoam" === n.type && "series" === n.componentType && n.seriesId === t.id) (a = this._mapDraw) && o.add(a.group); else if (t.needsDrawMap) { + var a = this._mapDraw || new Uh(i, !0); + o.add(a.group), a.draw(t, e, i, this, n), this._mapDraw = a + } else this._mapDraw && this._mapDraw.remove(), this._mapDraw = null; + t.get("showLegendSymbol") && e.getComponent("legend") && this._renderSymbols(t, e, i) + } + } + }, remove: function () { + this._mapDraw && this._mapDraw.remove(), this._mapDraw = null, this.group.removeAll() + }, dispose: function () { + this._mapDraw && this._mapDraw.remove(), this._mapDraw = null + }, _renderSymbols: function (t, e, i) { + var n = t.originalData, o = this.group; + n.each(n.mapDimension("value"), function (e, i) { + if (!isNaN(e)) { + var a = n.getItemLayout(i); + if (a && a.point) { + var r = a.point, s = a.offset, l = new pS({ + style: {fill: t.getData().getVisual("color")}, + shape: {cx: r[0] + 9 * s, cy: r[1], r: 3}, + silent: !0, + z2: s ? 8 : 10 + }); + if (!s) { + var u = t.mainSeries.getData(), h = n.getName(i), c = u.indexOfName(h), + d = n.getItemModel(i), f = d.getModel("label"), p = d.getModel("emphasis.label"), + g = u.getItemGraphicEl(c), m = D(t.getFormattedLabel(i, "normal"), h), + v = D(t.getFormattedLabel(i, "emphasis"), m), y = function () { + var t = no({}, p, {text: p.get("show") ? v : null}, { + isRectText: !0, + useInsideStyle: !1 + }, !0); + l.style.extendFrom(t), l.__mapOriginalZ2 = l.z2, l.z2 += 1 + }, x = function () { + no(l.style, f, { + text: f.get("show") ? m : null, + textPosition: f.getShallow("position") || "bottom" + }, { + isRectText: !0, + useInsideStyle: !1 + }), null != l.__mapOriginalZ2 && (l.z2 = l.__mapOriginalZ2, l.__mapOriginalZ2 = null) + }; + g.on("mouseover", y).on("mouseout", x).on("emphasis", y).on("normal", x), x() + } + o.add(l) + } + } + }) + } + }), cs({type: "geoRoam", event: "geoRoam", update: "updateTransform"}, function (t, e) { + var i = t.componentType || "series"; + e.eachComponent({mainType: i, query: t}, function (e) { + var n = e.coordinateSystem; + if ("geo" === n.type) { + var o = Xh(n, t, e.get("scaleLimit")); + e.setCenter && e.setCenter(o.center), e.setZoom && e.setZoom(o.zoom), "series" === i && d(e.seriesGroup, function (t) { + t.setCenter(o.center), t.setZoom(o.zoom) + }) + } + }) + }); + fs(function (t) { + var e = {}; + t.eachSeriesByType("map", function (i) { + var n = i.getMapType(); + if (!i.getHostGeoModel() && !e[n]) { + var o = {}; + d(i.seriesGroup, function (e) { + var i = e.coordinateSystem, n = e.originalData; + e.get("showLegendSymbol") && t.getComponent("legend") && n.each(n.mapDimension("value"), function (t, e) { + var a = n.getName(e), r = i.getRegion(a); + if (r && !isNaN(t)) { + var s = o[a] || 0, l = i.dataToPoint(r.center); + o[a] = s + 1, n.setItemLayout(e, {point: l, offset: s}) + } + }) + }); + var a = i.getData(); + a.each(function (t) { + var e = a.getName(t), i = a.getItemLayout(t) || {}; + i.showLabel = !o[e], a.setItemLayout(t, i) + }), e[n] = !0 + } + }) + }), ps(function (t) { + t.eachSeriesByType("map", function (t) { + var e = t.get("color"), i = t.getModel("itemStyle"), n = i.get("areaColor"), + o = i.get("color") || e[t.seriesIndex % e.length]; + t.getData().setVisual({areaColor: n, color: o}) + }) + }), hs(LI.PROCESSOR.STATISTIC, function (t) { + var e = {}; + t.eachSeriesByType("map", function (t) { + var i = t.getHostGeoModel(), n = i ? "o" + i.id : "i" + t.getMapType(); + (e[n] = e[n] || []).push(t) + }), d(e, function (t, e) { + for (var i = jh(f(t, function (t) { + return t.getData() + }), t[0].get("mapValueCalculation")), n = 0; n < t.length; n++) t[n].originalData = t[n].getData(); + for (n = 0; n < t.length; n++) t[n].seriesGroup = t, t[n].needsDrawMap = 0 === n && !t[n].getHostGeoModel(), t[n].setData(i.cloneShallow()), t[n].mainSeries = t[0] + }) + }), us(function (t) { + var e = []; + d(t.series, function (t) { + t && "map" === t.type && (e.push(t), t.map = t.map || t.mapType, r(t, t.mapLocation)) + }) + }), $D("map", [{type: "mapToggleSelect", event: "mapselectchanged", method: "toggleSelected"}, { + type: "mapSelect", + event: "mapselected", + method: "select" + }, {type: "mapUnSelect", event: "mapunselected", method: "unSelect"}]); + var wA = d, bA = "\0__link_datas", SA = "\0__link_mainData", MA = function (t, e) { + this.name = t || "", this.depth = 0, this.height = 0, this.parentNode = null, this.dataIndex = -1, this.children = [], this.viewChildren = [], this.hostTree = e + }; + MA.prototype = { + constructor: MA, isRemoved: function () { + return this.dataIndex < 0 + }, eachNode: function (t, e, i) { + "function" == typeof t && (i = e, e = t, t = null), _(t = t || {}) && (t = {order: t}); + var n, o = t.order || "preorder", a = this[t.attr || "children"]; + "preorder" === o && (n = e.call(i, this)); + for (var r = 0; !n && r < a.length; r++) a[r].eachNode(t, e, i); + "postorder" === o && e.call(i, this) + }, updateDepthAndHeight: function (t) { + var e = 0; + this.depth = t; + for (var i = 0; i < this.children.length; i++) { + var n = this.children[i]; + n.updateDepthAndHeight(t + 1), n.height > e && (e = n.height) + } + this.height = e + 1 + }, getNodeById: function (t) { + if (this.getId() === t) return this; + for (var e = 0, i = this.children, n = i.length; e < n; e++) { + var o = i[e].getNodeById(t); + if (o) return o + } + }, contains: function (t) { + if (t === this) return !0; + for (var e = 0, i = this.children, n = i.length; e < n; e++) { + var o = i[e].contains(t); + if (o) return o + } + }, getAncestors: function (t) { + for (var e = [], i = t ? this : this.parentNode; i;) e.push(i), i = i.parentNode; + return e.reverse(), e + }, getValue: function (t) { + var e = this.hostTree.data; + return e.get(e.getDimension(t || "value"), this.dataIndex) + }, setLayout: function (t, e) { + this.dataIndex >= 0 && this.hostTree.data.setItemLayout(this.dataIndex, t, e) + }, getLayout: function () { + return this.hostTree.data.getItemLayout(this.dataIndex) + }, getModel: function (t) { + if (!(this.dataIndex < 0)) { + var e, i = this.hostTree, n = i.data.getItemModel(this.dataIndex), o = this.getLevelModel(); + return o || 0 !== this.children.length && (0 === this.children.length || !1 !== this.isExpand) || (e = this.getLeavesModel()), n.getModel(t, (o || e || i.hostModel).getModel(t)) + } + }, getLevelModel: function () { + return (this.hostTree.levelModels || [])[this.depth] + }, getLeavesModel: function () { + return this.hostTree.leavesModel + }, setVisual: function (t, e) { + this.dataIndex >= 0 && this.hostTree.data.setItemVisual(this.dataIndex, t, e) + }, getVisual: function (t, e) { + return this.hostTree.data.getItemVisual(this.dataIndex, t, e) + }, getRawIndex: function () { + return this.hostTree.data.getRawIndex(this.dataIndex) + }, getId: function () { + return this.hostTree.data.getId(this.dataIndex) + }, isAncestorOf: function (t) { + for (var e = t.parentNode; e;) { + if (e === this) return !0; + e = e.parentNode + } + return !1 + }, isDescendantOf: function (t) { + return t !== this && t.isAncestorOf(this) + } + }, ic.prototype = { + constructor: ic, type: "tree", eachNode: function (t, e, i) { + this.root.eachNode(t, e, i) + }, getNodeByDataIndex: function (t) { + var e = this.data.getRawIndex(t); + return this._nodes[e] + }, getNodeByName: function (t) { + return this.root.getNodeByName(t) + }, update: function () { + for (var t = this.data, e = this._nodes, i = 0, n = e.length; i < n; i++) e[i].dataIndex = -1; + for (var i = 0, n = t.count(); i < n; i++) e[t.getRawIndex(i)].dataIndex = i + }, clearLayouts: function () { + this.data.clearItemLayouts() + } + }, ic.createTree = function (t, e, i) { + function n(t, e) { + var i = t.value; + r = Math.max(r, y(i) ? i.length : 1), a.push(t); + var s = new MA(t.name, o); + e ? nc(s, e) : o.root = s, o._nodes.push(s); + var l = t.children; + if (l) for (var u = 0; u < l.length; u++) n(l[u], s) + } + + var o = new ic(e, i.levels, i.leaves), a = [], r = 1; + n(t), o.root.updateDepthAndHeight(0); + var s = hT(a, {coordDimensions: ["value"], dimensionsCount: r}), l = new sT(s, e); + return l.initData(a), Yh({mainData: l, struct: o, structAttr: "tree"}), o.update(), o + }, KM.extend({ + type: "series.tree", + layoutInfo: null, + layoutMode: "box", + getInitialData: function (t) { + var e = {name: t.name, children: t.data}, i = t.leaves || {}, n = {}; + n.leaves = i; + var o = ic.createTree(e, this, n), a = 0; + o.eachNode("preorder", function (t) { + t.depth > a && (a = t.depth) + }); + var r = t.expandAndCollapse && t.initialTreeDepth >= 0 ? t.initialTreeDepth : a; + return o.root.eachNode("preorder", function (t) { + var e = t.hostTree.data.getRawDataItem(t.dataIndex); + t.isExpand = e && null != e.collapsed ? !e.collapsed : t.depth <= r + }), o.data + }, + getOrient: function () { + var t = this.get("orient"); + return "horizontal" === t ? t = "LR" : "vertical" === t && (t = "TB"), t + }, + formatTooltip: function (t) { + for (var e = this.getData().tree, i = e.root.children[0], n = e.getNodeByDataIndex(t), o = n.getValue(), a = n.name; n && n !== i;) a = n.parentNode.name + "." + a, n = n.parentNode; + return Zo(a + (isNaN(o) || null == o ? "" : " : " + o)) + }, + defaultOption: { + zlevel: 0, + z: 2, + left: "12%", + top: "12%", + right: "12%", + bottom: "12%", + layout: "orthogonal", + orient: "LR", + symbol: "emptyCircle", + symbolSize: 7, + expandAndCollapse: !0, + initialTreeDepth: 2, + lineStyle: {color: "#ccc", width: 1.5, curveness: .5}, + itemStyle: {color: "lightsteelblue", borderColor: "#c23531", borderWidth: 1.5}, + label: {show: !0, color: "#555"}, + leaves: {label: {show: !0}}, + animationEasing: "linear", + animationDuration: 700, + animationDurationUpdate: 1e3 + } + }), _s({ + type: "tree", init: function (t, e) { + this._oldTree, this._mainGroup = new sw, this.group.add(this._mainGroup) + }, render: function (t, e, i, n) { + var o = t.getData(), a = t.layoutInfo, r = this._mainGroup, s = t.get("layout"); + "radial" === s ? r.attr("position", [a.x + a.width / 2, a.y + a.height / 2]) : r.attr("position", [a.x, a.y]); + var l = this._data, u = { + expandAndCollapse: t.get("expandAndCollapse"), + layout: s, + orient: t.getOrient(), + curvature: t.get("lineStyle.curveness"), + symbolRotate: t.get("symbolRotate"), + symbolOffset: t.get("symbolOffset"), + hoverAnimation: t.get("hoverAnimation"), + useNameLabel: !0, + fadeIn: !0 + }; + o.diff(l).add(function (e) { + vc(o, e) && xc(o, e, null, r, t, u) + }).update(function (e, i) { + var n = l.getItemGraphicEl(i); + vc(o, e) ? xc(o, e, n, r, t, u) : n && _c(l, i, n, r, t, u) + }).remove(function (e) { + var i = l.getItemGraphicEl(e); + i && _c(l, e, i, r, t, u) + }).execute(), !0 === u.expandAndCollapse && o.eachItemGraphicEl(function (e, n) { + e.off("click").on("click", function () { + i.dispatchAction({type: "treeExpandAndCollapse", seriesId: t.id, dataIndex: n}) + }) + }), this._data = o + }, dispose: function () { + }, remove: function () { + this._mainGroup.removeAll(), this._data = null + } + }), cs({type: "treeExpandAndCollapse", event: "treeExpandAndCollapse", update: "update"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "tree", query: t}, function (e) { + var i = t.dataIndex, n = e.getData().tree.getNodeByDataIndex(i); + n.isExpand = !n.isExpand + }) + }); + ps(gD("tree", "circle")), fs(function (t, e) { + t.eachSeriesByType("tree", function (t) { + Mc(t, e) + }) + }), KM.extend({ + type: "series.treemap", layoutMode: "box", dependencies: ["grid", "polar"], _viewRoot: null, defaultOption: { + progressive: 0, + hoverLayerThreshold: 1 / 0, + left: "center", + top: "middle", + right: null, + bottom: null, + width: "80%", + height: "80%", + sort: !0, + clipWindow: "origin", + squareRatio: .5 * (1 + Math.sqrt(5)), + leafDepth: null, + drillDownIcon: "▶", + zoomToNodeRatio: .1024, + roam: !0, + nodeClick: "zoomToNode", + animation: !0, + animationDurationUpdate: 900, + animationEasing: "quinticInOut", + breadcrumb: { + show: !0, + height: 22, + left: "center", + top: "bottom", + emptyItemWidth: 25, + itemStyle: { + color: "rgba(0,0,0,0.7)", + borderColor: "rgba(255,255,255,0.7)", + borderWidth: 1, + shadowColor: "rgba(150,150,150,1)", + shadowBlur: 3, + shadowOffsetX: 0, + shadowOffsetY: 0, + textStyle: {color: "#fff"} + }, + emphasis: {textStyle: {}} + }, + label: {show: !0, distance: 0, padding: 5, position: "inside", color: "#fff", ellipsis: !0}, + upperLabel: { + show: !1, + position: [0, "50%"], + height: 20, + color: "#fff", + ellipsis: !0, + verticalAlign: "middle" + }, + itemStyle: { + color: null, + colorAlpha: null, + colorSaturation: null, + borderWidth: 0, + gapWidth: 0, + borderColor: "#fff", + borderColorSaturation: null + }, + emphasis: { + upperLabel: { + show: !0, + position: [0, "50%"], + color: "#fff", + ellipsis: !0, + verticalAlign: "middle" + } + }, + visualDimension: 0, + visualMin: null, + visualMax: null, + color: [], + colorAlpha: null, + colorSaturation: null, + colorMappingBy: "index", + visibleMin: 10, + childrenVisibleMin: null, + levels: [] + }, getInitialData: function (t, e) { + var i = {name: t.name, children: t.data}; + Cc(i); + var n = t.levels || []; + n = t.levels = Lc(n, e); + var o = {}; + return o.levels = n, ic.createTree(i, this, o).data + }, optionUpdated: function () { + this.resetViewRoot() + }, formatTooltip: function (t) { + var e = this.getData(), i = this.getRawValue(t), n = Fo(y(i) ? i[0] : i); + return Zo(e.getName(t) + ": " + n) + }, getDataParams: function (t) { + var e = KM.prototype.getDataParams.apply(this, arguments), i = this.getData().tree.getNodeByDataIndex(t); + return e.treePathInfo = Ac(i, this), e + }, setLayoutInfo: function (t) { + this.layoutInfo = this.layoutInfo || {}, a(this.layoutInfo, t) + }, mapIdToIndex: function (t) { + var e = this._idIndexMap; + e || (e = this._idIndexMap = R(), this._idIndexMapCount = 0); + var i = e.get(t); + return null == i && e.set(t, i = this._idIndexMapCount++), i + }, getViewRoot: function () { + return this._viewRoot + }, resetViewRoot: function (t) { + t ? this._viewRoot = t : t = this._viewRoot; + var e = this.getRawData().tree.root; + t && (t === e || e.contains(t)) || (this._viewRoot = e) + } + }); + var IA = 5; + kc.prototype = { + constructor: kc, render: function (t, e, i, n) { + var o = t.getModel("breadcrumb"), a = this.group; + if (a.removeAll(), o.get("show") && i) { + var r = o.getModel("itemStyle"), s = r.getModel("textStyle"), l = { + pos: {left: o.get("left"), right: o.get("right"), top: o.get("top"), bottom: o.get("bottom")}, + box: {width: e.getWidth(), height: e.getHeight()}, + emptyItemWidth: o.get("emptyItemWidth"), + totalWidth: 0, + renderList: [] + }; + this._prepare(i, l, s), this._renderContent(t, l, r, s, n), ta(a, l.pos, l.box) + } + }, _prepare: function (t, e, i) { + for (var n = t; n; n = n.parentNode) { + var o = n.getModel().get("name"), a = i.getTextRect(o), r = Math.max(a.width + 16, e.emptyItemWidth); + e.totalWidth += r + 8, e.renderList.push({node: n, text: o, width: r}) + } + }, _renderContent: function (t, e, i, n, o) { + for (var a = 0, s = e.emptyItemWidth, l = t.get("breadcrumb.height"), u = Jo(e.pos, e.box), h = e.totalWidth, c = e.renderList, d = c.length - 1; d >= 0; d--) { + var f = c[d], p = f.node, g = f.width, m = f.text; + h > u.width && (h -= g - s, g = s, m = null); + var y = new wS({ + shape: {points: Pc(a, 0, g, l, d === c.length - 1, 0 === d)}, + style: r(i.getItemStyle(), { + lineJoin: "bevel", + text: m, + textFill: n.getTextColor(), + textFont: n.getFont() + }), + z: 10, + onclick: v(o, p) + }); + this.group.add(y), Nc(y, t, p), a += g + 8 + } + }, remove: function () { + this.group.removeAll() + } + }; + var TA = m, DA = sw, AA = SS, CA = d, LA = ["label"], kA = ["emphasis", "label"], PA = ["upperLabel"], + NA = ["emphasis", "upperLabel"], OA = 10, EA = 1, RA = 2, + zA = sb([["fill", "color"], ["stroke", "strokeColor"], ["lineWidth", "strokeWidth"], ["shadowBlur"], ["shadowOffsetX"], ["shadowOffsetY"], ["shadowColor"]]), + BA = function (t) { + var e = zA(t); + return e.stroke = e.fill = e.lineWidth = null, e + }; + _s({ + type: "treemap", init: function (t, e) { + this._containerGroup, this._storage = { + nodeGroup: [], + background: [], + content: [] + }, this._oldTree, this._breadcrumb, this._controller, this._state = "ready" + }, render: function (t, e, i, n) { + if (!(l(e.findComponents({mainType: "series", subType: "treemap", query: n}), t) < 0)) { + this.seriesModel = t, this.api = i, this.ecModel = e; + var o = Ic(n, ["treemapZoomToNode", "treemapRootToNode"], t), a = n && n.type, r = t.layoutInfo, + s = !this._oldTree, u = this._storage, h = "treemapRootToNode" === a && o && u ? { + rootNodeGroup: u.nodeGroup[o.node.getRawIndex()], + direction: n.direction + } : null, c = this._giveContainerGroup(r), d = this._doRender(c, t, h); + s || a && "treemapZoomToNode" !== a && "treemapRootToNode" !== a ? d.renderFinally() : this._doAnimation(c, d, t, h), this._resetController(i), this._renderBreadcrumb(t, i, o) + } + }, _giveContainerGroup: function (t) { + var e = this._containerGroup; + return e || (e = this._containerGroup = new DA, this._initEvents(e), this.group.add(e)), e.attr("position", [t.x, t.y]), e + }, _doRender: function (t, e, i) { + function n(t, e, i, o, a) { + function r(t) { + return t.getId() + } + + function s(r, s) { + var l = null != r ? t[r] : null, u = null != s ? e[s] : null, c = h(l, u, i, a); + c && n(l && l.viewChildren || [], u && u.viewChildren || [], c, o, a + 1) + } + + o ? (e = t, CA(t, function (t, e) { + !t.isRemoved() && s(e, e) + })) : new Ss(e, t, r, r).add(s).update(s).remove(v(s, null)).execute() + } + + var o = e.getData().tree, a = this._oldTree, r = {nodeGroup: [], background: [], content: []}, + s = {nodeGroup: [], background: [], content: []}, l = this._storage, u = [], + h = v(Ec, e, s, l, i, r, u); + n(o.root ? [o.root] : [], a && a.root ? [a.root] : [], t, o === a || !a, 0); + var c = function (t) { + var e = {nodeGroup: [], background: [], content: []}; + return t && CA(t, function (t, i) { + var n = e[i]; + CA(t, function (t) { + t && (n.push(t), t.__tmWillDelete = 1) + }) + }), e + }(l); + return this._oldTree = o, this._storage = s, { + lastsForAnimation: r, + willDeleteEls: c, + renderFinally: function () { + CA(c, function (t) { + CA(t, function (t) { + t.parent && t.parent.remove(t) + }) + }), CA(u, function (t) { + t.invisible = !0, t.dirty() + }) + } + } + }, _doAnimation: function (t, e, i, n) { + if (i.get("animation")) { + var o = i.get("animationDurationUpdate"), r = i.get("animationEasing"), s = Oc(); + CA(e.willDeleteEls, function (t, e) { + CA(t, function (t, i) { + if (!t.invisible) { + var a, l = t.parent; + if (n && "drillDown" === n.direction) a = l === n.rootNodeGroup ? { + shape: { + x: 0, + y: 0, + width: l.__tmNodeWidth, + height: l.__tmNodeHeight + }, style: {opacity: 0} + } : {style: {opacity: 0}}; else { + var u = 0, h = 0; + l.__tmWillDelete || (u = l.__tmNodeWidth / 2, h = l.__tmNodeHeight / 2), a = "nodeGroup" === e ? { + position: [u, h], + style: {opacity: 0} + } : {shape: {x: u, y: h, width: 0, height: 0}, style: {opacity: 0}} + } + a && s.add(t, a, o, r) + } + }) + }), CA(this._storage, function (t, i) { + CA(t, function (t, n) { + var l = e.lastsForAnimation[i][n], u = {}; + l && ("nodeGroup" === i ? l.old && (u.position = t.position.slice(), t.attr("position", l.old)) : (l.old && (u.shape = a({}, t.shape), t.setShape(l.old)), l.fadein ? (t.setStyle("opacity", 0), u.style = {opacity: 1}) : 1 !== t.style.opacity && (u.style = {opacity: 1})), s.add(t, u, o, r)) + }) + }, this), this._state = "animating", s.done(TA(function () { + this._state = "ready", e.renderFinally() + }, this)).start() + } + }, _resetController: function (t) { + var e = this._controller; + e || ((e = this._controller = new kh(t.getZr())).enable(this.seriesModel.get("roam")), e.on("pan", TA(this._onPan, this)), e.on("zoom", TA(this._onZoom, this))); + var i = new $t(0, 0, t.getWidth(), t.getHeight()); + e.setPointerChecker(function (t, e, n) { + return i.contain(e, n) + }) + }, _clearController: function () { + var t = this._controller; + t && (t.dispose(), t = null) + }, _onPan: function (t, e) { + if ("animating" !== this._state && (Math.abs(t) > 3 || Math.abs(e) > 3)) { + var i = this.seriesModel.getData().tree.root; + if (!i) return; + var n = i.getLayout(); + if (!n) return; + this.api.dispatchAction({ + type: "treemapMove", + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: {x: n.x + t, y: n.y + e, width: n.width, height: n.height} + }) + } + }, _onZoom: function (t, e, i) { + if ("animating" !== this._state) { + var n = this.seriesModel.getData().tree.root; + if (!n) return; + var o = n.getLayout(); + if (!o) return; + var a = new $t(o.x, o.y, o.width, o.height), r = this.seriesModel.layoutInfo; + e -= r.x, i -= r.y; + var s = st(); + ct(s, s, [-e, -i]), ft(s, s, [t, t]), ct(s, s, [e, i]), a.applyTransform(s), this.api.dispatchAction({ + type: "treemapRender", + from: this.uid, + seriesId: this.seriesModel.id, + rootRect: {x: a.x, y: a.y, width: a.width, height: a.height} + }) + } + }, _initEvents: function (t) { + t.on("click", function (t) { + if ("ready" === this._state) { + var e = this.seriesModel.get("nodeClick", !0); + if (e) { + var i = this.findTarget(t.offsetX, t.offsetY); + if (i) { + var n = i.node; + if (n.getLayout().isLeafRoot) this._rootToNode(i); else if ("zoomToNode" === e) this._zoomToNode(i); else if ("link" === e) { + var o = n.hostTree.data.getItemModel(n.dataIndex), a = o.get("link", !0), + r = o.get("target", !0) || "blank"; + a && window.open(a, r) + } + } + } + } + }, this) + }, _renderBreadcrumb: function (t, e, i) { + i || (i = null != t.get("leafDepth", !0) ? {node: t.getViewRoot()} : this.findTarget(e.getWidth() / 2, e.getHeight() / 2)) || (i = {node: t.getData().tree.root}), (this._breadcrumb || (this._breadcrumb = new kc(this.group))).render(t, e, i.node, TA(function (e) { + "animating" !== this._state && (Dc(t.getViewRoot(), e) ? this._rootToNode({node: e}) : this._zoomToNode({node: e})) + }, this)) + }, remove: function () { + this._clearController(), this._containerGroup && this._containerGroup.removeAll(), this._storage = { + nodeGroup: [], + background: [], + content: [] + }, this._state = "ready", this._breadcrumb && this._breadcrumb.remove() + }, dispose: function () { + this._clearController() + }, _zoomToNode: function (t) { + this.api.dispatchAction({ + type: "treemapZoomToNode", + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: t.node + }) + }, _rootToNode: function (t) { + this.api.dispatchAction({ + type: "treemapRootToNode", + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: t.node + }) + }, findTarget: function (t, e) { + var i; + return this.seriesModel.getViewRoot().eachNode({attr: "viewChildren", order: "preorder"}, function (n) { + var o = this._storage.background[n.getRawIndex()]; + if (o) { + var a = o.transformCoordToLocal(t, e), r = o.shape; + if (!(r.x <= a[0] && a[0] <= r.x + r.width && r.y <= a[1] && a[1] <= r.y + r.height)) return !1; + i = {node: n, offsetX: a[0], offsetY: a[1]} + } + }, this), i + } + }); + for (var VA = ["treemapZoomToNode", "treemapRender", "treemapMove"], GA = 0; GA < VA.length; GA++) cs({ + type: VA[GA], + update: "updateView" + }, function () { + }); + cs({type: "treemapRootToNode", update: "updateView"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "treemap", query: t}, function (e, i) { + var n = Ic(t, ["treemapZoomToNode", "treemapRootToNode"], e); + if (n) { + var o = e.getViewRoot(); + o && (t.direction = Dc(o, n.node) ? "rollUp" : "drillDown"), e.resetViewRoot(n.node) + } + }) + }); + var WA = d, FA = w, HA = -1, ZA = function (t) { + var e = t.mappingMethod, n = t.type, o = this.option = i(t); + this.type = n, this.mappingMethod = e, this._normalizeData = XA[e]; + var a = UA[n]; + this.applyVisual = a.applyVisual, this.getColorMapper = a.getColorMapper, this._doMap = a._doMap[e], "piecewise" === e ? (Vc(o), zc(o)) : "category" === e ? o.categories ? Bc(o) : Vc(o, !0) : (k("linear" !== e || o.dataExtent), Vc(o)) + }; + ZA.prototype = { + constructor: ZA, mapValueToVisual: function (t) { + var e = this._normalizeData(t); + return this._doMap(e, t) + }, getNormalizer: function () { + return m(this._normalizeData, this) + } + }; + var UA = ZA.visualHandlers = { + color: { + applyVisual: Fc("color"), getColorMapper: function () { + var t = this.option; + return m("category" === t.mappingMethod ? function (t, e) { + return !e && (t = this._normalizeData(t)), Hc.call(this, t) + } : function (e, i, n) { + var o = !!n; + return !i && (e = this._normalizeData(e)), n = Nt(e, t.parsedVisual, n), o ? n : zt(n, "rgba") + }, this) + }, _doMap: { + linear: function (t) { + return zt(Nt(t, this.option.parsedVisual), "rgba") + }, category: Hc, piecewise: function (t, e) { + var i = Xc.call(this, e); + return null == i && (i = zt(Nt(t, this.option.parsedVisual), "rgba")), i + }, fixed: Zc + } + }, + colorHue: Gc(function (t, e) { + return Et(t, e) + }), + colorSaturation: Gc(function (t, e) { + return Et(t, null, e) + }), + colorLightness: Gc(function (t, e) { + return Et(t, null, null, e) + }), + colorAlpha: Gc(function (t, e) { + return Rt(t, e) + }), + opacity: {applyVisual: Fc("opacity"), _doMap: Uc([0, 1])}, + liftZ: {applyVisual: Fc("liftZ"), _doMap: {linear: Zc, category: Zc, piecewise: Zc, fixed: Zc}}, + symbol: { + applyVisual: function (t, e, i) { + var n = this.mapValueToVisual(t); + if (_(n)) i("symbol", n); else if (FA(n)) for (var o in n) n.hasOwnProperty(o) && i(o, n[o]) + }, _doMap: { + linear: Wc, category: Hc, piecewise: function (t, e) { + var i = Xc.call(this, e); + return null == i && (i = Wc.call(this, t)), i + }, fixed: Zc + } + }, + symbolSize: {applyVisual: Fc("symbolSize"), _doMap: Uc([0, 1])} + }, XA = { + linear: function (t) { + return To(t, this.option.dataExtent, [0, 1], !0) + }, piecewise: function (t) { + var e = this.option.pieceList, i = ZA.findPieceIndex(t, e, !0); + if (null != i) return To(i, [0, e.length - 1], [0, 1], !0) + }, category: function (t) { + var e = this.option.categories ? this.option.categoryMap[t] : t; + return null == e ? HA : e + }, fixed: B + }; + ZA.listVisualTypes = function () { + var t = []; + return d(UA, function (e, i) { + t.push(i) + }), t + }, ZA.addVisualHandler = function (t, e) { + UA[t] = e + }, ZA.isValidType = function (t) { + return UA.hasOwnProperty(t) + }, ZA.eachVisual = function (t, e, i) { + w(t) ? d(t, e, i) : e.call(i, t) + }, ZA.mapVisual = function (t, e, i) { + var n, o = y(t) ? [] : w(t) ? {} : (n = !0, null); + return ZA.eachVisual(t, function (t, a) { + var r = e.call(i, t, a); + n ? o = r : o[a] = r + }), o + }, ZA.retrieveVisuals = function (t) { + var e, i = {}; + return t && WA(UA, function (n, o) { + t.hasOwnProperty(o) && (i[o] = t[o], e = !0) + }), e ? i : null + }, ZA.prepareVisualTypes = function (t) { + if (FA(t)) { + var e = []; + WA(t, function (t, i) { + e.push(i) + }), t = e + } else { + if (!y(t)) return []; + t = t.slice() + } + return t.sort(function (t, e) { + return "color" === e && "color" !== t && 0 === t.indexOf("color") ? 1 : -1 + }), t + }, ZA.dependsOn = function (t, e) { + return "color" === e ? !(!t || 0 !== t.indexOf(e)) : t === e + }, ZA.findPieceIndex = function (t, e, i) { + function n(e, i) { + var n = Math.abs(e - t); + n < a && (a = n, o = i) + } + + for (var o, a = 1 / 0, r = 0, s = e.length; r < s; r++) { + var l = e[r].value; + if (null != l) { + if (l === t || "string" == typeof l && l === t + "") return r; + i && n(l, r) + } + } + for (var r = 0, s = e.length; r < s; r++) { + var u = e[r], h = u.interval, c = u.close; + if (h) { + if (h[0] === -1 / 0) { + if (Yc(c[1], t, h[1])) return r + } else if (h[1] === 1 / 0) { + if (Yc(c[0], h[0], t)) return r + } else if (Yc(c[0], h[0], t) && Yc(c[1], t, h[1])) return r; + i && n(h[0], r), i && n(h[1], r) + } + } + if (i) return t === 1 / 0 ? e.length - 1 : t === -1 / 0 ? 0 : o + }; + var jA = y, YA = "itemStyle", qA = { + seriesType: "treemap", reset: function (t, e, i, n) { + var o = t.getData().tree, a = o.root, r = t.getModel(YA); + a.isRemoved() || qc(a, {}, f(o.levelModels, function (t) { + return t ? t.get(YA) : null + }), r, t.getViewRoot().getAncestors(), t) + } + }, KA = Math.max, $A = Math.min, JA = T, QA = d, tC = ["itemStyle", "borderWidth"], eC = ["itemStyle", "gapWidth"], + iC = ["upperLabel", "show"], nC = ["upperLabel", "height"], oC = { + seriesType: "treemap", reset: function (t, e, i, n) { + var o = i.getWidth(), r = i.getHeight(), s = t.option, + l = Qo(t.getBoxLayoutParams(), {width: i.getWidth(), height: i.getHeight()}), u = s.size || [], + h = Do(JA(l.width, u[0]), o), c = Do(JA(l.height, u[1]), r), d = n && n.type, + f = Ic(n, ["treemapZoomToNode", "treemapRootToNode"], t), + p = "treemapRender" === d || "treemapMove" === d ? n.rootRect : null, g = t.getViewRoot(), m = Tc(g); + if ("treemapMove" !== d) { + var v = "treemapZoomToNode" === d ? hd(t, f, g, h, c) : p ? [p.width, p.height] : [h, c], y = s.sort; + y && "asc" !== y && "desc" !== y && (y = "desc"); + var x = {squareRatio: s.squareRatio, sort: y, leafDepth: s.leafDepth}; + g.hostTree.clearLayouts(); + _ = {x: 0, y: 0, width: v[0], height: v[1], area: v[0] * v[1]}; + g.setLayout(_), nd(g, x, !1, 0); + var _ = g.getLayout(); + QA(m, function (t, e) { + var i = (m[e + 1] || g).getValue(); + t.setLayout(a({dataExtent: [i, i], borderWidth: 0, upperHeight: 0}, _)) + }) + } + var w = t.getData().tree.root; + w.setLayout(cd(l, p, f), !0), t.setLayoutInfo(l), dd(w, new $t(-l.x, -l.y, o, r), m, g, 0) + } + }; + ps(qA), fs(oC); + var aC = function (t) { + this._directed = t || !1, this.nodes = [], this.edges = [], this._nodesMap = {}, this._edgesMap = {}, this.data, this.edgeData + }, rC = aC.prototype; + rC.type = "graph", rC.isDirected = function () { + return this._directed + }, rC.addNode = function (t, e) { + t = t || "" + e; + var i = this._nodesMap; + if (!i[pd(t)]) { + var n = new gd(t, e); + return n.hostGraph = this, this.nodes.push(n), i[pd(t)] = n, n + } + }, rC.getNodeByIndex = function (t) { + var e = this.data.getRawIndex(t); + return this.nodes[e] + }, rC.getNodeById = function (t) { + return this._nodesMap[pd(t)] + }, rC.addEdge = function (t, e, i) { + var n = this._nodesMap, o = this._edgesMap; + if ("number" == typeof t && (t = this.nodes[t]), "number" == typeof e && (e = this.nodes[e]), gd.isInstance(t) || (t = n[pd(t)]), gd.isInstance(e) || (e = n[pd(e)]), t && e) { + var a = t.id + "-" + e.id; + if (!o[a]) { + var r = new md(t, e, i); + return r.hostGraph = this, this._directed && (t.outEdges.push(r), e.inEdges.push(r)), t.edges.push(r), t !== e && e.edges.push(r), this.edges.push(r), o[a] = r, r + } + } + }, rC.getEdgeByIndex = function (t) { + var e = this.edgeData.getRawIndex(t); + return this.edges[e] + }, rC.getEdge = function (t, e) { + gd.isInstance(t) && (t = t.id), gd.isInstance(e) && (e = e.id); + var i = this._edgesMap; + return this._directed ? i[t + "-" + e] : i[t + "-" + e] || i[e + "-" + t] + }, rC.eachNode = function (t, e) { + for (var i = this.nodes, n = i.length, o = 0; o < n; o++) i[o].dataIndex >= 0 && t.call(e, i[o], o) + }, rC.eachEdge = function (t, e) { + for (var i = this.edges, n = i.length, o = 0; o < n; o++) i[o].dataIndex >= 0 && i[o].node1.dataIndex >= 0 && i[o].node2.dataIndex >= 0 && t.call(e, i[o], o) + }, rC.breadthFirstTraverse = function (t, e, i, n) { + if (gd.isInstance(e) || (e = this._nodesMap[pd(e)]), e) { + for (var o = "out" === i ? "outEdges" : "in" === i ? "inEdges" : "edges", a = 0; a < this.nodes.length; a++) this.nodes[a].__visited = !1; + if (!t.call(n, e, null)) for (var r = [e]; r.length;) for (var s = r.shift(), l = s[o], a = 0; a < l.length; a++) { + var u = l[a], h = u.node1 === s ? u.node2 : u.node1; + if (!h.__visited) { + if (t.call(n, h, s)) return; + r.push(h), h.__visited = !0 + } + } + } + }, rC.update = function () { + for (var t = this.data, e = this.edgeData, i = this.nodes, n = this.edges, o = 0, a = i.length; o < a; o++) i[o].dataIndex = -1; + for (var o = 0, a = t.count(); o < a; o++) i[t.getRawIndex(o)].dataIndex = o; + e.filterSelf(function (t) { + var i = n[e.getRawIndex(t)]; + return i.node1.dataIndex >= 0 && i.node2.dataIndex >= 0 + }); + for (var o = 0, a = n.length; o < a; o++) n[o].dataIndex = -1; + for (var o = 0, a = e.count(); o < a; o++) n[e.getRawIndex(o)].dataIndex = o + }, rC.clone = function () { + for (var t = new aC(this._directed), e = this.nodes, i = this.edges, n = 0; n < e.length; n++) t.addNode(e[n].id, e[n].dataIndex); + for (n = 0; n < i.length; n++) { + var o = i[n]; + t.addEdge(o.node1.id, o.node2.id, o.dataIndex) + } + return t + }, gd.prototype = { + constructor: gd, degree: function () { + return this.edges.length + }, inDegree: function () { + return this.inEdges.length + }, outDegree: function () { + return this.outEdges.length + }, getModel: function (t) { + if (!(this.dataIndex < 0)) return this.hostGraph.data.getItemModel(this.dataIndex).getModel(t) + } + }, md.prototype.getModel = function (t) { + if (!(this.dataIndex < 0)) return this.hostGraph.edgeData.getItemModel(this.dataIndex).getModel(t) + }; + var sC = function (t, e) { + return { + getValue: function (i) { + var n = this[t][e]; + return n.get(n.getDimension(i || "value"), this.dataIndex) + }, setVisual: function (i, n) { + this.dataIndex >= 0 && this[t][e].setItemVisual(this.dataIndex, i, n) + }, getVisual: function (i, n) { + return this[t][e].getItemVisual(this.dataIndex, i, n) + }, setLayout: function (i, n) { + this.dataIndex >= 0 && this[t][e].setItemLayout(this.dataIndex, i, n) + }, getLayout: function () { + return this[t][e].getItemLayout(this.dataIndex) + }, getGraphicEl: function () { + return this[t][e].getItemGraphicEl(this.dataIndex) + }, getRawIndex: function () { + return this[t][e].getRawIndex(this.dataIndex) + } + } + }; + h(gd, sC("hostGraph", "data")), h(md, sC("hostGraph", "edgeData")), aC.Node = gd, aC.Edge = md, Wi(gd), Wi(md); + var lC = function (t, e, i, n, o) { + for (var a = new aC(n), r = 0; r < t.length; r++) a.addNode(T(t[r].id, t[r].name, r), r); + for (var s = [], u = [], h = 0, r = 0; r < e.length; r++) { + var c = e[r], d = c.source, f = c.target; + a.addEdge(d, f, h) && (u.push(c), s.push(T(c.id, d + " > " + f)), h++) + } + var p, g = i.get("coordinateSystem"); + if ("cartesian2d" === g || "polar" === g) p = Xs(t, i); else { + var m = Ca.get(g), v = m && "view" !== m.type ? m.dimensions || [] : []; + l(v, "value") < 0 && v.concat(["value"]); + var y = hT(t, {coordDimensions: v}); + (p = new sT(y, i)).initData(t) + } + var x = new sT(["value"], i); + return x.initData(u, s), o && o(p, x), Yh({ + mainData: p, + struct: a, + structAttr: "graph", + datas: {node: p, edge: x}, + datasAttr: {node: "data", edge: "edgeData"} + }), a.update(), a + }, uC = xs({ + type: "series.graph", + init: function (t) { + uC.superApply(this, "init", arguments), this.legendDataProvider = function () { + return this._categoriesData + }, this.fillDataTextStyle(t.edges || t.links), this._updateCategoriesData() + }, + mergeOption: function (t) { + uC.superApply(this, "mergeOption", arguments), this.fillDataTextStyle(t.edges || t.links), this._updateCategoriesData() + }, + mergeDefaultAndTheme: function (t) { + uC.superApply(this, "mergeDefaultAndTheme", arguments), Mi(t, ["edgeLabel"], ["show"]) + }, + getInitialData: function (t, e) { + var i = t.edges || t.links || [], n = t.data || t.nodes || [], o = this; + if (n && i) return lC(n, i, this, !0, function (t, i) { + function n(t) { + return (t = this.parsePath(t)) && "label" === t[0] ? r : t && "emphasis" === t[0] && "label" === t[1] ? l : this.parentModel + } + + t.wrapMethod("getItemModel", function (t) { + var e = o._categoriesModels[t.getShallow("category")]; + return e && (e.parentModel = t.parentModel, t.parentModel = e), t + }); + var a = o.getModel("edgeLabel"), r = new wo({label: a.option}, a.parentModel, e), + s = o.getModel("emphasis.edgeLabel"), l = new wo({emphasis: {label: s.option}}, s.parentModel, e); + i.wrapMethod("getItemModel", function (t) { + return t.customizeGetParent(n), t + }) + }).data + }, + getGraph: function () { + return this.getData().graph + }, + getEdgeData: function () { + return this.getGraph().edgeData + }, + getCategoriesData: function () { + return this._categoriesData + }, + formatTooltip: function (t, e, i) { + if ("edge" === i) { + var n = this.getData(), o = this.getDataParams(t, i), a = n.graph.getEdgeByIndex(t), + r = n.getName(a.node1.dataIndex), s = n.getName(a.node2.dataIndex), l = []; + return null != r && l.push(r), null != s && l.push(s), l = Zo(l.join(" > ")), o.value && (l += " : " + Zo(o.value)), l + } + return uC.superApply(this, "formatTooltip", arguments) + }, + _updateCategoriesData: function () { + var t = f(this.option.categories || [], function (t) { + return null != t.value ? t : a({value: 0}, t) + }), e = new sT(["value"], this); + e.initData(t), this._categoriesData = e, this._categoriesModels = e.mapArray(function (t) { + return e.getItemModel(t, !0) + }) + }, + setZoom: function (t) { + this.option.zoom = t + }, + setCenter: function (t) { + this.option.center = t + }, + isAnimationEnabled: function () { + return uC.superCall(this, "isAnimationEnabled") && !("force" === this.get("layout") && this.get("force.layoutAnimation")) + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "view", + legendHoverLink: !0, + hoverAnimation: !0, + layout: null, + focusNodeAdjacency: !1, + circular: {rotateLabel: !1}, + force: {initLayout: null, repulsion: [0, 50], gravity: .1, edgeLength: 30, layoutAnimation: !0}, + left: "center", + top: "center", + symbol: "circle", + symbolSize: 10, + edgeSymbol: ["none", "none"], + edgeSymbolSize: 10, + edgeLabel: {position: "middle"}, + draggable: !1, + roam: !1, + center: null, + zoom: 1, + nodeScaleRatio: .6, + label: {show: !1, formatter: "{b}"}, + itemStyle: {}, + lineStyle: {color: "#aaa", width: 1, curveness: 0, opacity: .5}, + emphasis: {label: {show: !0}} + } + }), hC = MS.prototype, cC = TS.prototype, dC = En({ + type: "ec-line", + style: {stroke: "#000", fill: null}, + shape: {x1: 0, y1: 0, x2: 0, y2: 0, percent: 1, cpx1: null, cpy1: null}, + buildPath: function (t, e) { + (vd(e) ? hC : cC).buildPath(t, e) + }, + pointAt: function (t) { + return vd(this.shape) ? hC.pointAt.call(this, t) : cC.pointAt.call(this, t) + }, + tangentAt: function (t) { + var e = this.shape, i = vd(e) ? [e.x2 - e.x1, e.y2 - e.y1] : cC.tangentAt.call(this, t); + return q(i, i) + } + }), fC = ["fromSymbol", "toSymbol"], pC = bd.prototype; + pC.beforeUpdate = function () { + var t = this, e = t.childOfName("fromSymbol"), i = t.childOfName("toSymbol"), n = t.childOfName("label"); + if (e || i || !n.ignore) { + for (var o = 1, a = this.parent; a;) a.scale && (o /= a.scale[0]), a = a.parent; + var r = t.childOfName("line"); + if (this.__dirty || r.__dirty) { + var s = r.shape.percent, l = r.pointAt(0), u = r.pointAt(s), h = U([], u, l); + if (q(h, h), e && (e.attr("position", l), c = r.tangentAt(0), e.attr("rotation", Math.PI / 2 - Math.atan2(c[1], c[0])), e.attr("scale", [o * s, o * s])), i) { + i.attr("position", u); + var c = r.tangentAt(1); + i.attr("rotation", -Math.PI / 2 - Math.atan2(c[1], c[0])), i.attr("scale", [o * s, o * s]) + } + if (!n.ignore) { + n.attr("position", u); + var d, f, p, g = 5 * o; + if ("end" === n.__position) d = [h[0] * g + u[0], h[1] * g + u[1]], f = h[0] > .8 ? "left" : h[0] < -.8 ? "right" : "center", p = h[1] > .8 ? "top" : h[1] < -.8 ? "bottom" : "middle"; else if ("middle" === n.__position) { + var m = s / 2, v = [(c = r.tangentAt(m))[1], -c[0]], y = r.pointAt(m); + v[1] > 0 && (v[0] = -v[0], v[1] = -v[1]), d = [y[0] + v[0] * g, y[1] + v[1] * g], f = "center", p = "bottom"; + var x = -Math.atan2(c[1], c[0]); + u[0] < l[0] && (x = Math.PI + x), n.attr("rotation", x) + } else d = [-h[0] * g + l[0], -h[1] * g + l[1]], f = h[0] > .8 ? "right" : h[0] < -.8 ? "left" : "center", p = h[1] > .8 ? "bottom" : h[1] < -.8 ? "top" : "middle"; + n.attr({ + style: {textVerticalAlign: n.__verticalAlign || p, textAlign: n.__textAlign || f}, + position: d, + scale: [o, o] + }) + } + } + } + }, pC._createLine = function (t, e, i) { + var n = t.hostModel, o = _d(t.getItemLayout(e)); + o.shape.percent = 0, po(o, {shape: {percent: 1}}, n, e), this.add(o); + var a = new fS({name: "label"}); + this.add(a), d(fC, function (i) { + var n = xd(i, t, e); + this.add(n), this[yd(i)] = t.getItemVisual(e, i) + }, this), this._updateCommonStl(t, e, i) + }, pC.updateData = function (t, e, i) { + var n = t.hostModel, o = this.childOfName("line"), a = t.getItemLayout(e), r = {shape: {}}; + wd(r.shape, a), fo(o, r, n, e), d(fC, function (i) { + var n = t.getItemVisual(e, i), o = yd(i); + if (this[o] !== n) { + this.remove(this.childOfName(i)); + var a = xd(i, t, e); + this.add(a) + } + this[o] = n + }, this), this._updateCommonStl(t, e, i) + }, pC._updateCommonStl = function (t, e, i) { + var n = t.hostModel, o = this.childOfName("line"), a = i && i.lineStyle, s = i && i.hoverLineStyle, + l = i && i.labelModel, u = i && i.hoverLabelModel; + if (!i || t.hasItemOption) { + var h = t.getItemModel(e); + a = h.getModel("lineStyle").getLineStyle(), s = h.getModel("emphasis.lineStyle").getLineStyle(), l = h.getModel("label"), u = h.getModel("emphasis.label") + } + var c = t.getItemVisual(e, "color"), f = A(t.getItemVisual(e, "opacity"), a.opacity, 1); + o.useStyle(r({ + strokeNoScale: !0, + fill: "none", + stroke: c, + opacity: f + }, a)), o.hoverStyle = s, d(fC, function (t) { + var e = this.childOfName(t); + e && (e.setColor(c), e.setStyle({opacity: f})) + }, this); + var p, g, m = l.getShallow("show"), v = u.getShallow("show"), y = this.childOfName("label"); + if ((m || v) && (p = c || "#000", null == (g = n.getFormattedLabel(e, "normal", t.dataType)))) { + var x = n.getRawValue(e); + g = null == x ? t.getName(e) : isFinite(x) ? Ao(x) : x + } + var _ = m ? g : null, w = v ? D(n.getFormattedLabel(e, "emphasis", t.dataType), g) : null, b = y.style; + null == _ && null == w || (no(y.style, l, {text: _}, {autoColor: p}), y.__textAlign = b.textAlign, y.__verticalAlign = b.textVerticalAlign, y.__position = l.get("position") || "middle"), y.hoverStyle = null != w ? { + text: w, + textFill: u.getTextColor(!0), + fontStyle: u.getShallow("fontStyle"), + fontWeight: u.getShallow("fontWeight"), + fontSize: u.getShallow("fontSize"), + fontFamily: u.getShallow("fontFamily") + } : {text: null}, y.ignore = !m && !v, eo(this) + }, pC.highlight = function () { + this.trigger("emphasis") + }, pC.downplay = function () { + this.trigger("normal") + }, pC.updateLayout = function (t, e) { + this.setLinePoints(t.getItemLayout(e)) + }, pC.setLinePoints = function (t) { + var e = this.childOfName("line"); + wd(e.shape, t), e.dirty() + }, u(bd, sw); + var gC = Sd.prototype; + gC.isPersistent = function () { + return !0 + }, gC.updateData = function (t) { + var e = this, i = e.group, n = e._lineData; + e._lineData = t, n || i.removeAll(); + var o = Td(t); + t.diff(n).add(function (i) { + Md(e, t, i, o) + }).update(function (i, a) { + Id(e, n, t, a, i, o) + }).remove(function (t) { + i.remove(n.getItemGraphicEl(t)) + }).execute() + }, gC.updateLayout = function () { + var t = this._lineData; + t && t.eachItemGraphicEl(function (e, i) { + e.updateLayout(t, i) + }, this) + }, gC.incrementalPrepareUpdate = function (t) { + this._seriesScope = Td(t), this._lineData = null, this.group.removeAll() + }, gC.incrementalUpdate = function (t, e) { + for (var i = t.start; i < t.end; i++) if (Ad(e.getItemLayout(i))) { + var n = new this._ctor(e, i, this._seriesScope); + n.traverse(function (t) { + t.isGroup || (t.incremental = t.useHoverLayer = !0) + }), this.group.add(n), e.setItemGraphicEl(i, n) + } + }, gC.remove = function () { + this._clearIncremental(), this._incremental = null, this.group.removeAll() + }, gC._clearIncremental = function () { + var t = this._incremental; + t && t.clearDisplaybles() + }; + var mC = [], vC = [], yC = [], xC = Qi, _C = M_, wC = Math.abs, bC = function (t, e) { + function i(t) { + var e = t.getVisual("symbolSize"); + return e instanceof Array && (e = (e[0] + e[1]) / 2), e + } + + var n = [], o = on, a = [[], [], []], r = [[], []], s = []; + e /= 2, t.eachEdge(function (t, l) { + var u = t.getLayout(), h = t.getVisual("fromSymbol"), c = t.getVisual("toSymbol"); + u.__original || (u.__original = [W(u[0]), W(u[1])], u[2] && u.__original.push(W(u[2]))); + var d = u.__original; + if (null != u[2]) { + if (G(a[0], d[0]), G(a[1], d[2]), G(a[2], d[1]), h && "none" != h) { + var f = i(t.node1), p = Cd(a, d[0], f * e); + o(a[0][0], a[1][0], a[2][0], p, n), a[0][0] = n[3], a[1][0] = n[4], o(a[0][1], a[1][1], a[2][1], p, n), a[0][1] = n[3], a[1][1] = n[4] + } + if (c && "none" != c) { + var f = i(t.node2), p = Cd(a, d[1], f * e); + o(a[0][0], a[1][0], a[2][0], p, n), a[1][0] = n[1], a[2][0] = n[2], o(a[0][1], a[1][1], a[2][1], p, n), a[1][1] = n[1], a[2][1] = n[2] + } + G(u[0], a[0]), G(u[1], a[2]), G(u[2], a[1]) + } else { + if (G(r[0], d[0]), G(r[1], d[1]), U(s, r[1], r[0]), q(s, s), h && "none" != h) { + f = i(t.node1); + Z(r[0], r[0], s, f * e) + } + if (c && "none" != c) { + f = i(t.node2); + Z(r[1], r[1], s, -f * e) + } + G(u[0], r[0]), G(u[1], r[1]) + } + }) + }, SC = ["itemStyle", "opacity"], MC = ["lineStyle", "opacity"]; + _s({ + type: "graph", init: function (t, e) { + var i = new Ql, n = new Sd, o = this.group; + this._controller = new kh(e.getZr()), this._controllerHost = {target: o}, o.add(i.group), o.add(n.group), this._symbolDraw = i, this._lineDraw = n, this._firstRender = !0 + }, render: function (t, e, i) { + var n = t.coordinateSystem; + this._model = t, this._nodeScaleRatio = t.get("nodeScaleRatio"); + var o = this._symbolDraw, a = this._lineDraw, r = this.group; + if ("view" === n.type) { + var s = {position: n.position, scale: n.scale}; + this._firstRender ? r.attr(s) : fo(r, s, t) + } + bC(t.getGraph(), this._getNodeGlobalScale(t)); + var l = t.getData(); + o.updateData(l); + var u = t.getEdgeData(); + a.updateData(u), this._updateNodeAndLinkScale(), this._updateController(t, e, i), clearTimeout(this._layoutTimeout); + var h = t.forceLayout, c = t.get("force.layoutAnimation"); + h && this._startForceLayoutIteration(h, c), l.eachItemGraphicEl(function (e, n) { + var o = l.getItemModel(n); + e.off("drag").off("dragend"); + var a = l.getItemModel(n).get("draggable"); + a && e.on("drag", function () { + h && (h.warmUp(), !this._layouting && this._startForceLayoutIteration(h, c), h.setFixed(n), l.setItemLayout(n, e.position)) + }, this).on("dragend", function () { + h && h.setUnfixed(n) + }, this), e.setDraggable(a && h), e.off("mouseover", e.__focusNodeAdjacency), e.off("mouseout", e.__unfocusNodeAdjacency), o.get("focusNodeAdjacency") && (e.on("mouseover", e.__focusNodeAdjacency = function () { + i.dispatchAction({type: "focusNodeAdjacency", seriesId: t.id, dataIndex: e.dataIndex}) + }), e.on("mouseout", e.__unfocusNodeAdjacency = function () { + i.dispatchAction({type: "unfocusNodeAdjacency", seriesId: t.id}) + })) + }, this), l.graph.eachEdge(function (e) { + var n = e.getGraphicEl(); + n.off("mouseover", n.__focusNodeAdjacency), n.off("mouseout", n.__unfocusNodeAdjacency), e.getModel().get("focusNodeAdjacency") && (n.on("mouseover", n.__focusNodeAdjacency = function () { + i.dispatchAction({type: "focusNodeAdjacency", seriesId: t.id, edgeDataIndex: e.dataIndex}) + }), n.on("mouseout", n.__unfocusNodeAdjacency = function () { + i.dispatchAction({type: "unfocusNodeAdjacency", seriesId: t.id}) + })) + }); + var d = "circular" === t.get("layout") && t.get("circular.rotateLabel"), f = l.getLayout("cx"), + p = l.getLayout("cy"); + l.eachItemGraphicEl(function (t, e) { + var i = t.getSymbolPath(); + if (d) { + var n = l.getItemLayout(e), o = Math.atan2(n[1] - p, n[0] - f); + o < 0 && (o = 2 * Math.PI + o); + var a = n[0] < f; + a && (o -= Math.PI); + var r = a ? "left" : "right"; + i.setStyle({ + textRotation: -o, + textPosition: r, + textOrigin: "center" + }), i.hoverStyle && (i.hoverStyle.textPosition = r) + } else i.setStyle({textRotation: 0}) + }), this._firstRender = !1 + }, dispose: function () { + this._controller && this._controller.dispose(), this._controllerHost = {} + }, focusNodeAdjacency: function (t, e, i, n) { + var o = this._model.getData().graph, a = n.dataIndex, r = n.edgeDataIndex, s = o.getNodeByIndex(a), + l = o.getEdgeByIndex(r); + (s || l) && (o.eachNode(function (t) { + kd(t, SC, .1) + }), o.eachEdge(function (t) { + kd(t, MC, .1) + }), s && (Pd(s, SC), d(s.edges, function (t) { + t.dataIndex < 0 || (Pd(t, MC), Pd(t.node1, SC), Pd(t.node2, SC)) + })), l && (Pd(l, MC), Pd(l.node1, SC), Pd(l.node2, SC))) + }, unfocusNodeAdjacency: function (t, e, i, n) { + var o = this._model.getData().graph; + o.eachNode(function (t) { + kd(t, SC) + }), o.eachEdge(function (t) { + kd(t, MC) + }) + }, _startForceLayoutIteration: function (t, e) { + var i = this; + !function n() { + t.step(function (t) { + i.updateLayout(i._model), (i._layouting = !t) && (e ? i._layoutTimeout = setTimeout(n, 16) : n()) + }) + }() + }, _updateController: function (t, e, i) { + var n = this._controller, o = this._controllerHost, a = this.group; + n.setPointerChecker(function (e, n, o) { + var r = a.getBoundingRect(); + return r.applyTransform(a.transform), r.contain(n, o) && !Wh(e, i, t) + }), "view" === t.coordinateSystem.type ? (n.enable(t.get("roam")), o.zoomLimit = t.get("scaleLimit"), o.zoom = t.coordinateSystem.getZoom(), n.off("pan").off("zoom").on("pan", function (e, n) { + Vh(o, e, n), i.dispatchAction({seriesId: t.id, type: "graphRoam", dx: e, dy: n}) + }).on("zoom", function (e, n, a) { + Gh(o, e, n, a), i.dispatchAction({ + seriesId: t.id, + type: "graphRoam", + zoom: e, + originX: n, + originY: a + }), this._updateNodeAndLinkScale(), bC(t.getGraph(), this._getNodeGlobalScale(t)), this._lineDraw.updateLayout() + }, this)) : n.disable() + }, _updateNodeAndLinkScale: function () { + var t = this._model, e = t.getData(), i = this._getNodeGlobalScale(t), n = [i, i]; + e.eachItemGraphicEl(function (t, e) { + t.attr("scale", n) + }) + }, _getNodeGlobalScale: function (t) { + var e = t.coordinateSystem; + if ("view" !== e.type) return 1; + var i = this._nodeScaleRatio, n = e.scale, o = n && n[0] || 1; + return ((e.getZoom() - 1) * i + 1) / o + }, updateLayout: function (t) { + bC(t.getGraph(), this._getNodeGlobalScale(t)), this._symbolDraw.updateLayout(), this._lineDraw.updateLayout() + }, remove: function (t, e) { + this._symbolDraw && this._symbolDraw.remove(), this._lineDraw && this._lineDraw.remove() + } + }), cs({type: "graphRoam", event: "graphRoam", update: "none"}, function (t, e) { + e.eachComponent({mainType: "series", query: t}, function (e) { + var i = Xh(e.coordinateSystem, t); + e.setCenter && e.setCenter(i.center), e.setZoom && e.setZoom(i.zoom) + }) + }), cs({ + type: "focusNodeAdjacency", + event: "focusNodeAdjacency", + update: "series.graph:focusNodeAdjacency" + }, function () { + }), cs({ + type: "unfocusNodeAdjacency", + event: "unfocusNodeAdjacency", + update: "series.graph:unfocusNodeAdjacency" + }, function () { + }); + var IC = Z; + hs(function (t) { + var e = t.findComponents({mainType: "legend"}); + e && e.length && t.eachSeriesByType("graph", function (t) { + var i = t.getCategoriesData(), n = t.getGraph().data, o = i.mapArray(i.getName); + n.filterSelf(function (t) { + var i = n.getItemModel(t).getShallow("category"); + if (null != i) { + "number" == typeof i && (i = o[i]); + for (var a = 0; a < e.length; a++) if (!e[a].isSelected(i)) return !1 + } + return !0 + }) + }, this) + }), ps(gD("graph", "circle", null)), ps(function (t) { + var e = {}; + t.eachSeriesByType("graph", function (t) { + var i = t.getCategoriesData(), n = t.getData(), o = {}; + i.each(function (n) { + var a = i.getName(n); + o["ec-" + a] = n; + var r = i.getItemModel(n).get("itemStyle.color") || t.getColorFromPalette(a, e); + i.setItemVisual(n, "color", r) + }), i.count() && n.each(function (t) { + var e = n.getItemModel(t).getShallow("category"); + null != e && ("string" == typeof e && (e = o["ec-" + e]), n.getItemVisual(t, "color", !0) || n.setItemVisual(t, "color", i.getItemVisual(e, "color"))) + }) + }) + }), ps(function (t) { + t.eachSeriesByType("graph", function (t) { + var e = t.getGraph(), i = t.getEdgeData(), n = Nd(t.get("edgeSymbol")), o = Nd(t.get("edgeSymbolSize")), + a = "lineStyle.color".split("."), r = "lineStyle.opacity".split("."); + i.setVisual("fromSymbol", n && n[0]), i.setVisual("toSymbol", n && n[1]), i.setVisual("fromSymbolSize", o && o[0]), i.setVisual("toSymbolSize", o && o[1]), i.setVisual("color", t.get(a)), i.setVisual("opacity", t.get(r)), i.each(function (t) { + var n = i.getItemModel(t), o = e.getEdgeByIndex(t), s = Nd(n.getShallow("symbol", !0)), + l = Nd(n.getShallow("symbolSize", !0)), u = n.get(a), h = n.get(r); + switch (u) { + case"source": + u = o.node1.getVisual("color"); + break; + case"target": + u = o.node2.getVisual("color") + } + s[0] && o.setVisual("fromSymbol", s[0]), s[1] && o.setVisual("toSymbol", s[1]), l[0] && o.setVisual("fromSymbolSize", l[0]), l[1] && o.setVisual("toSymbolSize", l[1]), o.setVisual("color", u), o.setVisual("opacity", h) + }) + }) + }), fs(function (t, e) { + t.eachSeriesByType("graph", function (t) { + var e = t.get("layout"), i = t.coordinateSystem; + if (i && "view" !== i.type) { + var n = t.getData(), o = []; + d(i.dimensions, function (t) { + o = o.concat(n.mapDimension(t, !0)) + }); + for (var a = 0; a < n.count(); a++) { + for (var r = [], s = !1, l = 0; l < o.length; l++) { + var u = n.get(o[l], a); + isNaN(u) || (s = !0), r.push(u) + } + s ? n.setItemLayout(a, i.dataToPoint(r)) : n.setItemLayout(a, [NaN, NaN]) + } + Ed(n.graph) + } else e && "none" !== e || Od(t) + }) + }), fs(function (t) { + t.eachSeriesByType("graph", function (t) { + "circular" === t.get("layout") && Rd(t) + }) + }), fs(function (t) { + t.eachSeriesByType("graph", function (t) { + var e = t.coordinateSystem; + if (!e || "view" === e.type) if ("force" === t.get("layout")) { + var i = t.preservedPoints || {}, n = t.getGraph(), o = n.data, a = n.edgeData, r = t.getModel("force"), + s = r.get("initLayout"); + t.preservedPoints ? o.each(function (t) { + var e = o.getId(t); + o.setItemLayout(t, i[e] || [NaN, NaN]) + }) : s && "none" !== s ? "circular" === s && Rd(t) : Od(t); + var l = o.getDataExtent("value"), u = a.getDataExtent("value"), h = r.get("repulsion"), + c = r.get("edgeLength"); + y(h) || (h = [h, h]), y(c) || (c = [c, c]), c = [c[1], c[0]]; + var d = o.mapArray("value", function (t, e) { + var i = o.getItemLayout(e), n = To(t, l, h); + return isNaN(n) && (n = (h[0] + h[1]) / 2), { + w: n, + rep: n, + fixed: o.getItemModel(e).get("fixed"), + p: !i || isNaN(i[0]) || isNaN(i[1]) ? null : i + } + }), f = a.mapArray("value", function (t, e) { + var i = n.getEdgeByIndex(e), o = To(t, u, c); + return isNaN(o) && (o = (c[0] + c[1]) / 2), { + n1: d[i.node1.dataIndex], + n2: d[i.node2.dataIndex], + d: o, + curveness: i.getModel().get("lineStyle.curveness") || 0 + } + }), p = (e = t.coordinateSystem).getBoundingRect(), g = zd(d, f, {rect: p, gravity: r.get("gravity")}), + m = g.step; + g.step = function (t) { + for (var e = 0, a = d.length; e < a; e++) d[e].fixed && G(d[e].p, n.getNodeByIndex(e).getLayout()); + m(function (e, a, r) { + for (var s = 0, l = e.length; s < l; s++) e[s].fixed || n.getNodeByIndex(s).setLayout(e[s].p), i[o.getId(s)] = e[s].p; + for (var s = 0, l = a.length; s < l; s++) { + var u = a[s], h = n.getEdgeByIndex(s), c = u.n1.p, d = u.n2.p, f = h.getLayout(); + (f = f ? f.slice() : [])[0] = f[0] || [], f[1] = f[1] || [], G(f[0], c), G(f[1], d), +u.curveness && (f[2] = [(c[0] + d[0]) / 2 - (c[1] - d[1]) * u.curveness, (c[1] + d[1]) / 2 - (d[0] - c[0]) * u.curveness]), h.setLayout(f) + } + t && t(r) + }) + }, t.forceLayout = g, t.preservedPoints = i, g.step() + } else t.forceLayout = null + }) + }), ds("graphView", { + create: function (t, e) { + var i = []; + return t.eachSeriesByType("graph", function (t) { + var n = t.get("coordinateSystem"); + if (!n || "view" === n) { + var o = t.getData(), a = [], r = []; + rn(o.mapArray(function (t) { + var e = o.getItemModel(t); + return [+e.get("x"), +e.get("y")] + }), a, r), r[0] - a[0] == 0 && (r[0] += 1, a[0] -= 1), r[1] - a[1] == 0 && (r[1] += 1, a[1] -= 1); + var s = (r[0] - a[0]) / (r[1] - a[1]), l = Bd(t, e, s); + isNaN(s) && (a = [l.x, l.y], r = [l.x + l.width, l.y + l.height]); + var u = r[0] - a[0], h = r[1] - a[1], c = l.width, d = l.height, f = t.coordinateSystem = new wh; + f.zoomLimit = t.get("scaleLimit"), f.setBoundingRect(a[0], a[1], u, h), f.setViewRect(l.x, l.y, c, d), f.setCenter(t.get("center")), f.setZoom(t.get("zoom")), i.push(f) + } + }), i + } + }); + KM.extend({ + type: "series.gauge", + getInitialData: function (t, e) { + var i = t.data || []; + return y(i) || (i = [i]), t.data = i, jD(this, ["value"]) + }, + defaultOption: { + zlevel: 0, + z: 2, + center: ["50%", "50%"], + legendHoverLink: !0, + radius: "75%", + startAngle: 225, + endAngle: -45, + clockwise: !0, + min: 0, + max: 100, + splitNumber: 10, + axisLine: {show: !0, lineStyle: {color: [[.2, "#91c7ae"], [.8, "#63869e"], [1, "#c23531"]], width: 30}}, + splitLine: {show: !0, length: 30, lineStyle: {color: "#eee", width: 2, type: "solid"}}, + axisTick: {show: !0, splitNumber: 5, length: 8, lineStyle: {color: "#eee", width: 1, type: "solid"}}, + axisLabel: {show: !0, distance: 5, color: "auto"}, + pointer: {show: !0, length: "80%", width: 8}, + itemStyle: {color: "auto"}, + title: {show: !0, offsetCenter: [0, "-40%"], color: "#333", fontSize: 15}, + detail: { + show: !0, + backgroundColor: "rgba(0,0,0,0)", + borderWidth: 0, + borderColor: "#ccc", + width: 100, + height: null, + padding: [5, 10], + offsetCenter: [0, "40%"], + color: "auto", + fontSize: 30 + } + } + }); + var TC = In.extend({ + type: "echartsGaugePointer", + shape: {angle: 0, width: 10, r: 10, x: 0, y: 0}, + buildPath: function (t, e) { + var i = Math.cos, n = Math.sin, o = e.r, a = e.width, r = e.angle, + s = e.x - i(r) * a * (a >= o / 3 ? 1 : 2), l = e.y - n(r) * a * (a >= o / 3 ? 1 : 2); + r = e.angle - Math.PI / 2, t.moveTo(s, l), t.lineTo(e.x + i(r) * a, e.y + n(r) * a), t.lineTo(e.x + i(e.angle) * o, e.y + n(e.angle) * o), t.lineTo(e.x - i(r) * a, e.y - n(r) * a), t.lineTo(s, l) + } + }), DC = 2 * Math.PI, AC = (gr.extend({ + type: "gauge", render: function (t, e, i) { + this.group.removeAll(); + var n = t.get("axisLine.lineStyle.color"), o = Vd(t, i); + this._renderMain(t, e, i, n, o) + }, dispose: function () { + }, _renderMain: function (t, e, i, n, o) { + for (var a = this.group, r = t.getModel("axisLine").getModel("lineStyle"), s = t.get("clockwise"), l = -t.get("startAngle") / 180 * Math.PI, u = -t.get("endAngle") / 180 * Math.PI, h = (u - l) % DC, c = l, d = r.get("width"), f = 0; f < n.length; f++) { + var p = Math.min(Math.max(n[f][0], 0), 1), g = new vS({ + shape: { + startAngle: c, + endAngle: u = l + h * p, + cx: o.cx, + cy: o.cy, + clockwise: s, + r0: o.r - d, + r: o.r + }, silent: !0 + }); + g.setStyle({fill: n[f][1]}), g.setStyle(r.getLineStyle(["color", "borderWidth", "borderColor"])), a.add(g), c = u + } + var m = function (t) { + if (t <= 0) return n[0][1]; + for (var e = 0; e < n.length; e++) if (n[e][0] >= t && (0 === e ? 0 : n[e - 1][0]) < t) return n[e][1]; + return n[e - 1][1] + }; + if (!s) { + var v = l; + l = u, u = v + } + this._renderTicks(t, e, i, m, o, l, u, s), this._renderPointer(t, e, i, m, o, l, u, s), this._renderTitle(t, e, i, m, o), this._renderDetail(t, e, i, m, o) + }, _renderTicks: function (t, e, i, n, o, a, r, s) { + for (var l = this.group, u = o.cx, h = o.cy, c = o.r, d = +t.get("min"), f = +t.get("max"), p = t.getModel("splitLine"), g = t.getModel("axisTick"), m = t.getModel("axisLabel"), v = t.get("splitNumber"), y = g.get("splitNumber"), x = Do(p.get("length"), c), _ = Do(g.get("length"), c), w = a, b = (r - a) / v, S = b / y, M = p.getModel("lineStyle").getLineStyle(), I = g.getModel("lineStyle").getLineStyle(), T = 0; T <= v; T++) { + var D = Math.cos(w), A = Math.sin(w); + if (p.get("show")) { + var C = new MS({ + shape: {x1: D * c + u, y1: A * c + h, x2: D * (c - x) + u, y2: A * (c - x) + h}, + style: M, + silent: !0 + }); + "auto" === M.stroke && C.setStyle({stroke: n(T / v)}), l.add(C) + } + if (m.get("show")) { + var L = Gd(Ao(T / v * (f - d) + d), m.get("formatter")), k = m.get("distance"), P = n(T / v); + l.add(new fS({ + style: no({}, m, { + text: L, + x: D * (c - x - k) + u, + y: A * (c - x - k) + h, + textVerticalAlign: A < -.4 ? "top" : A > .4 ? "bottom" : "middle", + textAlign: D < -.4 ? "left" : D > .4 ? "right" : "center" + }, {autoColor: P}), silent: !0 + })) + } + if (g.get("show") && T !== v) { + for (var N = 0; N <= y; N++) { + var D = Math.cos(w), A = Math.sin(w), O = new MS({ + shape: {x1: D * c + u, y1: A * c + h, x2: D * (c - _) + u, y2: A * (c - _) + h}, + silent: !0, + style: I + }); + "auto" === I.stroke && O.setStyle({stroke: n((T + N / y) / v)}), l.add(O), w += S + } + w -= S + } else w += b + } + }, _renderPointer: function (t, e, i, n, o, a, r, s) { + var l = this.group, u = this._data; + if (t.get("pointer.show")) { + var h = [+t.get("min"), +t.get("max")], c = [a, r], d = t.getData(), f = d.mapDimension("value"); + d.diff(u).add(function (e) { + var i = new TC({shape: {angle: a}}); + po(i, {shape: {angle: To(d.get(f, e), h, c, !0)}}, t), l.add(i), d.setItemGraphicEl(e, i) + }).update(function (e, i) { + var n = u.getItemGraphicEl(i); + fo(n, {shape: {angle: To(d.get(f, e), h, c, !0)}}, t), l.add(n), d.setItemGraphicEl(e, n) + }).remove(function (t) { + var e = u.getItemGraphicEl(t); + l.remove(e) + }).execute(), d.eachItemGraphicEl(function (t, e) { + var i = d.getItemModel(e), a = i.getModel("pointer"); + t.setShape({ + x: o.cx, + y: o.cy, + width: Do(a.get("width"), o.r), + r: Do(a.get("length"), o.r) + }), t.useStyle(i.getModel("itemStyle").getItemStyle()), "auto" === t.style.fill && t.setStyle("fill", n(To(d.get(f, e), h, [0, 1], !0))), eo(t, i.getModel("emphasis.itemStyle").getItemStyle()) + }), this._data = d + } else u && u.eachItemGraphicEl(function (t) { + l.remove(t) + }) + }, _renderTitle: function (t, e, i, n, o) { + var a = t.getData(), r = a.mapDimension("value"), s = t.getModel("title"); + if (s.get("show")) { + var l = s.get("offsetCenter"), u = o.cx + Do(l[0], o.r), h = o.cy + Do(l[1], o.r), c = +t.get("min"), + d = +t.get("max"), f = n(To(t.getData().get(r, 0), [c, d], [0, 1], !0)); + this.group.add(new fS({ + silent: !0, + style: no({}, s, { + x: u, + y: h, + text: a.getName(0), + textAlign: "center", + textVerticalAlign: "middle" + }, {autoColor: f, forceRich: !0}) + })) + } + }, _renderDetail: function (t, e, i, n, o) { + var a = t.getModel("detail"), r = +t.get("min"), s = +t.get("max"); + if (a.get("show")) { + var l = a.get("offsetCenter"), u = o.cx + Do(l[0], o.r), h = o.cy + Do(l[1], o.r), + c = Do(a.get("width"), o.r), d = Do(a.get("height"), o.r), f = t.getData(), + p = f.get(f.mapDimension("value"), 0), g = n(To(p, [r, s], [0, 1], !0)); + this.group.add(new fS({ + silent: !0, + style: no({}, a, { + x: u, + y: h, + text: Gd(p, a.get("formatter")), + textWidth: isNaN(c) ? null : c, + textHeight: isNaN(d) ? null : d, + textAlign: "center", + textVerticalAlign: "middle" + }, {autoColor: g, forceRich: !0}) + })) + } + } + }), xs({ + type: "series.funnel", + init: function (t) { + AC.superApply(this, "init", arguments), this.legendDataProvider = function () { + return this.getRawData() + }, this._defaultLabelLine(t) + }, + getInitialData: function (t, e) { + return jD(this, ["value"]) + }, + _defaultLabelLine: function (t) { + Mi(t, "labelLine", ["show"]); + var e = t.labelLine, i = t.emphasis.labelLine; + e.show = e.show && t.label.show, i.show = i.show && t.emphasis.label.show + }, + getDataParams: function (t) { + var e = this.getData(), i = AC.superCall(this, "getDataParams", t), n = e.mapDimension("value"), + o = e.getSum(n); + return i.percent = o ? +(e.get(n, t) / o * 100).toFixed(2) : 0, i.$vars.push("percent"), i + }, + defaultOption: { + zlevel: 0, + z: 2, + legendHoverLink: !0, + left: 80, + top: 60, + right: 80, + bottom: 60, + minSize: "0%", + maxSize: "100%", + sort: "descending", + gap: 0, + funnelAlign: "center", + label: {show: !0, position: "outer"}, + labelLine: {show: !0, length: 20, lineStyle: {width: 1, type: "solid"}}, + itemStyle: {borderColor: "#fff", borderWidth: 1}, + emphasis: {label: {show: !0}} + } + })), CC = Wd.prototype, LC = ["itemStyle", "opacity"]; + CC.updateData = function (t, e, i) { + var n = this.childAt(0), o = t.hostModel, a = t.getItemModel(e), s = t.getItemLayout(e), + l = t.getItemModel(e).get(LC); + l = null == l ? 1 : l, n.useStyle({}), i ? (n.setShape({points: s.points}), n.setStyle({opacity: 0}), po(n, {style: {opacity: l}}, o, e)) : fo(n, { + style: {opacity: l}, + shape: {points: s.points} + }, o, e); + var u = a.getModel("itemStyle"), h = t.getItemVisual(e, "color"); + n.setStyle(r({ + lineJoin: "round", + fill: h + }, u.getItemStyle(["opacity"]))), n.hoverStyle = u.getModel("emphasis").getItemStyle(), this._updateLabel(t, e), eo(this) + }, CC._updateLabel = function (t, e) { + var i = this.childAt(1), n = this.childAt(2), o = t.hostModel, a = t.getItemModel(e), + r = t.getItemLayout(e).label, s = t.getItemVisual(e, "color"); + fo(i, {shape: {points: r.linePoints || r.linePoints}}, o, e), fo(n, { + style: { + x: r.x, + y: r.y + } + }, o, e), n.attr({rotation: r.rotation, origin: [r.x, r.y], z2: 10}); + var l = a.getModel("label"), u = a.getModel("emphasis.label"), h = a.getModel("labelLine"), + c = a.getModel("emphasis.labelLine"), s = t.getItemVisual(e, "color"); + io(n.style, n.hoverStyle = {}, l, u, { + labelFetcher: t.hostModel, + labelDataIndex: e, + defaultText: t.getName(e), + autoColor: s, + useInsideStyle: !!r.inside + }, { + textAlign: r.textAlign, + textVerticalAlign: r.verticalAlign + }), n.ignore = n.normalIgnore = !l.get("show"), n.hoverIgnore = !u.get("show"), i.ignore = i.normalIgnore = !h.get("show"), i.hoverIgnore = !c.get("show"), i.setStyle({stroke: s}), i.setStyle(h.getModel("lineStyle").getLineStyle()), i.hoverStyle = c.getModel("lineStyle").getLineStyle() + }, u(Wd, sw); + gr.extend({ + type: "funnel", render: function (t, e, i) { + var n = t.getData(), o = this._data, a = this.group; + n.diff(o).add(function (t) { + var e = new Wd(n, t); + n.setItemGraphicEl(t, e), a.add(e) + }).update(function (t, e) { + var i = o.getItemGraphicEl(e); + i.updateData(n, t), a.add(i), n.setItemGraphicEl(t, i) + }).remove(function (t) { + var e = o.getItemGraphicEl(t); + a.remove(e) + }).execute(), this._data = n + }, remove: function () { + this.group.removeAll(), this._data = null + }, dispose: function () { + } + }); + ps(JD("funnel")), fs(function (t, e, i) { + t.eachSeriesByType("funnel", function (t) { + var i = t.getData(), n = i.mapDimension("value"), o = t.get("sort"), a = Fd(t, e), r = Hd(i, o), + s = [Do(t.get("minSize"), a.width), Do(t.get("maxSize"), a.width)], l = i.getDataExtent(n), + u = t.get("min"), h = t.get("max"); + null == u && (u = Math.min(l[0], 0)), null == h && (h = l[1]); + var c = t.get("funnelAlign"), d = t.get("gap"), f = (a.height - d * (i.count() - 1)) / i.count(), p = a.y, + g = function (t, e) { + var o, r = To(i.get(n, t) || 0, [u, h], s, !0); + switch (c) { + case"left": + o = a.x; + break; + case"center": + o = a.x + (a.width - r) / 2; + break; + case"right": + o = a.x + a.width - r + } + return [[o, e], [o + r, e]] + }; + "ascending" === o && (f = -f, d = -d, p += a.height, r = r.reverse()); + for (var m = 0; m < r.length; m++) { + var v = r[m], y = r[m + 1], x = i.getItemModel(v).get("itemStyle.height"); + null == x ? x = f : (x = Do(x, a.height), "ascending" === o && (x = -x)); + var _ = g(v, p), w = g(y, p + x); + p += x + d, i.setItemLayout(v, {points: _.concat(w.slice().reverse())}) + } + Zd(i) + }) + }), hs(iA("funnel")); + var kC = function (t, e, i, n, o) { + qT.call(this, t, e, i), this.type = n || "value", this.axisIndex = o + }; + kC.prototype = { + constructor: kC, model: null, isHorizontal: function () { + return "horizontal" !== this.coordinateSystem.getModel().get("layout") + } + }, u(kC, qT); + var PC = function (t, e, i, n, o, a) { + e[0] = Yd(e[0], i), e[1] = Yd(e[1], i), t = t || 0; + var r = i[1] - i[0]; + null != o && (o = Yd(o, [0, r])), null != a && (a = Math.max(a, null != o ? o : 0)), "all" === n && (o = a = Math.abs(e[1] - e[0]), n = 0); + var s = jd(e, n); + e[n] += t; + var l = o || 0, u = i.slice(); + s.sign < 0 ? u[0] += l : u[1] -= l, e[n] = Yd(e[n], u); + h = jd(e, n); + null != o && (h.sign !== s.sign || h.span < o) && (e[1 - n] = e[n] + s.sign * o); + var h = jd(e, n); + return null != a && h.span > a && (e[1 - n] = e[n] + h.sign * a), e + }, NC = d, OC = Math.min, EC = Math.max, RC = Math.floor, zC = Math.ceil, BC = Ao, VC = Math.PI; + qd.prototype = { + type: "parallel", constructor: qd, _init: function (t, e, i) { + var n = t.dimensions, o = t.parallelAxisIndex; + NC(n, function (t, i) { + var n = o[i], a = e.getComponent("parallelAxis", n), + r = this._axesMap.set(t, new kC(t, xl(a), [0, 0], a.get("type"), n)), s = "category" === r.type; + r.onBand = s && a.get("boundaryGap"), r.inverse = a.get("inverse"), a.axis = r, r.model = a, r.coordinateSystem = a.coordinateSystem = this + }, this) + }, update: function (t, e) { + this._updateAxesFromSeries(this._model, t) + }, containPoint: function (t) { + var e = this._makeLayoutInfo(), i = e.axisBase, n = e.layoutBase, o = e.pixelDimIndex, a = t[1 - o], + r = t[o]; + return a >= i && a <= i + e.axisLength && r >= n && r <= n + e.layoutLength + }, getModel: function () { + return this._model + }, _updateAxesFromSeries: function (t, e) { + e.eachSeries(function (i) { + if (t.contains(i, e)) { + var n = i.getData(); + NC(this.dimensions, function (t) { + var e = this._axesMap.get(t); + e.scale.unionExtentFromData(n, n.mapDimension(t)), yl(e.scale, e.model) + }, this) + } + }, this) + }, resize: function (t, e) { + this._rect = Qo(t.getBoxLayoutParams(), {width: e.getWidth(), height: e.getHeight()}), this._layoutAxes() + }, getRect: function () { + return this._rect + }, _makeLayoutInfo: function () { + var t, e = this._model, i = this._rect, n = ["x", "y"], o = ["width", "height"], a = e.get("layout"), + r = "horizontal" === a ? 0 : 1, s = i[o[r]], l = [0, s], u = this.dimensions.length, + h = Kd(e.get("axisExpandWidth"), l), c = Kd(e.get("axisExpandCount") || 0, [0, u]), + d = e.get("axisExpandable") && u > 3 && u > c && c > 1 && h > 0 && s > 0, f = e.get("axisExpandWindow"); + f ? (t = Kd(f[1] - f[0], l), f[1] = f[0] + t) : (t = Kd(h * (c - 1), l), (f = [h * (e.get("axisExpandCenter") || RC(u / 2)) - t / 2])[1] = f[0] + t); + var p = (s - t) / (u - c); + p < 3 && (p = 0); + var g = [RC(BC(f[0] / h, 1)) + 1, zC(BC(f[1] / h, 1)) - 1], m = p / h * f[0]; + return { + layout: a, + pixelDimIndex: r, + layoutBase: i[n[r]], + layoutLength: s, + axisBase: i[n[1 - r]], + axisLength: i[o[1 - r]], + axisExpandable: d, + axisExpandWidth: h, + axisCollapseWidth: p, + axisExpandWindow: f, + axisCount: u, + winInnerIndices: g, + axisExpandWindow0Pos: m + } + }, _layoutAxes: function () { + var t = this._rect, e = this._axesMap, i = this.dimensions, n = this._makeLayoutInfo(), o = n.layout; + e.each(function (t) { + var e = [0, n.axisLength], i = t.inverse ? 1 : 0; + t.setExtent(e[i], e[1 - i]) + }), NC(i, function (e, i) { + var a = (n.axisExpandable ? Jd : $d)(i, n), + r = {horizontal: {x: a.position, y: n.axisLength}, vertical: {x: 0, y: a.position}}, + s = {horizontal: VC / 2, vertical: 0}, l = [r[o].x + t.x, r[o].y + t.y], u = s[o], h = st(); + dt(h, h, u), ct(h, h, l), this._axesLayout[e] = { + position: l, + rotation: u, + transform: h, + axisNameAvailableWidth: a.axisNameAvailableWidth, + axisLabelShow: a.axisLabelShow, + nameTruncateMaxWidth: a.nameTruncateMaxWidth, + tickDirection: 1, + labelDirection: 1 + } + }, this) + }, getAxis: function (t) { + return this._axesMap.get(t) + }, dataToPoint: function (t, e) { + return this.axisCoordToPoint(this._axesMap.get(e).dataToCoord(t), e) + }, eachActiveState: function (t, e, i, n) { + null == i && (i = 0), null == n && (n = t.count()); + var o = this._axesMap, a = this.dimensions, r = [], s = []; + d(a, function (e) { + r.push(t.mapDimension(e)), s.push(o.get(e).model) + }); + for (var l = this.hasAxisBrushed(), u = i; u < n; u++) { + var h; + if (l) { + h = "active"; + for (var c = t.getValues(r, u), f = 0, p = a.length; f < p; f++) if ("inactive" === s[f].getActiveState(c[f])) { + h = "inactive"; + break + } + } else h = "normal"; + e(h, u) + } + }, hasAxisBrushed: function () { + for (var t = this.dimensions, e = this._axesMap, i = !1, n = 0, o = t.length; n < o; n++) "normal" !== e.get(t[n]).model.getActiveState() && (i = !0); + return i + }, axisCoordToPoint: function (t, e) { + return mo([t, 0], this._axesLayout[e].transform) + }, getAxisLayout: function (t) { + return i(this._axesLayout[t]) + }, getSlidedAxisExpandWindow: function (t) { + var e = this._makeLayoutInfo(), i = e.pixelDimIndex, n = e.axisExpandWindow.slice(), o = n[1] - n[0], + a = [0, e.axisExpandWidth * (e.axisCount - 1)]; + if (!this.containPoint(t)) return {behavior: "none", axisExpandWindow: n}; + var r, s = t[i] - e.layoutBase - e.axisExpandWindow0Pos, l = "slide", u = e.axisCollapseWidth, + h = this._model.get("axisExpandSlideTriggerArea"), c = null != h[0]; + if (u) c && u && s < o * h[0] ? (l = "jump", r = s - o * h[2]) : c && u && s > o * (1 - h[0]) ? (l = "jump", r = s - o * (1 - h[2])) : (r = s - o * h[1]) >= 0 && (r = s - o * (1 - h[1])) <= 0 && (r = 0), (r *= e.axisExpandWidth / u) ? PC(r, n, a, "all") : l = "none"; else { + o = n[1] - n[0]; + (n = [EC(0, a[1] * s / o - o / 2)])[1] = OC(a[1], n[0] + o), n[0] = n[1] - o + } + return {axisExpandWindow: n, behavior: l} + } + }, Ca.register("parallel", { + create: function (t, e) { + var i = []; + return t.eachComponent("parallel", function (n, o) { + var a = new qd(n, t, e); + a.name = "parallel_" + o, a.resize(n, e), n.coordinateSystem = a, a.model = n, i.push(a) + }), t.eachSeries(function (e) { + if ("parallel" === e.get("coordinateSystem")) { + var i = t.queryComponents({ + mainType: "parallel", + index: e.get("parallelIndex"), + id: e.get("parallelId") + })[0]; + e.coordinateSystem = i.coordinateSystem + } + }), i + } + }); + var GC = hM.extend({ + type: "baseParallelAxis", axis: null, activeIntervals: [], getAreaSelectStyle: function () { + return sb([["fill", "color"], ["lineWidth", "borderWidth"], ["stroke", "borderColor"], ["width", "width"], ["opacity", "opacity"]])(this.getModel("areaSelectStyle")) + }, setActiveIntervals: function (t) { + var e = this.activeIntervals = i(t); + if (e) for (var n = e.length - 1; n >= 0; n--) Co(e[n]) + }, getActiveState: function (t) { + var e = this.activeIntervals; + if (!e.length) return "normal"; + if (null == t || isNaN(t)) return "inactive"; + if (1 === e.length) { + var i = e[0]; + if (i[0] <= t && t <= i[1]) return "active" + } else for (var n = 0, o = e.length; n < o; n++) if (e[n][0] <= t && t <= e[n][1]) return "active"; + return "inactive" + } + }), WC = { + type: "value", + dim: null, + areaSelectStyle: { + width: 20, + borderWidth: 1, + borderColor: "rgba(160,197,232)", + color: "rgba(160,197,232)", + opacity: .3 + }, + realtime: !0, + z: 10 + }; + n(GC.prototype, ET), MD("parallel", GC, function (t, e) { + return e.type || (e.data ? "category" : "value") + }, WC), hM.extend({ + type: "parallel", + dependencies: ["parallelAxis"], + coordinateSystem: null, + dimensions: null, + parallelAxisIndex: null, + layoutMode: "box", + defaultOption: { + zlevel: 0, + z: 0, + left: 80, + top: 60, + right: 80, + bottom: 60, + layout: "horizontal", + axisExpandable: !1, + axisExpandCenter: null, + axisExpandCount: 0, + axisExpandWidth: 50, + axisExpandRate: 17, + axisExpandDebounce: 50, + axisExpandSlideTriggerArea: [-.15, .05, .4], + axisExpandTriggerOn: "click", + parallelAxisDefault: null + }, + init: function () { + hM.prototype.init.apply(this, arguments), this.mergeOption({}) + }, + mergeOption: function (t) { + var e = this.option; + t && n(e, t, !0), this._initDimensions() + }, + contains: function (t, e) { + var i = t.get("parallelIndex"); + return null != i && e.getComponent("parallel", i) === this + }, + setAxisExpand: function (t) { + d(["axisExpandable", "axisExpandCenter", "axisExpandCount", "axisExpandWidth", "axisExpandWindow"], function (e) { + t.hasOwnProperty(e) && (this.option[e] = t[e]) + }, this) + }, + _initDimensions: function () { + var t = this.dimensions = [], e = this.parallelAxisIndex = []; + d(g(this.dependentModels.parallelAxis, function (t) { + return (t.get("parallelIndex") || 0) === this.componentIndex + }, this), function (i) { + t.push("dim" + i.get("dim")), e.push(i.componentIndex) + }) + } + }), cs({type: "axisAreaSelect", event: "axisAreaSelected"}, function (t, e) { + e.eachComponent({mainType: "parallelAxis", query: t}, function (e) { + e.axis.model.setActiveIntervals(t.intervals) + }) + }), cs("parallelAxisExpand", function (t, e) { + e.eachComponent({mainType: "parallel", query: t}, function (e) { + e.setAxisExpand(t) + }) + }); + var FC = v, HC = d, ZC = f, UC = Math.min, XC = Math.max, jC = Math.pow, YC = 1e4, qC = 6, KC = 6, $C = "globalPan", + JC = {w: [0, 0], e: [0, 1], n: [1, 0], s: [1, 1]}, + QC = {w: "ew", e: "ew", n: "ns", s: "ns", ne: "nesw", sw: "nesw", nw: "nwse", se: "nwse"}, tL = { + brushStyle: {lineWidth: 2, stroke: "rgba(0,0,0,0.3)", fill: "rgba(0,0,0,0.1)"}, + transformable: !0, + brushMode: "single", + removeOnClick: !1 + }, eL = 0; + Qd.prototype = { + constructor: Qd, enableBrush: function (t) { + return this._brushType && ef(this), t.brushType && tf(this, t), this + }, setPanels: function (t) { + if (t && t.length) { + var e = this._panels = {}; + d(t, function (t) { + e[t.panelId] = i(t) + }) + } else this._panels = null; + return this + }, mount: function (t) { + t = t || {}, this._enableGlobalPan = t.enableGlobalPan; + var e = this.group; + return this._zr.add(e), e.attr({ + position: t.position || [0, 0], + rotation: t.rotation || 0, + scale: t.scale || [1, 1] + }), this._transform = e.getLocalTransform(), this + }, eachCover: function (t, e) { + HC(this._covers, t, e) + }, updateCovers: function (t) { + function e(t, e) { + return (null != t.id ? t.id : a + e) + "-" + t.brushType + } + + function o(e, i) { + var n = t[e]; + if (null != i && r[i] === u) s[e] = r[i]; else { + var o = s[e] = null != i ? (r[i].__brushOption = n, r[i]) : of(l, nf(l, n)); + sf(l, o) + } + } + + t = f(t, function (t) { + return n(i(tL), t, !0) + }); + var a = "\0-brush-index-", r = this._covers, s = this._covers = [], l = this, u = this._creatingCover; + return new Ss(r, t, function (t, i) { + return e(t.__brushOption, i) + }, e).add(o).update(o).remove(function (t) { + r[t] !== u && l.group.remove(r[t]) + }).execute(), this + }, unmount: function () { + return this.enableBrush(!1), cf(this), this._zr.remove(this.group), this + }, dispose: function () { + this.unmount(), this.off() + } + }, h(Qd, D_); + var iL = { + mousedown: function (t) { + if (this._dragging) Nf.call(this, t); else if (!t.target || !t.target.draggable) { + Cf(t); + var e = this.group.transformCoordToLocal(t.offsetX, t.offsetY); + this._creatingCover = null, (this._creatingPanel = uf(this, t, e)) && (this._dragging = !0, this._track = [e.slice()]) + } + }, mousemove: function (t) { + var e = this.group.transformCoordToLocal(t.offsetX, t.offsetY); + if (Af(this, t, e), this._dragging) { + Cf(t); + var i = kf(this, t, e, !1); + i && df(this, i) + } + }, mouseup: Nf + }, nL = { + lineX: Of(0), lineY: Of(1), rect: { + createCover: function (t, e) { + return gf(FC(Sf, function (t) { + return t + }, function (t) { + return t + }), t, e, ["w", "e", "n", "s", "se", "sw", "ne", "nw"]) + }, getCreatingRange: function (t) { + var e = pf(t); + return _f(e[1][0], e[1][1], e[0][0], e[0][1]) + }, updateCoverShape: function (t, e, i, n) { + mf(t, e, i, n) + }, updateCommon: vf, contain: Lf + }, polygon: { + createCover: function (t, e) { + var i = new sw; + return i.add(new bS({name: "main", style: xf(e), silent: !0})), i + }, getCreatingRange: function (t) { + return t + }, endCreating: function (t, e) { + e.remove(e.childAt(0)), e.add(new wS({ + name: "main", + draggable: !0, + drift: FC(Mf, t, e), + ondragend: FC(df, t, {isEnd: !0}) + })) + }, updateCoverShape: function (t, e, i, n) { + e.childAt(0).setShape({points: Tf(t, e, i)}) + }, updateCommon: vf, contain: Lf + } + }, oL = ["axisLine", "axisTickLabel", "axisName"], aL = ys({ + type: "parallelAxis", init: function (t, e) { + aL.superApply(this, "init", arguments), (this._brushController = new Qd(e.getZr())).on("brush", m(this._onBrush, this)) + }, render: function (t, e, i, n) { + if (!Vf(t, e, n)) { + this.axisModel = t, this.api = i, this.group.removeAll(); + var o = this._axisGroup; + if (this._axisGroup = new sw, this.group.add(this._axisGroup), t.get("show")) { + var r = Wf(t, e), s = r.coordinateSystem, l = t.getAreaSelectStyle(), u = l.width, h = t.axis.dim, + c = a({strokeContainThreshold: u}, s.getAxisLayout(h)), f = new LD(t, c); + d(oL, f.add, f), this._axisGroup.add(f.getGroup()), this._refreshBrushController(c, l, t, r, u, i); + var p = n && !1 === n.animation ? null : t; + yo(o, this._axisGroup, p) + } + } + }, _refreshBrushController: function (t, e, i, n, o, a) { + var r = i.axis.getExtent(), s = r[1] - r[0], l = Math.min(30, .1 * Math.abs(s)), + u = $t.create({x: r[0], y: -o / 2, width: s, height: o}); + u.x -= l, u.width += 2 * l, this._brushController.mount({ + enableGlobalPan: !0, + rotation: t.rotation, + position: t.position + }).setPanels([{ + panelId: "pl", + clipPath: Ef(u), + isTargetByCursor: zf(u, a, n), + getLinearBrushOtherExtent: Rf(u, 0) + }]).enableBrush({brushType: "lineX", brushStyle: e, removeOnClick: !0}).updateCovers(Gf(i)) + }, _onBrush: function (t, e) { + var i = this.axisModel, n = i.axis, o = f(t, function (t) { + return [n.coordToData(t.range[0], !0), n.coordToData(t.range[1], !0)] + }); + (!i.option.realtime === e.isEnd || e.removeOnClick) && this.api.dispatchAction({ + type: "axisAreaSelect", + parallelAxisId: i.id, + intervals: o + }) + }, dispose: function () { + this._brushController.dispose() + } + }); + ys({ + type: "parallel", render: function (t, e, i) { + this._model = t, this._api = i, this._handlers || (this._handlers = {}, d(rL, function (t, e) { + i.getZr().on(e, this._handlers[e] = m(t, this)) + }, this)), wr(this, "_throttledDispatchExpand", t.get("axisExpandRate"), "fixRate") + }, dispose: function (t, e) { + d(this._handlers, function (t, i) { + e.getZr().off(i, t) + }), this._handlers = null + }, _throttledDispatchExpand: function (t) { + this._dispatchExpand(t) + }, _dispatchExpand: function (t) { + t && this._api.dispatchAction(a({type: "parallelAxisExpand"}, t)) + } + }); + var rL = { + mousedown: function (t) { + Ff(this, "click") && (this._mouseDownPoint = [t.offsetX, t.offsetY]) + }, mouseup: function (t) { + var e = this._mouseDownPoint; + if (Ff(this, "click") && e) { + var i = [t.offsetX, t.offsetY]; + if (Math.pow(e[0] - i[0], 2) + Math.pow(e[1] - i[1], 2) > 5) return; + var n = this._model.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX, t.offsetY]); + "none" !== n.behavior && this._dispatchExpand({axisExpandWindow: n.axisExpandWindow}) + } + this._mouseDownPoint = null + }, mousemove: function (t) { + if (!this._mouseDownPoint && Ff(this, "mousemove")) { + var e = this._model, i = e.coordinateSystem.getSlidedAxisExpandWindow([t.offsetX, t.offsetY]), + n = i.behavior; + "jump" === n && this._throttledDispatchExpand.debounceNextCall(e.get("axisExpandDebounce")), this._throttledDispatchExpand("none" === n ? null : { + axisExpandWindow: i.axisExpandWindow, + animation: "jump" === n && null + }) + } + } + }; + us(function (t) { + Ud(t), Xd(t) + }), KM.extend({ + type: "series.parallel", + dependencies: ["parallel"], + visualColorAccessPath: "lineStyle.color", + getInitialData: function (t, e) { + var i = this.getSource(); + return Hf(i, this), Xs(i, this) + }, + getRawIndicesByActiveState: function (t) { + var e = this.coordinateSystem, i = this.getData(), n = []; + return e.eachActiveState(i, function (e, o) { + t === e && n.push(i.getRawIndex(o)) + }), n + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "parallel", + parallelIndex: 0, + label: {show: !1}, + inactiveOpacity: .05, + activeOpacity: 1, + lineStyle: {width: 1, opacity: .45, type: "solid"}, + emphasis: {label: {show: !1}}, + progressive: 500, + smooth: !1, + animationEasing: "linear" + } + }); + var sL = .3, lL = (gr.extend({ + type: "parallel", init: function () { + this._dataGroup = new sw, this.group.add(this._dataGroup), this._data, this._initialized + }, render: function (t, e, i, n) { + var o = this._dataGroup, a = t.getData(), r = this._data, s = t.coordinateSystem, l = s.dimensions, + u = Yf(t); + if (a.diff(r).add(function (t) { + qf(jf(a, o, t, l, s), a, t, u) + }).update(function (e, i) { + var o = r.getItemGraphicEl(i), h = Xf(a, e, l, s); + a.setItemGraphicEl(e, o), fo(o, {shape: {points: h}}, n && !1 === n.animation ? null : t, e), qf(o, a, e, u) + }).remove(function (t) { + var e = r.getItemGraphicEl(t); + o.remove(e) + }).execute(), !this._initialized) { + this._initialized = !0; + var h = Uf(s, t, function () { + setTimeout(function () { + o.removeClipPath() + }) + }); + o.setClipPath(h) + } + this._data = a + }, incrementalPrepareRender: function (t, e, i) { + this._initialized = !0, this._data = null, this._dataGroup.removeAll() + }, incrementalRender: function (t, e, i) { + for (var n = e.getData(), o = e.coordinateSystem, a = o.dimensions, r = Yf(e), s = t.start; s < t.end; s++) { + var l = jf(n, this._dataGroup, s, a, o); + l.incremental = !0, qf(l, n, s, r) + } + }, dispose: function () { + }, remove: function () { + this._dataGroup && this._dataGroup.removeAll(), this._data = null + } + }), ["lineStyle", "normal", "opacity"]); + ps({ + seriesType: "parallel", reset: function (t, e, i) { + var n = t.getModel("itemStyle"), o = t.getModel("lineStyle"), a = e.get("color"), + r = o.get("color") || n.get("color") || a[t.seriesIndex % a.length], s = t.get("inactiveOpacity"), + l = t.get("activeOpacity"), u = t.getModel("lineStyle").getLineStyle(), h = t.coordinateSystem, + c = t.getData(), d = {normal: u.opacity, active: l, inactive: s}; + return c.setVisual("color", r), { + progress: function (t, e) { + h.eachActiveState(e, function (t, i) { + var n = d[t]; + if ("normal" === t && e.hasItemOption) { + var o = e.getItemModel(i).get(lL, !0); + null != o && (n = o) + } + e.setItemVisual(i, "opacity", n) + }, t.start, t.end) + } + } + } + }); + var uL = KM.extend({ + type: "series.sankey", + layoutInfo: null, + getInitialData: function (t) { + var e = t.edges || t.links, i = t.data || t.nodes; + if (i && e) return lC(i, e, this, !0).data + }, + setNodePosition: function (t, e) { + var i = this.option.data[t]; + i.localX = e[0], i.localY = e[1] + }, + getGraph: function () { + return this.getData().graph + }, + getEdgeData: function () { + return this.getGraph().edgeData + }, + formatTooltip: function (t, e, i) { + if ("edge" === i) { + var n = this.getDataParams(t, i), o = n.data, a = o.source + " -- " + o.target; + return n.value && (a += " : " + n.value), Zo(a) + } + return uL.superCall(this, "formatTooltip", t, e) + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "view", + layout: null, + left: "5%", + top: "5%", + right: "20%", + bottom: "5%", + nodeWidth: 20, + nodeGap: 8, + draggable: !0, + layoutIterations: 32, + label: {show: !0, position: "right", color: "#000", fontSize: 12}, + itemStyle: {borderWidth: 1, borderColor: "#333"}, + lineStyle: {color: "#314656", opacity: .2, curveness: .5}, + emphasis: {label: {show: !0}, lineStyle: {opacity: .6}}, + animationEasing: "linear", + animationDuration: 1e3 + } + }), hL = En({ + shape: {x1: 0, y1: 0, x2: 0, y2: 0, cpx1: 0, cpy1: 0, cpx2: 0, cpy2: 0, extent: 0}, + buildPath: function (t, e) { + var i = e.extent / 2; + t.moveTo(e.x1, e.y1 - i), t.bezierCurveTo(e.cpx1, e.cpy1 - i, e.cpx2, e.cpy2 - i, e.x2, e.y2 - i), t.lineTo(e.x2, e.y2 + i), t.bezierCurveTo(e.cpx2, e.cpy2 + i, e.cpx1, e.cpy1 + i, e.x1, e.y1 + i), t.closePath() + } + }); + _s({ + type: "sankey", _model: null, render: function (t, e, i) { + var n = t.getGraph(), o = this.group, a = t.layoutInfo, r = a.width, s = a.height, l = t.getData(), + u = t.getData("edge"); + this._model = t, o.removeAll(), o.attr("position", [a.x, a.y]), n.eachEdge(function (e) { + var i = new hL; + i.dataIndex = e.dataIndex, i.seriesIndex = t.seriesIndex, i.dataType = "edge"; + var n = e.getModel("lineStyle"), a = n.get("curveness"), l = e.node1.getLayout(), + h = e.node1.getModel(), c = h.get("localX"), d = h.get("localY"), f = e.node2.getLayout(), + p = e.node2.getModel(), g = p.get("localX"), m = p.get("localY"), v = e.getLayout(); + i.shape.extent = Math.max(1, v.dy); + var y = (null != c ? c * r : l.x) + l.dx, x = (null != d ? d * s : l.y) + v.sy + v.dy / 2, + _ = null != g ? g * r : f.x, w = (null != m ? m * s : f.y) + v.ty + v.dy / 2, + b = y * (1 - a) + _ * a, S = x, M = y * a + _ * (1 - a), I = w; + switch (i.setShape({ + x1: y, + y1: x, + x2: _, + y2: w, + cpx1: b, + cpy1: S, + cpx2: M, + cpy2: I + }), i.setStyle(n.getItemStyle()), i.style.fill) { + case"source": + i.style.fill = e.node1.getVisual("color"); + break; + case"target": + i.style.fill = e.node2.getVisual("color") + } + eo(i, e.getModel("emphasis.lineStyle").getItemStyle()), o.add(i), u.setItemGraphicEl(e.dataIndex, i) + }), n.eachNode(function (e) { + var i = e.getLayout(), n = e.getModel(), a = n.get("localX"), u = n.get("localY"), + h = n.getModel("label"), c = n.getModel("emphasis.label"), d = new SS({ + shape: { + x: null != a ? a * r : i.x, + y: null != u ? u * s : i.y, + width: i.dx, + height: i.dy + }, style: n.getModel("itemStyle").getItemStyle() + }), f = e.getModel("emphasis.itemStyle").getItemStyle(); + io(d.style, f, h, c, { + labelFetcher: t, + labelDataIndex: e.dataIndex, + defaultText: e.id, + isRectText: !0 + }), d.setStyle("fill", e.getVisual("color")), eo(d, f), o.add(d), l.setItemGraphicEl(e.dataIndex, d), d.dataType = "node" + }), t.get("draggable") && l.eachItemGraphicEl(function (e, n) { + e.drift = function (e, o) { + this.shape.x += e, this.shape.y += o, this.dirty(), i.dispatchAction({ + type: "dragNode", + seriesId: t.id, + dataIndex: l.getRawIndex(n), + localX: this.shape.x / r, + localY: this.shape.y / s + }) + }, e.draggable = !0, e.cursor = "move" + }), !this._data && t.get("animation") && o.setClipPath($f(o.getBoundingRect(), t, function () { + o.removeClipPath() + })), this._data = t.getData() + }, dispose: function () { + } + }), cs({type: "dragNode", event: "dragNode", update: "update"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "sankey", query: t}, function (e) { + e.setNodePosition(t.dataIndex, [t.localX, t.localY]) + }) + }); + fs(function (t, e, i) { + t.eachSeriesByType("sankey", function (t) { + var i = t.get("nodeWidth"), n = t.get("nodeGap"), o = Qf(t, e); + t.layoutInfo = o; + var a = o.width, r = o.height, s = t.getGraph(), l = s.nodes, u = s.edges; + ep(l), tp(l, u, i, n, a, r, 0 !== g(l, function (t) { + return 0 === t.getLayout().value + }).length ? 0 : t.get("layoutIterations")) + }) + }), ps(function (t, e) { + t.eachSeriesByType("sankey", function (t) { + var e = t.getGraph().nodes; + if (e.length) { + var i = 1 / 0, n = -1 / 0; + d(e, function (t) { + var e = t.getLayout().value; + e < i && (i = e), e > n && (n = e) + }), d(e, function (e) { + var o = new ZA({ + type: "color", + mappingMethod: "linear", + dataExtent: [i, n], + visual: t.get("color") + }).mapValueToVisual(e.getLayout().value); + e.setVisual("color", o); + var a = e.getModel().get("itemStyle.color"); + null != a && e.setVisual("color", a) + }) + } + }) + }); + var cL = { + _baseAxisDim: null, getInitialData: function (t, e) { + var i, n, o = e.getComponent("xAxis", this.get("xAxisIndex")), + a = e.getComponent("yAxis", this.get("yAxisIndex")), r = o.get("type"), s = a.get("type"); + "category" === r ? (t.layout = "horizontal", i = o.getOrdinalMeta(), n = !0) : "category" === s ? (t.layout = "vertical", i = a.getOrdinalMeta(), n = !0) : t.layout = t.layout || "horizontal"; + var l = ["x", "y"], u = "horizontal" === t.layout ? 0 : 1, h = this._baseAxisDim = l[u], c = l[1 - u], + f = [o, a], p = f[u].get("type"), g = f[1 - u].get("type"), m = t.data; + if (m && n) { + var v = []; + d(m, function (t, e) { + var i; + t.value && y(t.value) ? (i = t.value.slice(), t.value.unshift(e)) : y(t) ? (i = t.slice(), t.unshift(e)) : i = t, v.push(i) + }), t.data = v + } + var x = this.defaultValueDimensions; + return jD(this, { + coordDimensions: [{ + name: h, + type: Ts(p), + ordinalMeta: i, + otherDims: {tooltip: !1, itemName: 0}, + dimsDef: ["base"] + }, {name: c, type: Ts(g), dimsDef: x.slice()}], dimensionsCount: x.length + 1 + }) + }, getBaseAxis: function () { + var t = this._baseAxisDim; + return this.ecModel.getComponent(t + "Axis", this.get(t + "AxisIndex")).axis + } + }; + h(KM.extend({ + type: "series.boxplot", + dependencies: ["xAxis", "yAxis", "grid"], + defaultValueDimensions: [{name: "min", defaultTooltip: !0}, {name: "Q1", defaultTooltip: !0}, { + name: "median", + defaultTooltip: !0 + }, {name: "Q3", defaultTooltip: !0}, {name: "max", defaultTooltip: !0}], + dimensions: null, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "cartesian2d", + legendHoverLink: !0, + hoverAnimation: !0, + layout: null, + boxWidth: [7, 50], + itemStyle: {color: "#fff", borderWidth: 1}, + emphasis: { + itemStyle: { + borderWidth: 2, + shadowBlur: 5, + shadowOffsetX: 2, + shadowOffsetY: 2, + shadowColor: "rgba(0,0,0,0.4)" + } + }, + animationEasing: "elasticOut", + animationDuration: 800 + } + }), cL, !0); + var dL = ["itemStyle"], fL = ["emphasis", "itemStyle"], pL = (gr.extend({ + type: "boxplot", render: function (t, e, i) { + var n = t.getData(), o = this.group, a = this._data; + this._data || o.removeAll(); + var r = "horizontal" === t.get("layout") ? 1 : 0; + n.diff(a).add(function (t) { + if (n.hasValue(t)) { + var e = _p(n.getItemLayout(t), n, t, r, !0); + n.setItemGraphicEl(t, e), o.add(e) + } + }).update(function (t, e) { + var i = a.getItemGraphicEl(e); + if (n.hasValue(t)) { + var s = n.getItemLayout(t); + i ? wp(s, i, n, t) : i = _p(s, n, t, r), o.add(i), n.setItemGraphicEl(t, i) + } else o.remove(i) + }).remove(function (t) { + var e = a.getItemGraphicEl(t); + e && o.remove(e) + }).execute(), this._data = n + }, remove: function (t) { + var e = this.group, i = this._data; + this._data = null, i && i.eachItemGraphicEl(function (t) { + t && e.remove(t) + }) + }, dispose: B + }), In.extend({ + type: "boxplotBoxPath", shape: {}, buildPath: function (t, e) { + var i = e.points, n = 0; + for (t.moveTo(i[n][0], i[n][1]), n++; n < 4; n++) t.lineTo(i[n][0], i[n][1]); + for (t.closePath(); n < i.length; n++) t.moveTo(i[n][0], i[n][1]), n++, t.lineTo(i[n][0], i[n][1]) + } + })), gL = ["itemStyle", "borderColor"], mL = d; + ps(function (t, e) { + var i = t.get("color"); + t.eachRawSeriesByType("boxplot", function (e) { + var n = i[e.seriesIndex % i.length], o = e.getData(); + o.setVisual({ + legendSymbol: "roundRect", + color: e.get(gL) || n + }), t.isSeriesFiltered(e) || o.each(function (t) { + var e = o.getItemModel(t); + o.setItemVisual(t, {color: e.get(gL, !0)}) + }) + }) + }), fs(function (t) { + var e = Sp(t); + mL(e, function (t) { + var e = t.seriesModels; + e.length && (Mp(t), mL(e, function (e, i) { + Ip(e, t.boxOffsetList[i], t.boxWidthList[i]) + })) + }) + }), h(KM.extend({ + type: "series.candlestick", + dependencies: ["xAxis", "yAxis", "grid"], + defaultValueDimensions: [{name: "open", defaultTooltip: !0}, { + name: "close", + defaultTooltip: !0 + }, {name: "lowest", defaultTooltip: !0}, {name: "highest", defaultTooltip: !0}], + dimensions: null, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "cartesian2d", + legendHoverLink: !0, + hoverAnimation: !0, + layout: null, + itemStyle: { + color: "#c23531", + color0: "#314656", + borderWidth: 1, + borderColor: "#c23531", + borderColor0: "#314656" + }, + emphasis: {itemStyle: {borderWidth: 2}}, + barMaxWidth: null, + barMinWidth: null, + barWidth: null, + large: !0, + largeThreshold: 600, + progressive: 3e3, + progressiveThreshold: 1e4, + progressiveChunkMode: "mod", + animationUpdate: !1, + animationEasing: "linear", + animationDuration: 300 + }, + getShadowDim: function () { + return "open" + }, + brushSelector: function (t, e, i) { + var n = e.getItemLayout(t); + return n && i.rect(n.brushRect) + } + }), cL, !0); + var vL = ["itemStyle"], yL = ["emphasis", "itemStyle"], xL = ["color", "color0", "borderColor", "borderColor0"], + _L = (gr.extend({ + type: "candlestick", render: function (t, e, i) { + this._updateDrawMode(t), this._isLargeDraw ? this._renderLarge(t) : this._renderNormal(t) + }, incrementalPrepareRender: function (t, e, i) { + this._clear(), this._updateDrawMode(t) + }, incrementalRender: function (t, e, i, n) { + this._isLargeDraw ? this._incrementalRenderLarge(t, e) : this._incrementalRenderNormal(t, e) + }, _updateDrawMode: function (t) { + var e = t.pipelineContext.large; + (null == this._isLargeDraw || e ^ this._isLargeDraw) && (this._isLargeDraw = e, this._clear()) + }, _renderNormal: function (t) { + var e = t.getData(), i = this._data, n = this.group, o = e.getLayout("isSimpleBox"); + this._data || n.removeAll(), e.diff(i).add(function (i) { + if (e.hasValue(i)) { + var a, r = e.getItemLayout(i); + po(a = Tp(r, 0, !0), {shape: {points: r.ends}}, t, i), Dp(a, e, i, o), n.add(a), e.setItemGraphicEl(i, a) + } + }).update(function (a, r) { + var s = i.getItemGraphicEl(r); + if (e.hasValue(a)) { + var l = e.getItemLayout(a); + s ? fo(s, {shape: {points: l.ends}}, t, a) : s = Tp(l), Dp(s, e, a, o), n.add(s), e.setItemGraphicEl(a, s) + } else n.remove(s) + }).remove(function (t) { + var e = i.getItemGraphicEl(t); + e && n.remove(e) + }).execute(), this._data = e + }, _renderLarge: function (t) { + this._clear(), Cp(t, this.group) + }, _incrementalRenderNormal: function (t, e) { + for (var i, n = e.getData(), o = n.getLayout("isSimpleBox"); null != (i = t.next());) { + var a; + Dp(a = Tp(n.getItemLayout(i)), n, i, o), a.incremental = !0, this.group.add(a) + } + }, _incrementalRenderLarge: function (t, e) { + Cp(e, this.group, !0) + }, remove: function (t) { + this._clear() + }, _clear: function () { + this.group.removeAll(), this._data = null + }, dispose: B + }), In.extend({ + type: "normalCandlestickBox", shape: {}, buildPath: function (t, e) { + var i = e.points; + this.__simpleBox ? (t.moveTo(i[4][0], i[4][1]), t.lineTo(i[6][0], i[6][1])) : (t.moveTo(i[0][0], i[0][1]), t.lineTo(i[1][0], i[1][1]), t.lineTo(i[2][0], i[2][1]), t.lineTo(i[3][0], i[3][1]), t.closePath(), t.moveTo(i[4][0], i[4][1]), t.lineTo(i[5][0], i[5][1]), t.moveTo(i[6][0], i[6][1]), t.lineTo(i[7][0], i[7][1])) + } + })), wL = In.extend({ + type: "largeCandlestickBox", shape: {}, buildPath: function (t, e) { + for (var i = e.points, n = 0; n < i.length;) if (this.__sign === i[n++]) { + var o = i[n++]; + t.moveTo(o, i[n++]), t.lineTo(o, i[n++]) + } else n += 3 + } + }), bL = ["itemStyle", "borderColor"], SL = ["itemStyle", "borderColor0"], ML = ["itemStyle", "color"], + IL = ["itemStyle", "color0"], TL = { + seriesType: "candlestick", plan: QM(), performRawSeries: !0, reset: function (t, e) { + function i(t, e) { + return e.get(t > 0 ? ML : IL) + } + + function n(t, e) { + return e.get(t > 0 ? bL : SL) + } + + var o = t.getData(), a = t.pipelineContext.large; + if (o.setVisual({ + legendSymbol: "roundRect", + colorP: i(1, t), + colorN: i(-1, t), + borderColorP: n(1, t), + borderColorN: n(-1, t) + }), !e.isSeriesFiltered(t)) return !a && { + progress: function (t, e) { + for (var o; null != (o = t.next());) { + var a = e.getItemModel(o), r = e.getItemLayout(o).sign; + e.setItemVisual(o, {color: i(r, a), borderColor: n(r, a)}) + } + } + } + } + }, DL = "undefined" != typeof Float32Array ? Float32Array : Array, AL = { + seriesType: "candlestick", plan: QM(), reset: function (t) { + var e = t.coordinateSystem, i = t.getData(), n = Pp(t, i), o = 0, a = 1, r = ["x", "y"], + s = i.mapDimension(r[o]), l = i.mapDimension(r[a], !0), u = l[0], h = l[1], c = l[2], d = l[3]; + if (i.setLayout({candleWidth: n, isSimpleBox: n <= 1.3}), !(null == s || l.length < 4)) return { + progress: t.pipelineContext.large ? function (t, i) { + for (var n, r, l = new DL(5 * t.count), f = 0, p = [], g = []; null != (r = t.next());) { + var m = i.get(s, r), v = i.get(u, r), y = i.get(h, r), x = i.get(c, r), _ = i.get(d, r); + isNaN(m) || isNaN(x) || isNaN(_) ? (l[f++] = NaN, f += 4) : (l[f++] = kp(i, r, v, y, h), p[o] = m, p[a] = x, n = e.dataToPoint(p, null, g), l[f++] = n ? n[0] : NaN, l[f++] = n ? n[1] : NaN, p[a] = _, n = e.dataToPoint(p, null, g), l[f++] = n ? n[1] : NaN) + } + i.setLayout("largePoints", l) + } : function (t, i) { + function r(t, i) { + var n = []; + return n[o] = i, n[a] = t, isNaN(i) || isNaN(t) ? [NaN, NaN] : e.dataToPoint(n) + } + + function l(t, e, i) { + var a = e.slice(), r = e.slice(); + a[o] = Fn(a[o] + n / 2, 1, !1), r[o] = Fn(r[o] - n / 2, 1, !0), i ? t.push(a, r) : t.push(r, a) + } + + function f(t) { + return t[o] = Fn(t[o], 1), t + } + + for (var p; null != (p = t.next());) { + var g = i.get(s, p), m = i.get(u, p), v = i.get(h, p), y = i.get(c, p), x = i.get(d, p), + _ = Math.min(m, v), w = Math.max(m, v), b = r(_, g), S = r(w, g), M = r(y, g), I = r(x, g), + T = []; + l(T, S, 0), l(T, b, 1), T.push(f(I), f(S), f(M), f(b)), i.setItemLayout(p, { + sign: kp(i, p, m, v, h), + initBaseline: m > v ? S[a] : b[a], + ends: T, + brushRect: function (t, e, i) { + var s = r(t, i), l = r(e, i); + return s[o] -= n / 2, l[o] -= n / 2, { + x: s[0], + y: s[1], + width: a ? n : l[0] - s[0], + height: a ? l[1] - s[1] : n + } + }(y, x, g) + }) + } + } + } + } + }; + us(function (t) { + t && y(t.series) && d(t.series, function (t) { + w(t) && "k" === t.type && (t.type = "candlestick") + }) + }), ps(TL), fs(AL), KM.extend({ + type: "series.effectScatter", + dependencies: ["grid", "polar"], + getInitialData: function (t, e) { + return Xs(this.getSource(), this) + }, + brushSelector: "point", + defaultOption: { + coordinateSystem: "cartesian2d", + zlevel: 0, + z: 2, + legendHoverLink: !0, + effectType: "ripple", + progressive: 0, + showEffectOn: "render", + rippleEffect: {period: 4, scale: 2.5, brushType: "fill"}, + symbolSize: 10 + } + }); + var CL = Ep.prototype; + CL.stopEffectAnimation = function () { + this.childAt(1).removeAll() + }, CL.startEffectAnimation = function (t) { + for (var e = t.symbolType, i = t.color, n = this.childAt(1), o = 0; o < 3; o++) { + var a = Tl(e, -1, -1, 2, 2, i); + a.attr({style: {strokeNoScale: !0}, z2: 99, silent: !0, scale: [.5, .5]}); + var r = -o / 3 * t.period + t.effectOffset; + a.animate("", !0).when(t.period, {scale: [t.rippleScale / 2, t.rippleScale / 2]}).delay(r).start(), a.animateStyle(!0).when(t.period, {opacity: 0}).delay(r).start(), n.add(a) + } + Op(n, t) + }, CL.updateEffectAnimation = function (t) { + for (var e = this._effectCfg, i = this.childAt(1), n = ["symbolType", "period", "rippleScale"], o = 0; o < n.length; o++) { + var a = n[o]; + if (e[a] !== t[a]) return this.stopEffectAnimation(), void this.startEffectAnimation(t) + } + Op(i, t) + }, CL.highlight = function () { + this.trigger("emphasis") + }, CL.downplay = function () { + this.trigger("normal") + }, CL.updateData = function (t, e) { + var i = t.hostModel; + this.childAt(0).updateData(t, e); + var n = this.childAt(1), o = t.getItemModel(e), a = t.getItemVisual(e, "symbol"), + r = Np(t.getItemVisual(e, "symbolSize")), s = t.getItemVisual(e, "color"); + n.attr("scale", r), n.traverse(function (t) { + t.attr({fill: s}) + }); + var l = o.getShallow("symbolOffset"); + if (l) { + var u = n.position; + u[0] = Do(l[0], r[0]), u[1] = Do(l[1], r[1]) + } + n.rotation = (o.getShallow("symbolRotate") || 0) * Math.PI / 180 || 0; + var h = {}; + if (h.showEffectOn = i.get("showEffectOn"), h.rippleScale = o.get("rippleEffect.scale"), h.brushType = o.get("rippleEffect.brushType"), h.period = 1e3 * o.get("rippleEffect.period"), h.effectOffset = e / t.count(), h.z = o.getShallow("z") || 0, h.zlevel = o.getShallow("zlevel") || 0, h.symbolType = a, h.color = s, this.off("mouseover").off("mouseout").off("emphasis").off("normal"), "render" === h.showEffectOn) this._effectCfg ? this.updateEffectAnimation(h) : this.startEffectAnimation(h), this._effectCfg = h; else { + this._effectCfg = null, this.stopEffectAnimation(); + var c = this.childAt(0), d = function () { + c.highlight(), "render" !== h.showEffectOn && this.startEffectAnimation(h) + }, f = function () { + c.downplay(), "render" !== h.showEffectOn && this.stopEffectAnimation() + }; + this.on("mouseover", d, this).on("mouseout", f, this).on("emphasis", d, this).on("normal", f, this) + } + this._effectCfg = h + }, CL.fadeOut = function (t) { + this.off("mouseover").off("mouseout").off("emphasis").off("normal"), t && t() + }, u(Ep, sw), _s({ + type: "effectScatter", init: function () { + this._symbolDraw = new Ql(Ep) + }, render: function (t, e, i) { + var n = t.getData(), o = this._symbolDraw; + o.updateData(n), this.group.add(o.group) + }, updateTransform: function (t, e, i) { + var n = t.getData(); + this.group.dirty(); + var o = mD().reset(t); + o.progress && o.progress({start: 0, end: n.count()}, n), this._symbolDraw.updateLayout(n) + }, _updateGroupTransform: function (t) { + var e = t.coordinateSystem; + e && e.getRoamTransform && (this.group.transform = gt(e.getRoamTransform()), this.group.decomposeTransform()) + }, remove: function (t, e) { + this._symbolDraw && this._symbolDraw.remove(e) + }, dispose: function () { + } + }), ps(gD("effectScatter", "circle")), fs(mD("effectScatter")); + var LL = "undefined" == typeof Uint32Array ? Array : Uint32Array, + kL = "undefined" == typeof Float64Array ? Array : Float64Array, PL = KM.extend({ + type: "series.lines", + dependencies: ["grid", "polar"], + visualColorAccessPath: "lineStyle.color", + init: function (t) { + t.data = t.data || [], Rp(t); + var e = this._processFlatCoordsArray(t.data); + this._flatCoords = e.flatCoords, this._flatCoordsOffset = e.flatCoordsOffset, e.flatCoords && (t.data = new Float32Array(e.count)), PL.superApply(this, "init", arguments) + }, + mergeOption: function (t) { + if (t.data = t.data || [], Rp(t), t.data) { + var e = this._processFlatCoordsArray(t.data); + this._flatCoords = e.flatCoords, this._flatCoordsOffset = e.flatCoordsOffset, e.flatCoords && (t.data = new Float32Array(e.count)) + } + PL.superApply(this, "mergeOption", arguments) + }, + appendData: function (t) { + var e = this._processFlatCoordsArray(t.data); + e.flatCoords && (this._flatCoords ? (this._flatCoords = z(this._flatCoords, e.flatCoords), this._flatCoordsOffset = z(this._flatCoordsOffset, e.flatCoordsOffset)) : (this._flatCoords = e.flatCoords, this._flatCoordsOffset = e.flatCoordsOffset), t.data = new Float32Array(e.count)), this.getRawData().appendData(t.data) + }, + _getCoordsFromItemModel: function (t) { + var e = this.getData().getItemModel(t); + return e.option instanceof Array ? e.option : e.getShallow("coords") + }, + getLineCoordsCount: function (t) { + return this._flatCoordsOffset ? this._flatCoordsOffset[2 * t + 1] : this._getCoordsFromItemModel(t).length + }, + getLineCoords: function (t, e) { + if (this._flatCoordsOffset) { + for (var i = this._flatCoordsOffset[2 * t], n = this._flatCoordsOffset[2 * t + 1], o = 0; o < n; o++) e[o] = e[o] || [], e[o][0] = this._flatCoords[i + 2 * o], e[o][1] = this._flatCoords[i + 2 * o + 1]; + return n + } + for (var a = this._getCoordsFromItemModel(t), o = 0; o < a.length; o++) e[o] = e[o] || [], e[o][0] = a[o][0], e[o][1] = a[o][1]; + return a.length + }, + _processFlatCoordsArray: function (t) { + var e = 0; + if (this._flatCoords && (e = this._flatCoords.length), "number" == typeof t[0]) { + for (var i = t.length, n = new LL(i), o = new kL(i), a = 0, r = 0, s = 0, l = 0; l < i;) { + s++; + var u = t[l++]; + n[r++] = a + e, n[r++] = u; + for (var h = 0; h < u; h++) { + var c = t[l++], d = t[l++]; + o[a++] = c, o[a++] = d + } + } + return {flatCoordsOffset: new Uint32Array(n.buffer, 0, r), flatCoords: o, count: s} + } + return {flatCoordsOffset: null, flatCoords: null, count: t.length} + }, + getInitialData: function (t, e) { + var i = new sT(["value"], this); + return i.hasItemOption = !1, i.initData(t.data, [], function (t, e, n, o) { + if (t instanceof Array) return NaN; + i.hasItemOption = !0; + var a = t.value; + return null != a ? a instanceof Array ? a[o] : a : void 0 + }), i + }, + formatTooltip: function (t) { + var e = this.getData().getItemModel(t), i = e.get("name"); + if (i) return i; + var n = e.get("fromName"), o = e.get("toName"), a = []; + return null != n && a.push(n), null != o && a.push(o), Zo(a.join(" > ")) + }, + preventIncremental: function () { + return !!this.get("effect.show") + }, + getProgressive: function () { + var t = this.option.progressive; + return null == t ? this.option.large ? 1e4 : this.get("progressive") : t + }, + getProgressiveThreshold: function () { + var t = this.option.progressiveThreshold; + return null == t ? this.option.large ? 2e4 : this.get("progressiveThreshold") : t + }, + defaultOption: { + coordinateSystem: "geo", + zlevel: 0, + z: 2, + legendHoverLink: !0, + hoverAnimation: !0, + xAxisIndex: 0, + yAxisIndex: 0, + symbol: ["none", "none"], + symbolSize: [10, 10], + geoIndex: 0, + effect: {show: !1, period: 4, constantSpeed: 0, symbol: "circle", symbolSize: 3, loop: !0, trailLength: .2}, + large: !1, + largeThreshold: 2e3, + polyline: !1, + label: {show: !1, position: "end"}, + lineStyle: {opacity: .5} + } + }), NL = zp.prototype; + NL.createLine = function (t, e, i) { + return new bd(t, e, i) + }, NL._updateEffectSymbol = function (t, e) { + var i = t.getItemModel(e).getModel("effect"), n = i.get("symbolSize"), o = i.get("symbol"); + y(n) || (n = [n, n]); + var a = i.get("color") || t.getItemVisual(e, "color"), r = this.childAt(1); + this._symbolType !== o && (this.remove(r), (r = Tl(o, -.5, -.5, 1, 1, a)).z2 = 100, r.culling = !0, this.add(r)), r && (r.setStyle("shadowColor", a), r.setStyle(i.getItemStyle(["color"])), r.attr("scale", n), r.setColor(a), r.attr("scale", n), this._symbolType = o, this._updateEffectAnimation(t, i, e)) + }, NL._updateEffectAnimation = function (t, e, i) { + var n = this.childAt(1); + if (n) { + var o = this, a = t.getItemLayout(i), r = 1e3 * e.get("period"), s = e.get("loop"), + l = e.get("constantSpeed"), u = T(e.get("delay"), function (e) { + return e / t.count() * r / 3 + }), h = "function" == typeof u; + if (n.ignore = !0, this.updateAnimationPoints(n, a), l > 0 && (r = this.getLineLength(n) / l * 1e3), r !== this._period || s !== this._loop) { + n.stopAnimation(); + var c = u; + h && (c = u(i)), n.__t > 0 && (c = -r * n.__t), n.__t = 0; + var d = n.animate("", s).when(r, {__t: 1}).delay(c).during(function () { + o.updateSymbolPosition(n) + }); + s || d.done(function () { + o.remove(n) + }), d.start() + } + this._period = r, this._loop = s + } + }, NL.getLineLength = function (t) { + return S_(t.__p1, t.__cp1) + S_(t.__cp1, t.__p2) + }, NL.updateAnimationPoints = function (t, e) { + t.__p1 = e[0], t.__p2 = e[1], t.__cp1 = e[2] || [(e[0][0] + e[1][0]) / 2, (e[0][1] + e[1][1]) / 2] + }, NL.updateData = function (t, e, i) { + this.childAt(0).updateData(t, e, i), this._updateEffectSymbol(t, e) + }, NL.updateSymbolPosition = function (t) { + var e = t.__p1, i = t.__p2, n = t.__cp1, o = t.__t, a = t.position, r = Qi, s = tn; + a[0] = r(e[0], n[0], i[0], o), a[1] = r(e[1], n[1], i[1], o); + var l = s(e[0], n[0], i[0], o), u = s(e[1], n[1], i[1], o); + t.rotation = -Math.atan2(u, l) - Math.PI / 2, t.ignore = !1 + }, NL.updateLayout = function (t, e) { + this.childAt(0).updateLayout(t, e); + var i = t.getItemModel(e).getModel("effect"); + this._updateEffectAnimation(t, i, e) + }, u(zp, sw); + var OL = Bp.prototype; + OL._createPolyline = function (t, e, i) { + var n = t.getItemLayout(e), o = new bS({shape: {points: n}}); + this.add(o), this._updateCommonStl(t, e, i) + }, OL.updateData = function (t, e, i) { + var n = t.hostModel; + fo(this.childAt(0), {shape: {points: t.getItemLayout(e)}}, n, e), this._updateCommonStl(t, e, i) + }, OL._updateCommonStl = function (t, e, i) { + var n = this.childAt(0), o = t.getItemModel(e), a = t.getItemVisual(e, "color"), s = i && i.lineStyle, + l = i && i.hoverLineStyle; + i && !t.hasItemOption || (s = o.getModel("lineStyle").getLineStyle(), l = o.getModel("emphasis.lineStyle").getLineStyle()), n.useStyle(r({ + strokeNoScale: !0, + fill: "none", + stroke: a + }, s)), n.hoverStyle = l, eo(this) + }, OL.updateLayout = function (t, e) { + this.childAt(0).setShape("points", t.getItemLayout(e)) + }, u(Bp, sw); + var EL = Vp.prototype; + EL.createLine = function (t, e, i) { + return new Bp(t, e, i) + }, EL.updateAnimationPoints = function (t, e) { + this._points = e; + for (var i = [0], n = 0, o = 1; o < e.length; o++) { + var a = e[o - 1], r = e[o]; + n += S_(a, r), i.push(n) + } + if (0 !== n) { + for (o = 0; o < i.length; o++) i[o] /= n; + this._offsets = i, this._length = n + } + }, EL.getLineLength = function (t) { + return this._length + }, EL.updateSymbolPosition = function (t) { + var e = t.__t, i = this._points, n = this._offsets, o = i.length; + if (n) { + var a = this._lastFrame; + if (e < this._lastFramePercent) { + for (r = Math.min(a + 1, o - 1); r >= 0 && !(n[r] <= e); r--) ; + r = Math.min(r, o - 2) + } else { + for (var r = a; r < o && !(n[r] > e); r++) ; + r = Math.min(r - 1, o - 2) + } + J(t.position, i[r], i[r + 1], (e - n[r]) / (n[r + 1] - n[r])); + var s = i[r + 1][0] - i[r][0], l = i[r + 1][1] - i[r][1]; + t.rotation = -Math.atan2(l, s) - Math.PI / 2, this._lastFrame = r, this._lastFramePercent = e, t.ignore = !1 + } + }, u(Vp, zp); + var RL = En({ + shape: {polyline: !1, curveness: 0, segs: []}, buildPath: function (t, e) { + var i = e.segs, n = e.curveness; + if (e.polyline) for (r = 0; r < i.length;) { + var o = i[r++]; + if (o > 0) { + t.moveTo(i[r++], i[r++]); + for (var a = 1; a < o; a++) t.lineTo(i[r++], i[r++]) + } + } else for (var r = 0; r < i.length;) { + var s = i[r++], l = i[r++], u = i[r++], h = i[r++]; + if (t.moveTo(s, l), n > 0) { + var c = (s + u) / 2 - (l - h) * n, d = (l + h) / 2 - (u - s) * n; + t.quadraticCurveTo(c, d, u, h) + } else t.lineTo(u, h) + } + }, findDataIndex: function (t, e) { + var i = this.shape, n = i.segs, o = i.curveness; + if (i.polyline) for (var a = 0, r = 0; r < n.length;) { + var s = n[r++]; + if (s > 0) for (var l = n[r++], u = n[r++], h = 1; h < s; h++) if (cn(l, u, c = n[r++], d = n[r++])) return a; + a++ + } else for (var a = 0, r = 0; r < n.length;) { + var l = n[r++], u = n[r++], c = n[r++], d = n[r++]; + if (o > 0) { + if (fn(l, u, (l + c) / 2 - (u - d) * o, (u + d) / 2 - (c - l) * o, c, d)) return a + } else if (cn(l, u, c, d)) return a; + a++ + } + return -1 + } + }), zL = Gp.prototype; + zL.isPersistent = function () { + return !this._incremental + }, zL.updateData = function (t) { + this.group.removeAll(); + var e = new RL({rectHover: !0, cursor: "default"}); + e.setShape({segs: t.getLayout("linesPoints")}), this._setCommon(e, t), this.group.add(e), this._incremental = null + }, zL.incrementalPrepareUpdate = function (t) { + this.group.removeAll(), this._clearIncremental(), t.count() > 5e5 ? (this._incremental || (this._incremental = new On({silent: !0})), this.group.add(this._incremental)) : this._incremental = null + }, zL.incrementalUpdate = function (t, e) { + var i = new RL; + i.setShape({segs: e.getLayout("linesPoints")}), this._setCommon(i, e, !!this._incremental), this._incremental ? this._incremental.addDisplayable(i, !0) : (i.rectHover = !0, i.cursor = "default", i.__startIndex = t.start, this.group.add(i)) + }, zL.remove = function () { + this._clearIncremental(), this._incremental = null, this.group.removeAll() + }, zL._setCommon = function (t, e, i) { + var n = e.hostModel; + t.setShape({ + polyline: n.get("polyline"), + curveness: n.get("lineStyle.curveness") + }), t.useStyle(n.getModel("lineStyle").getLineStyle()), t.style.strokeNoScale = !0; + var o = e.getVisual("color"); + o && t.setStyle("stroke", o), t.setStyle("fill"), i || (t.seriesIndex = n.seriesIndex, t.on("mousemove", function (e) { + t.dataIndex = null; + var i = t.findDataIndex(e.offsetX, e.offsetY); + i > 0 && (t.dataIndex = i + t.__startIndex) + })) + }, zL._clearIncremental = function () { + var t = this._incremental; + t && t.clearDisplaybles() + }; + var BL = { + seriesType: "lines", plan: QM(), reset: function (t) { + var e = t.coordinateSystem, i = t.get("polyline"), n = t.pipelineContext.large; + return { + progress: function (o, a) { + var r = []; + if (n) { + var s, l = o.end - o.start; + if (i) { + for (var u = 0, h = o.start; h < o.end; h++) u += t.getLineCoordsCount(h); + s = new Float32Array(l + 2 * u) + } else s = new Float32Array(4 * l); + for (var c = 0, d = [], h = o.start; h < o.end; h++) { + g = t.getLineCoords(h, r), i && (s[c++] = g); + for (var f = 0; f < g; f++) d = e.dataToPoint(r[f], !1, d), s[c++] = d[0], s[c++] = d[1] + } + a.setLayout("linesPoints", s) + } else for (h = o.start; h < o.end; h++) { + var p = a.getItemModel(h), g = t.getLineCoords(h, r), m = []; + if (i) for (var v = 0; v < g; v++) m.push(e.dataToPoint(r[v])); else { + m[0] = e.dataToPoint(r[0]), m[1] = e.dataToPoint(r[1]); + var y = p.get("lineStyle.curveness"); + +y && (m[2] = [(m[0][0] + m[1][0]) / 2 - (m[0][1] - m[1][1]) * y, (m[0][1] + m[1][1]) / 2 - (m[1][0] - m[0][0]) * y]) + } + a.setItemLayout(h, m) + } + } + } + } + }; + _s({ + type: "lines", init: function () { + }, render: function (t, e, i) { + var n = t.getData(), o = this._updateLineDraw(n, t), a = t.get("zlevel"), r = t.get("effect.trailLength"), + s = i.getZr(), l = "svg" === s.painter.getType(); + l || s.painter.getLayer(a).clear(!0), null == this._lastZlevel || l || s.configLayer(this._lastZlevel, {motionBlur: !1}), this._showEffect(t) && r && (l || s.configLayer(a, { + motionBlur: !0, + lastFrameAlpha: Math.max(Math.min(r / 10 + .9, 1), 0) + })), o.updateData(n), this._lastZlevel = a, this._finished = !0 + }, incrementalPrepareRender: function (t, e, i) { + var n = t.getData(); + this._updateLineDraw(n, t).incrementalPrepareUpdate(n), this._clearLayer(i), this._finished = !1 + }, incrementalRender: function (t, e, i) { + this._lineDraw.incrementalUpdate(t, e.getData()), this._finished = t.end === e.getData().count() + }, updateTransform: function (t, e, i) { + var n = t.getData(), o = t.pipelineContext; + if (!this._finished || o.large || o.progressiveRender) return {update: !0}; + var a = BL.reset(t); + a.progress && a.progress({start: 0, end: n.count()}, n), this._lineDraw.updateLayout(), this._clearLayer(i) + }, _updateLineDraw: function (t, e) { + var i = this._lineDraw, n = this._showEffect(e), o = !!e.get("polyline"), a = e.pipelineContext.large; + return i && n === this._hasEffet && o === this._isPolyline && a === this._isLargeDraw || (i && i.remove(), i = this._lineDraw = a ? new Gp : new Sd(o ? n ? Vp : Bp : n ? zp : bd), this._hasEffet = n, this._isPolyline = o, this._isLargeDraw = a, this.group.removeAll()), this.group.add(i.group), i + }, _showEffect: function (t) { + return !!t.get("effect.show") + }, _clearLayer: function (t) { + var e = t.getZr(); + "svg" === e.painter.getType() || null == this._lastZlevel || e.painter.getLayer(this._lastZlevel).clear(!0) + }, remove: function (t, e) { + this._lineDraw && this._lineDraw.remove(), this._lineDraw = null, this._clearLayer(e) + }, dispose: function () { + } + }); + var VL = "lineStyle.opacity".split("."), GL = { + seriesType: "lines", reset: function (t, e, i) { + var n = Wp(t.get("symbol")), o = Wp(t.get("symbolSize")), a = t.getData(); + return a.setVisual("fromSymbol", n && n[0]), a.setVisual("toSymbol", n && n[1]), a.setVisual("fromSymbolSize", o && o[0]), a.setVisual("toSymbolSize", o && o[1]), a.setVisual("opacity", t.get(VL)), { + dataEach: a.hasItemOption ? function (t, e) { + var i = t.getItemModel(e), n = Wp(i.getShallow("symbol", !0)), + o = Wp(i.getShallow("symbolSize", !0)), a = i.get(VL); + n[0] && t.setItemVisual(e, "fromSymbol", n[0]), n[1] && t.setItemVisual(e, "toSymbol", n[1]), o[0] && t.setItemVisual(e, "fromSymbolSize", o[0]), o[1] && t.setItemVisual(e, "toSymbolSize", o[1]), t.setItemVisual(e, "opacity", a) + } : null + } + } + }; + fs(BL), ps(GL), KM.extend({ + type: "series.heatmap", + getInitialData: function (t, e) { + return Xs(this.getSource(), this, {generateCoord: "value"}) + }, + preventIncremental: function () { + var t = Ca.get(this.get("coordinateSystem")); + if (t && t.dimensions) return "lng" === t.dimensions[0] && "lat" === t.dimensions[1] + }, + defaultOption: { + coordinateSystem: "cartesian2d", + zlevel: 0, + z: 2, + geoIndex: 0, + blurSize: 30, + pointSize: 20, + maxOpacity: 1, + minOpacity: 0 + } + }); + Fp.prototype = { + update: function (t, e, i, n, o, a) { + var r = this._getBrush(), s = this._getGradient(t, o, "inRange"), l = this._getGradient(t, o, "outOfRange"), + u = this.pointSize + this.blurSize, h = this.canvas, c = h.getContext("2d"), d = t.length; + h.width = e, h.height = i; + for (var f = 0; f < d; ++f) { + var p = t[f], g = p[0], m = p[1], v = n(p[2]); + c.globalAlpha = v, c.drawImage(r, g - u, m - u) + } + if (!h.width || !h.height) return h; + for (var y = c.getImageData(0, 0, h.width, h.height), x = y.data, _ = 0, w = x.length, b = this.minOpacity, S = this.maxOpacity - b; _ < w;) { + var v = x[_ + 3] / 256, M = 4 * Math.floor(255 * v); + if (v > 0) { + var I = a(v) ? s : l; + v > 0 && (v = v * S + b), x[_++] = I[M], x[_++] = I[M + 1], x[_++] = I[M + 2], x[_++] = I[M + 3] * v * 256 + } else _ += 4 + } + return c.putImageData(y, 0, 0), h + }, _getBrush: function () { + var t = this._brushCanvas || (this._brushCanvas = m_()), e = this.pointSize + this.blurSize, i = 2 * e; + t.width = i, t.height = i; + var n = t.getContext("2d"); + return n.clearRect(0, 0, i, i), n.shadowOffsetX = i, n.shadowBlur = this.blurSize, n.shadowColor = "#000", n.beginPath(), n.arc(-e, e, this.pointSize, 0, 2 * Math.PI, !0), n.closePath(), n.fill(), t + }, _getGradient: function (t, e, i) { + for (var n = this._gradientPixels, o = n[i] || (n[i] = new Uint8ClampedArray(1024)), a = [0, 0, 0, 0], r = 0, s = 0; s < 256; s++) e[i](s / 255, !0, a), o[r++] = a[0], o[r++] = a[1], o[r++] = a[2], o[r++] = a[3]; + return o + } + }, _s({ + type: "heatmap", render: function (t, e, i) { + var n; + e.eachComponent("visualMap", function (e) { + e.eachTargetSeries(function (i) { + i === t && (n = e) + }) + }), this.group.removeAll(), this._incrementalDisplayable = null; + var o = t.coordinateSystem; + "cartesian2d" === o.type || "calendar" === o.type ? this._renderOnCartesianAndCalendar(t, i, 0, t.getData().count()) : Up(o) && this._renderOnGeo(o, t, n, i) + }, incrementalPrepareRender: function (t, e, i) { + this.group.removeAll() + }, incrementalRender: function (t, e, i, n) { + e.coordinateSystem && this._renderOnCartesianAndCalendar(e, n, t.start, t.end, !0) + }, _renderOnCartesianAndCalendar: function (t, e, i, n, o) { + var r, s, l = t.coordinateSystem; + if ("cartesian2d" === l.type) { + var u = l.getAxis("x"), h = l.getAxis("y"); + r = u.getBandWidth(), s = h.getBandWidth() + } + for (var c = this.group, d = t.getData(), f = t.getModel("itemStyle").getItemStyle(["color"]), p = t.getModel("emphasis.itemStyle").getItemStyle(), g = t.getModel("label"), m = t.getModel("emphasis.label"), v = l.type, y = "cartesian2d" === v ? [d.mapDimension("x"), d.mapDimension("y"), d.mapDimension("value")] : [d.mapDimension("time"), d.mapDimension("value")], x = i; x < n; x++) { + var _; + if ("cartesian2d" === v) { + if (isNaN(d.get(y[2], x))) continue; + var w = l.dataToPoint([d.get(y[0], x), d.get(y[1], x)]); + _ = new SS({ + shape: {x: w[0] - r / 2, y: w[1] - s / 2, width: r, height: s}, + style: {fill: d.getItemVisual(x, "color"), opacity: d.getItemVisual(x, "opacity")} + }) + } else { + if (isNaN(d.get(y[1], x))) continue; + _ = new SS({ + z2: 1, + shape: l.dataToRect([d.get(y[0], x)]).contentShape, + style: {fill: d.getItemVisual(x, "color"), opacity: d.getItemVisual(x, "opacity")} + }) + } + var b = d.getItemModel(x); + d.hasItemOption && (f = b.getModel("itemStyle").getItemStyle(["color"]), p = b.getModel("emphasis.itemStyle").getItemStyle(), g = b.getModel("label"), m = b.getModel("emphasis.label")); + var S = t.getRawValue(x), M = "-"; + S && null != S[2] && (M = S[2]), io(f, p, g, m, { + labelFetcher: t, + labelDataIndex: x, + defaultText: M, + isRectText: !0 + }), _.setStyle(f), eo(_, d.hasItemOption ? p : a({}, p)), _.incremental = o, o && (_.useHoverLayer = !0), c.add(_), d.setItemGraphicEl(x, _) + } + }, _renderOnGeo: function (t, e, i, n) { + var o = i.targetVisuals.inRange, a = i.targetVisuals.outOfRange, r = e.getData(), + s = this._hmLayer || this._hmLayer || new Fp; + s.blurSize = e.get("blurSize"), s.pointSize = e.get("pointSize"), s.minOpacity = e.get("minOpacity"), s.maxOpacity = e.get("maxOpacity"); + var l = t.getViewRect().clone(), u = t.getRoamTransform(); + l.applyTransform(u); + var h = Math.max(l.x, 0), c = Math.max(l.y, 0), d = Math.min(l.width + l.x, n.getWidth()), + f = Math.min(l.height + l.y, n.getHeight()), p = d - h, g = f - c, + m = [r.mapDimension("lng"), r.mapDimension("lat"), r.mapDimension("value")], + v = r.mapArray(m, function (e, i, n) { + var o = t.dataToPoint([e, i]); + return o[0] -= h, o[1] -= c, o.push(n), o + }), y = i.getExtent(), + x = "visualMap.continuous" === i.type ? Zp(y, i.option.range) : Hp(y, i.getPieceList(), i.option.selected); + s.update(v, p, g, o.color.getNormalizer(), { + inRange: o.color.getColorMapper(), + outOfRange: a.color.getColorMapper() + }, x); + var _ = new Je({style: {width: p, height: g, x: h, y: c, image: s.canvas}, silent: !0}); + this.group.add(_) + }, dispose: function () { + } + }); + var WL = GD.extend({ + type: "series.pictorialBar", + dependencies: ["grid"], + defaultOption: { + symbol: "circle", + symbolSize: null, + symbolRotate: null, + symbolPosition: null, + symbolOffset: null, + symbolMargin: null, + symbolRepeat: !1, + symbolRepeatDirection: "end", + symbolClip: !1, + symbolBoundingData: null, + symbolPatternSize: 400, + barGap: "-100%", + progressive: 0, + hoverAnimation: !1 + }, + getInitialData: function (t) { + return t.stack = null, WL.superApply(this, "getInitialData", arguments) + } + }), FL = ["itemStyle", "borderWidth"], HL = [{xy: "x", wh: "width", index: 0, posDesc: ["left", "right"]}, { + xy: "y", + wh: "height", + index: 1, + posDesc: ["top", "bottom"] + }], ZL = new pS; + _s({ + type: "pictorialBar", render: function (t, e, i) { + var n = this.group, o = t.getData(), a = this._data, r = t.coordinateSystem, + s = !!r.getBaseAxis().isHorizontal(), l = r.grid.getRect(), u = { + ecSize: {width: i.getWidth(), height: i.getHeight()}, + seriesModel: t, + coordSys: r, + coordSysExtent: [[l.x, l.x + l.width], [l.y, l.y + l.height]], + isHorizontal: s, + valueDim: HL[+s], + categoryDim: HL[1 - s] + }; + return o.diff(a).add(function (t) { + if (o.hasValue(t)) { + var e = ng(o, t), i = Xp(o, t, e, u), a = sg(o, u, i); + o.setItemGraphicEl(t, a), n.add(a), fg(a, u, i) + } + }).update(function (t, e) { + var i = a.getItemGraphicEl(e); + if (o.hasValue(t)) { + var r = ng(o, t), s = Xp(o, t, r, u), l = hg(o, s); + i && l !== i.__pictorialShapeStr && (n.remove(i), o.setItemGraphicEl(t, null), i = null), i ? lg(i, u, s) : i = sg(o, u, s, !0), o.setItemGraphicEl(t, i), i.__pictorialSymbolMeta = s, n.add(i), fg(i, u, s) + } else n.remove(i) + }).remove(function (t) { + var e = a.getItemGraphicEl(t); + e && ug(a, t, e.__pictorialSymbolMeta.animationModel, e) + }).execute(), this._data = o, this.group + }, dispose: B, remove: function (t, e) { + var i = this.group, n = this._data; + t.get("animation") ? n && n.eachItemGraphicEl(function (e) { + ug(n, e.dataIndex, t, e) + }) : i.removeAll() + } + }); + fs(v(cl, "pictorialBar")), ps(gD("pictorialBar", "roundRect")); + var UL = function (t, e, i, n, o) { + qT.call(this, t, e, i), this.type = n || "value", this.position = o || "bottom", this.orient = null + }; + UL.prototype = { + constructor: UL, model: null, isHorizontal: function () { + var t = this.position; + return "top" === t || "bottom" === t + }, pointToData: function (t, e) { + return this.coordinateSystem.pointToData(t, e)[0] + }, toGlobalCoord: null, toLocalCoord: null + }, u(UL, qT), gg.prototype = { + type: "singleAxis", axisPointerEnabled: !0, constructor: gg, _init: function (t, e, i) { + var n = this.dimension, o = new UL(n, xl(t), [0, 0], t.get("type"), t.get("position")), + a = "category" === o.type; + o.onBand = a && t.get("boundaryGap"), o.inverse = t.get("inverse"), o.orient = t.get("orient"), t.axis = o, o.model = t, o.coordinateSystem = this, this._axis = o + }, update: function (t, e) { + t.eachSeries(function (t) { + if (t.coordinateSystem === this) { + var e = t.getData(); + d(e.mapDimension(this.dimension, !0), function (t) { + this._axis.scale.unionExtentFromData(e, t) + }, this), yl(this._axis.scale, this._axis.model) + } + }, this) + }, resize: function (t, e) { + this._rect = Qo({ + left: t.get("left"), + top: t.get("top"), + right: t.get("right"), + bottom: t.get("bottom"), + width: t.get("width"), + height: t.get("height") + }, {width: e.getWidth(), height: e.getHeight()}), this._adjustAxis() + }, getRect: function () { + return this._rect + }, _adjustAxis: function () { + var t = this._rect, e = this._axis, i = e.isHorizontal(), n = i ? [0, t.width] : [0, t.height], + o = e.reverse ? 1 : 0; + e.setExtent(n[o], n[1 - o]), this._updateAxisTransform(e, i ? t.x : t.y) + }, _updateAxisTransform: function (t, e) { + var i = t.getExtent(), n = i[0] + i[1], o = t.isHorizontal(); + t.toGlobalCoord = o ? function (t) { + return t + e + } : function (t) { + return n - t + e + }, t.toLocalCoord = o ? function (t) { + return t - e + } : function (t) { + return n - t + e + } + }, getAxis: function () { + return this._axis + }, getBaseAxis: function () { + return this._axis + }, getAxes: function () { + return [this._axis] + }, getTooltipAxes: function () { + return {baseAxes: [this.getAxis()]} + }, containPoint: function (t) { + var e = this.getRect(), i = this.getAxis(); + return "horizontal" === i.orient ? i.contain(i.toLocalCoord(t[0])) && t[1] >= e.y && t[1] <= e.y + e.height : i.contain(i.toLocalCoord(t[1])) && t[0] >= e.y && t[0] <= e.y + e.height + }, pointToData: function (t) { + var e = this.getAxis(); + return [e.coordToData(e.toLocalCoord(t["horizontal" === e.orient ? 0 : 1]))] + }, dataToPoint: function (t) { + var e = this.getAxis(), i = this.getRect(), n = [], o = "horizontal" === e.orient ? 0 : 1; + return t instanceof Array && (t = t[0]), n[o] = e.toGlobalCoord(e.dataToCoord(+t)), n[1 - o] = 0 === o ? i.y + i.height / 2 : i.x + i.width / 2, n + } + }, Ca.register("single", { + create: function (t, e) { + var i = []; + return t.eachComponent("singleAxis", function (n, o) { + var a = new gg(n, t, e); + a.name = "single_" + o, a.resize(n, e), n.coordinateSystem = a, i.push(a) + }), t.eachSeries(function (e) { + if ("singleAxis" === e.get("coordinateSystem")) { + var i = t.queryComponents({ + mainType: "singleAxis", + index: e.get("singleAxisIndex"), + id: e.get("singleAxisId") + })[0]; + e.coordinateSystem = i && i.coordinateSystem + } + }), i + }, dimensions: gg.prototype.dimensions + }); + var XL = ["axisLine", "axisTickLabel", "axisName"], jL = ED.extend({ + type: "singleAxis", axisPointerClass: "SingleAxisPointer", render: function (t, e, i, n) { + var o = this.group; + o.removeAll(); + var a = mg(t), r = new LD(t, a); + d(XL, r.add, r), o.add(r.getGroup()), t.get("splitLine.show") && this._splitLine(t), jL.superCall(this, "render", t, e, i, n) + }, _splitLine: function (t) { + var e = t.axis; + if (!e.scale.isBlank()) { + var i = t.getModel("splitLine"), n = i.getModel("lineStyle"), o = n.get("width"), a = n.get("color"); + a = a instanceof Array ? a : [a]; + for (var r = t.coordinateSystem.getRect(), s = e.isHorizontal(), l = [], u = 0, h = e.getTicksCoords({tickModel: i}), c = [], d = [], f = 0; f < h.length; ++f) { + var p = e.toGlobalCoord(h[f].coord); + s ? (c[0] = p, c[1] = r.y, d[0] = p, d[1] = r.y + r.height) : (c[0] = r.x, c[1] = p, d[0] = r.x + r.width, d[1] = p); + var g = u++ % a.length; + l[g] = l[g] || [], l[g].push(new MS(Gn({ + shape: {x1: c[0], y1: c[1], x2: d[0], y2: d[1]}, + style: {lineWidth: o}, + silent: !0 + }))) + } + for (f = 0; f < l.length; ++f) this.group.add(zS(l[f], { + style: { + stroke: a[f % a.length], + lineDash: n.getLineDash(o), + lineWidth: o + }, silent: !0 + })) + } + } + }), YL = hM.extend({ + type: "singleAxis", + layoutMode: "box", + axis: null, + coordinateSystem: null, + getCoordSysModel: function () { + return this + } + }), qL = { + left: "5%", + top: "5%", + right: "5%", + bottom: "5%", + type: "value", + position: "bottom", + orient: "horizontal", + axisLine: {show: !0, lineStyle: {width: 2, type: "solid"}}, + tooltip: {show: !0}, + axisTick: {show: !0, length: 6, lineStyle: {width: 2}}, + axisLabel: {show: !0, interval: "auto"}, + splitLine: {show: !0, lineStyle: {type: "dashed", opacity: .2}} + }; + n(YL.prototype, ET), MD("single", YL, function (t, e) { + return e.type || (e.data ? "category" : "value") + }, qL); + var KL = function (t, e) { + var i, n = [], o = t.seriesIndex; + if (null == o || !(i = e.getSeriesByIndex(o))) return {point: []}; + var a = i.getData(), r = Pi(a, t); + if (null == r || r < 0 || y(r)) return {point: []}; + var s = a.getItemGraphicEl(r), l = i.coordinateSystem; + if (i.getTooltipPosition) n = i.getTooltipPosition(r) || []; else if (l && l.dataToPoint) n = l.dataToPoint(a.getValues(f(l.dimensions, function (t) { + return a.mapDimension(t) + }), r, !0)) || []; else if (s) { + var u = s.getBoundingRect().clone(); + u.applyTransform(s.transform), n = [u.x + u.width / 2, u.y + u.height / 2] + } + return {point: n, el: s} + }, $L = d, JL = v, QL = Ni(), tk = (vs({ + type: "axisPointer", + coordSysAxesInfo: null, + defaultOption: { + show: "auto", + triggerOn: null, + zlevel: 0, + z: 50, + type: "line", + snap: !1, + triggerTooltip: !0, + value: null, + status: null, + link: [], + animation: null, + animationDurationUpdate: 200, + lineStyle: {color: "#aaa", width: 1, type: "solid"}, + shadowStyle: {color: "rgba(150,150,150,0.3)"}, + label: { + show: !0, + formatter: null, + precision: "auto", + margin: 3, + color: "#fff", + padding: [5, 7, 5, 7], + backgroundColor: "auto", + borderColor: null, + borderWidth: 0, + shadowBlur: 3, + shadowColor: "#aaa" + }, + handle: { + show: !1, + icon: "M10.7,11.9v-1.3H9.3v1.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7v-1.2h6.6z M13.3,22H6.7v-1.2h6.6z M13.3,19.6H6.7v-1.2h6.6z", + size: 45, + margin: 50, + color: "#333", + shadowBlur: 3, + shadowColor: "#aaa", + shadowOffsetX: 0, + shadowOffsetY: 2, + throttle: 40 + } + } + }), Ni()), ek = d, ik = ys({ + type: "axisPointer", render: function (t, e, i) { + var n = e.getComponent("tooltip"), o = t.get("triggerOn") || n && n.get("triggerOn") || "mousemove|click"; + Dg("axisPointer", i, function (t, e, i) { + "none" !== o && ("leave" === t || o.indexOf(t) >= 0) && i({ + type: "updateAxisPointer", + currTrigger: t, + x: e && e.offsetX, + y: e && e.offsetY + }) + }) + }, remove: function (t, e) { + Ng(e.getZr(), "axisPointer"), ik.superApply(this._model, "remove", arguments) + }, dispose: function (t, e) { + Ng("axisPointer", e), ik.superApply(this._model, "dispose", arguments) + } + }), nk = Ni(), ok = i, ak = m; + (Og.prototype = { + _group: null, + _lastGraphicKey: null, + _handle: null, + _dragging: !1, + _lastValue: null, + _lastStatus: null, + _payloadInfo: null, + animationThreshold: 15, + render: function (t, e, i, n) { + var o = e.get("value"), a = e.get("status"); + if (this._axisModel = t, this._axisPointerModel = e, this._api = i, n || this._lastValue !== o || this._lastStatus !== a) { + this._lastValue = o, this._lastStatus = a; + var r = this._group, s = this._handle; + if (!a || "hide" === a) return r && r.hide(), void (s && s.hide()); + r && r.show(), s && s.show(); + var l = {}; + this.makeElOption(l, o, t, e, i); + var u = l.graphicKey; + u !== this._lastGraphicKey && this.clear(i), this._lastGraphicKey = u; + var h = this._moveAnimation = this.determineAnimation(t, e); + if (r) { + var c = v(Eg, e, h); + this.updatePointerEl(r, l, c, e), this.updateLabelEl(r, l, c, e) + } else r = this._group = new sw, this.createPointerEl(r, l, t, e), this.createLabelEl(r, l, t, e), i.getZr().add(r); + Vg(r, e, !0), this._renderHandle(o) + } + }, + remove: function (t) { + this.clear(t) + }, + dispose: function (t) { + this.clear(t) + }, + determineAnimation: function (t, e) { + var i = e.get("animation"), n = t.axis, o = "category" === n.type, a = e.get("snap"); + if (!a && !o) return !1; + if ("auto" === i || null == i) { + var r = this.animationThreshold; + if (o && n.getBandWidth() > r) return !0; + if (a) { + var s = qu(t).seriesDataCount, l = n.getExtent(); + return Math.abs(l[0] - l[1]) / s > r + } + return !1 + } + return !0 === i + }, + makeElOption: function (t, e, i, n, o) { + }, + createPointerEl: function (t, e, i, n) { + var o = e.pointer; + if (o) { + var a = nk(t).pointerEl = new BS[o.type](ok(e.pointer)); + t.add(a) + } + }, + createLabelEl: function (t, e, i, n) { + if (e.label) { + var o = nk(t).labelEl = new SS(ok(e.label)); + t.add(o), zg(o, n) + } + }, + updatePointerEl: function (t, e, i) { + var n = nk(t).pointerEl; + n && (n.setStyle(e.pointer.style), i(n, {shape: e.pointer.shape})) + }, + updateLabelEl: function (t, e, i, n) { + var o = nk(t).labelEl; + o && (o.setStyle(e.label.style), i(o, {shape: e.label.shape, position: e.label.position}), zg(o, n)) + }, + _renderHandle: function (t) { + if (!this._dragging && this.updateHandleTransform) { + var e = this._axisPointerModel, i = this._api.getZr(), n = this._handle, o = e.getModel("handle"), + a = e.get("status"); + if (!o.get("show") || !a || "hide" === a) return n && i.remove(n), void (this._handle = null); + var r; + this._handle || (r = !0, n = this._handle = _o(o.get("icon"), { + cursor: "move", + draggable: !0, + onmousemove: function (t) { + zw(t.event) + }, + onmousedown: ak(this._onHandleDragMove, this, 0, 0), + drift: ak(this._onHandleDragMove, this), + ondragend: ak(this._onHandleDragEnd, this) + }), i.add(n)), Vg(n, e, !1); + var s = ["color", "borderColor", "borderWidth", "opacity", "shadowColor", "shadowBlur", "shadowOffsetX", "shadowOffsetY"]; + n.setStyle(o.getItemStyle(null, s)); + var l = o.get("size"); + y(l) || (l = [l, l]), n.attr("scale", [l[0] / 2, l[1] / 2]), wr(this, "_doDispatchAxisPointer", o.get("throttle") || 0, "fixRate"), this._moveHandleToValue(t, r) + } + }, + _moveHandleToValue: function (t, e) { + Eg(this._axisPointerModel, !e && this._moveAnimation, this._handle, Bg(this.getHandleTransform(t, this._axisModel, this._axisPointerModel))) + }, + _onHandleDragMove: function (t, e) { + var i = this._handle; + if (i) { + this._dragging = !0; + var n = this.updateHandleTransform(Bg(i), [t, e], this._axisModel, this._axisPointerModel); + this._payloadInfo = n, i.stopAnimation(), i.attr(Bg(n)), nk(i).lastProp = null, this._doDispatchAxisPointer() + } + }, + _doDispatchAxisPointer: function () { + if (this._handle) { + var t = this._payloadInfo, e = this._axisModel; + this._api.dispatchAction({ + type: "updateAxisPointer", + x: t.cursorPoint[0], + y: t.cursorPoint[1], + tooltipOption: t.tooltipOption, + axesInfo: [{axisDim: e.axis.dim, axisIndex: e.componentIndex}] + }) + } + }, + _onHandleDragEnd: function (t) { + if (this._dragging = !1, this._handle) { + var e = this._axisPointerModel.get("value"); + this._moveHandleToValue(e), this._api.dispatchAction({type: "hideTip"}) + } + }, + getHandleTransform: null, + updateHandleTransform: null, + clear: function (t) { + this._lastValue = null, this._lastStatus = null; + var e = t.getZr(), i = this._group, n = this._handle; + e && i && (this._lastGraphicKey = null, i && e.remove(i), n && e.remove(n), this._group = null, this._handle = null, this._payloadInfo = null) + }, + doClear: function () { + }, + buildLabel: function (t, e, i) { + return i = i || 0, {x: t[i], y: t[1 - i], width: e[i], height: e[1 - i]} + } + }).constructor = Og, Gi(Og); + var rk = Og.extend({ + makeElOption: function (t, e, i, n, o) { + var a = i.axis, r = a.grid, s = n.get("type"), l = qg(r, a).getOtherAxis(a).getGlobalExtent(), + u = a.toGlobalCoord(a.dataToCoord(e, !0)); + if (s && "none" !== s) { + var h = Gg(n), c = sk[s](a, u, l, h); + c.style = h, t.graphicKey = c.type, t.pointer = c + } + Ug(e, t, eh(r.model, i), i, n, o) + }, getHandleTransform: function (t, e, i) { + var n = eh(e.axis.grid.model, e, {labelInside: !1}); + return n.labelMargin = i.get("handle.margin"), { + position: Zg(e.axis, t, n), + rotation: n.rotation + (n.labelDirection < 0 ? Math.PI : 0) + } + }, updateHandleTransform: function (t, e, i, n) { + var o = i.axis, a = o.grid, r = o.getGlobalExtent(!0), s = qg(a, o).getOtherAxis(o).getGlobalExtent(), + l = "x" === o.dim ? 0 : 1, u = t.position; + u[l] += e[l], u[l] = Math.min(r[1], u[l]), u[l] = Math.max(r[0], u[l]); + var h = (s[1] + s[0]) / 2, c = [h, h]; + c[l] = u[l]; + var d = [{verticalAlign: "middle"}, {align: "center"}]; + return {position: u, rotation: t.rotation, cursorPoint: c, tooltipOption: d[l]} + } + }), sk = { + line: function (t, e, i, n) { + var o = Xg([e, i[0]], [e, i[1]], Kg(t)); + return Gn({shape: o, style: n}), {type: "Line", shape: o} + }, shadow: function (t, e, i, n) { + var o = Math.max(1, t.getBandWidth()), a = i[1] - i[0]; + return {type: "Rect", shape: jg([e - o / 2, i[0]], [o, a], Kg(t))} + } + }; + ED.registerAxisPointerClass("CartesianAxisPointer", rk), us(function (t) { + if (t) { + (!t.axisPointer || 0 === t.axisPointer.length) && (t.axisPointer = {}); + var e = t.axisPointer.link; + e && !y(e) && (t.axisPointer.link = [e]) + } + }), hs(LI.PROCESSOR.STATISTIC, function (t, e) { + t.getComponent("axisPointer").coordSysAxesInfo = Fu(t, e) + }), cs({type: "updateAxisPointer", event: "updateAxisPointer", update: ":updateAxisPointer"}, function (t, e, i) { + var n = t.currTrigger, o = [t.x, t.y], a = t, r = t.dispatchAction || m(i.dispatchAction, i), + s = e.getComponent("axisPointer").coordSysAxesInfo; + if (s) { + Tg(o) && (o = KL({seriesIndex: a.seriesIndex, dataIndex: a.dataIndex}, e).point); + var l = Tg(o), u = a.axesInfo, h = s.axesInfo, c = "leave" === n || Tg(o), d = {}, f = {}, + p = {list: [], map: {}}, g = {showPointer: JL(xg, f), showTooltip: JL(_g, p)}; + $L(s.coordSysMap, function (t, e) { + var i = l || t.containPoint(o); + $L(s.coordSysAxesInfo[e], function (t, e) { + var n = t.axis, a = Mg(u, t); + if (!c && i && (!u || a)) { + var r = a && a.value; + null != r || l || (r = n.pointToData(o)), null != r && vg(t, r, g, !1, d) + } + }) + }); + var v = {}; + return $L(h, function (t, e) { + var i = t.linkGroup; + i && !f[e] && $L(i.axesInfo, function (e, n) { + var o = f[n]; + if (e !== t && o) { + var a = o.value; + i.mapper && (a = t.axis.scale.parse(i.mapper(a, Ig(e), Ig(t)))), v[t.key] = a + } + }) + }), $L(v, function (t, e) { + vg(h[e], t, g, !0, d) + }), wg(f, h, d), bg(p, o, t, r), Sg(h, 0, i), d + } + }); + var lk = ["x", "y"], uk = ["width", "height"], hk = Og.extend({ + makeElOption: function (t, e, i, n, o) { + var a = i.axis, r = a.coordinateSystem, s = Jg(r, 1 - $g(a)), l = r.dataToPoint(e)[0], u = n.get("type"); + if (u && "none" !== u) { + var h = Gg(n), c = ck[u](a, l, s, h); + c.style = h, t.graphicKey = c.type, t.pointer = c + } + Ug(e, t, mg(i), i, n, o) + }, getHandleTransform: function (t, e, i) { + var n = mg(e, {labelInside: !1}); + return n.labelMargin = i.get("handle.margin"), { + position: Zg(e.axis, t, n), + rotation: n.rotation + (n.labelDirection < 0 ? Math.PI : 0) + } + }, updateHandleTransform: function (t, e, i, n) { + var o = i.axis, a = o.coordinateSystem, r = $g(o), s = Jg(a, r), l = t.position; + l[r] += e[r], l[r] = Math.min(s[1], l[r]), l[r] = Math.max(s[0], l[r]); + var u = Jg(a, 1 - r), h = (u[1] + u[0]) / 2, c = [h, h]; + return c[r] = l[r], { + position: l, + rotation: t.rotation, + cursorPoint: c, + tooltipOption: {verticalAlign: "middle"} + } + } + }), ck = { + line: function (t, e, i, n) { + var o = Xg([e, i[0]], [e, i[1]], $g(t)); + return Gn({shape: o, style: n}), {type: "Line", shape: o} + }, shadow: function (t, e, i, n) { + var o = t.getBandWidth(), a = i[1] - i[0]; + return {type: "Rect", shape: jg([e - o / 2, i[0]], [o, a], $g(t))} + } + }; + ED.registerAxisPointerClass("SingleAxisPointer", hk), ys({type: "single"}); + var dk = KM.extend({ + type: "series.themeRiver", + dependencies: ["singleAxis"], + nameMap: null, + init: function (t) { + dk.superApply(this, "init", arguments), this.legendDataProvider = function () { + return this.getRawData() + } + }, + fixData: function (t) { + for (var e = t.length, i = f(Jf().key(function (t) { + return t[2] + }).entries(t), function (t) { + return {name: t.key, dataList: t.values} + }), n = i.length, o = -1, a = -1, r = 0; r < n; ++r) { + var s = i[r].dataList.length; + s > o && (o = s, a = r) + } + for (var l = 0; l < n; ++l) if (l !== a) for (var u = i[l].name, h = 0; h < o; ++h) { + for (var c = i[a].dataList[h][0], d = i[l].dataList.length, p = -1, g = 0; g < d; ++g) if (i[l].dataList[g][0] === c) { + p = g; + break + } + -1 === p && (t[e] = [], t[e][0] = c, t[e][1] = 0, t[e][2] = u, e++) + } + return t + }, + getInitialData: function (t, e) { + for (var i = e.queryComponents({ + mainType: "singleAxis", + index: this.get("singleAxisIndex"), + id: this.get("singleAxisId") + })[0].get("type"), n = g(t.data, function (t) { + return void 0 !== t[2] + }), o = this.fixData(n || []), a = [], r = this.nameMap = R(), s = 0, l = 0; l < o.length; ++l) a.push(o[l][2]), r.get(o[l][2]) || (r.set(o[l][2], s), s++); + var u = hT(o, { + coordDimensions: ["single"], + dimensionsDefine: [{name: "time", type: Ts(i)}, {name: "value", type: "float"}, { + name: "name", + type: "ordinal" + }], + encodeDefine: {single: 0, value: 1, itemName: 2} + }), h = new sT(u, this); + return h.initData(o), h + }, + getLayerSeries: function () { + for (var t = this.getData(), e = t.count(), i = [], n = 0; n < e; ++n) i[n] = n; + for (var o = f(Jf().key(function (e) { + return t.get("name", e) + }).entries(i), function (t) { + return {name: t.key, indices: t.values} + }), a = t.mapDimension("single"), r = 0; r < o.length; ++r) o[r].indices.sort(function (e, i) { + return t.get(a, e) - t.get(a, i) + }); + return o + }, + getAxisTooltipData: function (t, e, i) { + y(t) || (t = t ? [t] : []); + for (var n, o = this.getData(), a = this.getLayerSeries(), r = [], s = a.length, l = 0; l < s; ++l) { + for (var u = Number.MAX_VALUE, h = -1, c = a[l].indices.length, d = 0; d < c; ++d) { + var f = o.get(t[0], a[l].indices[d]), p = Math.abs(f - e); + p <= u && (n = f, u = p, h = a[l].indices[d]) + } + r.push(h) + } + return {dataIndices: r, nestestValue: n} + }, + formatTooltip: function (t) { + var e = this.getData(), i = e.getName(t), n = e.get(e.mapDimension("value"), t); + return (isNaN(n) || null == n) && (n = "-"), Zo(i + " : " + n) + }, + defaultOption: { + zlevel: 0, + z: 2, + coordinateSystem: "singleAxis", + boundaryGap: ["10%", "10%"], + singleAxisIndex: 0, + animationEasing: "linear", + label: {margin: 4, show: !0, position: "left", color: "#000", fontSize: 11}, + emphasis: {label: {show: !0}} + } + }); + _s({ + type: "themeRiver", init: function () { + this._layers = [] + }, render: function (t, e, i) { + function n(t) { + return t.name + } + + function o(e, i, n) { + var o = this._layers; + if ("remove" !== e) { + for (var u, h = [], c = [], f = l[i].indices, p = 0; p < f.length; p++) { + var g = r.getItemLayout(f[p]), m = g.x, v = g.y0, y = g.y; + h.push([m, v]), c.push([m, v + y]), u = r.getItemVisual(f[p], "color") + } + var x, _, w = r.getItemLayout(f[0]), b = r.getItemModel(f[p - 1]), S = b.getModel("label"), + M = S.get("margin"); + if ("add" === e) { + I = d[i] = new sw; + x = new pD({ + shape: { + points: h, + stackedOnPoints: c, + smooth: .4, + stackedOnSmooth: .4, + smoothConstraint: !1 + }, z2: 0 + }), _ = new fS({ + style: { + x: w.x - M, + y: w.y0 + w.y / 2 + } + }), I.add(x), I.add(_), s.add(I), x.setClipPath(Qg(x.getBoundingRect(), t, function () { + x.removeClipPath() + })) + } else { + var I = o[n]; + x = I.childAt(0), _ = I.childAt(1), s.add(I), d[i] = I, fo(x, { + shape: { + points: h, + stackedOnPoints: c + } + }, t), fo(_, {style: {x: w.x - M, y: w.y0 + w.y / 2}}, t) + } + var T = b.getModel("emphasis.itemStyle"), D = b.getModel("itemStyle"); + no(_.style, S, { + text: S.get("show") ? t.getFormattedLabel(f[p - 1], "normal") || r.getName(f[p - 1]) : null, + textVerticalAlign: "middle" + }), x.setStyle(a({fill: u}, D.getItemStyle(["color"]))), eo(x, T.getItemStyle()) + } else s.remove(o[i]) + } + + var r = t.getData(), s = this.group, l = t.getLayerSeries(), u = r.getLayout("layoutInfo"), h = u.rect, + c = u.boundaryGap; + s.attr("position", [0, h.y + c[0]]); + var d = {}; + new Ss(this._layersSeries || [], l, n, n).add(m(o, this, "add")).update(m(o, this, "update")).remove(m(o, this, "remove")).execute(), this._layersSeries = l, this._layers = d + }, dispose: function () { + } + }); + fs(function (t, e) { + t.eachSeriesByType("themeRiver", function (t) { + var e = t.getData(), i = t.coordinateSystem, n = {}, o = i.getRect(); + n.rect = o; + var a = t.get("boundaryGap"), r = i.getAxis(); + n.boundaryGap = a, "horizontal" === r.orient ? (a[0] = Do(a[0], o.height), a[1] = Do(a[1], o.height), tm(e, t, o.height - a[0] - a[1])) : (a[0] = Do(a[0], o.width), a[1] = Do(a[1], o.width), tm(e, t, o.width - a[0] - a[1])), e.setLayout("layoutInfo", n) + }) + }), ps(function (t) { + t.eachSeriesByType("themeRiver", function (t) { + var e = t.getData(), i = t.getRawData(), n = t.get("color"), o = R(); + e.each(function (t) { + o.set(e.getRawIndex(t), t) + }), i.each(function (a) { + var r = i.getName(a), s = n[(t.nameMap.get(r) - 1) % n.length]; + i.setItemVisual(a, "color", s); + var l = o.get(a); + null != l && e.setItemVisual(l, "color", s) + }) + }) + }), hs(iA("themeRiver")), KM.extend({ + type: "series.sunburst", + _viewRoot: null, + getInitialData: function (t, e) { + var i = {name: t.name, children: t.data}; + im(i); + var n = t.levels || [], o = {}; + return o.levels = n, ic.createTree(i, this, o).data + }, + optionUpdated: function () { + this.resetViewRoot() + }, + getDataParams: function (t) { + var e = KM.prototype.getDataParams.apply(this, arguments), i = this.getData().tree.getNodeByDataIndex(t); + return e.treePathInfo = Ac(i, this), e + }, + defaultOption: { + zlevel: 0, + z: 2, + center: ["50%", "50%"], + radius: [0, "75%"], + clockwise: !0, + startAngle: 90, + minAngle: 0, + percentPrecision: 2, + stillShowZeroSum: !0, + highlightPolicy: "descendant", + nodeClick: "rootToNode", + renderLabelForZeroData: !1, + label: { + rotate: "radial", + show: !0, + opacity: 1, + align: "center", + position: "inside", + distance: 5, + silent: !0, + emphasis: {} + }, + itemStyle: { + borderWidth: 1, + borderColor: "white", + opacity: 1, + emphasis: {}, + highlight: {opacity: 1}, + downplay: {opacity: .9} + }, + animationType: "expansion", + animationDuration: 1e3, + animationDurationUpdate: 500, + animationEasing: "cubicOut", + data: [], + levels: [], + sort: "desc" + }, + getViewRoot: function () { + return this._viewRoot + }, + resetViewRoot: function (t) { + t ? this._viewRoot = t : t = this._viewRoot; + var e = this.getRawData().tree.root; + t && (t === e || e.contains(t)) || (this._viewRoot = e) + } + }); + var fk = {NONE: "none", DESCENDANT: "descendant", ANCESTOR: "ancestor", SELF: "self"}, pk = 2, gk = 4, + mk = nm.prototype; + mk.updateData = function (t, e, i, o, s) { + this.node = e, e.piece = this, o = o || this._seriesModel, s = s || this._ecModel; + var l = this.childAt(0); + l.dataIndex = e.dataIndex; + var u = e.getModel(), h = e.getLayout(); + h || console.log(e.getLayout()); + var c = a({}, h); + c.label = null; + var d, f = om(e, 0, s), p = u.getModel("itemStyle").getItemStyle(); + d = r({ + lineJoin: "bevel", + fill: (d = "normal" === i ? p : n(u.getModel(i + ".itemStyle").getItemStyle(), p)).fill || f + }, d), t ? (l.setShape(c), l.shape.r = h.r0, fo(l, {shape: {r: h.r}}, o, e.dataIndex), l.useStyle(d)) : "object" == typeof d.fill && d.fill.type || "object" == typeof l.style.fill && l.style.fill.type ? (fo(l, {shape: c}, o), l.useStyle(d)) : fo(l, { + shape: c, + style: d + }, o), this._updateLabel(o, f, i); + var g = u.getShallow("cursor"); + if (g && l.attr("cursor", g), t) { + var m = o.getShallow("highlightPolicy"); + this._initEvents(l, e, o, m) + } + this._seriesModel = o || this._seriesModel, this._ecModel = s || this._ecModel + }, mk.onEmphasis = function (t) { + var e = this; + this.node.hostTree.root.eachNode(function (i) { + i.piece && (e.node === i ? i.piece.updateData(!1, i, "emphasis") : rm(i, e.node, t) ? i.piece.childAt(0).trigger("highlight") : t !== fk.NONE && i.piece.childAt(0).trigger("downplay")) + }) + }, mk.onNormal = function () { + this.node.hostTree.root.eachNode(function (t) { + t.piece && t.piece.updateData(!1, t, "normal") + }) + }, mk.onHighlight = function () { + this.updateData(!1, this.node, "highlight") + }, mk.onDownplay = function () { + this.updateData(!1, this.node, "downplay") + }, mk._updateLabel = function (t, e, i) { + function n(t) { + var e = r.get(t); + return null == e ? a.get(t) : e + } + + var o = this.node.getModel(), a = o.getModel("label"), + r = "normal" === i || "emphasis" === i ? a : o.getModel(i + ".label"), s = o.getModel("emphasis.label"), + l = T(t.getFormattedLabel(this.node.dataIndex, "normal", null, null, "label"), this.node.name); + !1 === n("show") && (l = ""); + var u = this.node.getLayout(), h = r.get("minAngle"); + null == h && (h = a.get("minAngle")), h = h / 180 * Math.PI; + var c = u.endAngle - u.startAngle; + null != h && Math.abs(c) < h && (l = ""); + var d = this.childAt(1); + io(d.style, d.hoverStyle || {}, a, s, { + defaultText: r.getShallow("show") ? l : null, + autoColor: e, + useInsideStyle: !0 + }); + var f, p = (u.startAngle + u.endAngle) / 2, g = Math.cos(p), m = Math.sin(p), v = n("position"), + y = n("distance") || 0, x = n("align"); + "outside" === v ? (f = u.r + y, x = p > Math.PI / 2 ? "right" : "left") : x && "center" !== x ? "left" === x ? (f = u.r0 + y, p > Math.PI / 2 && (x = "right")) : "right" === x && (f = u.r - y, p > Math.PI / 2 && (x = "left")) : (f = (u.r + u.r0) / 2, x = "center"), d.attr("style", { + text: l, + textAlign: x, + textVerticalAlign: n("verticalAlign") || "middle", + opacity: n("opacity") + }); + var _ = f * g + u.cx, w = f * m + u.cy; + d.attr("position", [_, w]); + var b = n("rotate"), S = 0; + "radial" === b ? (S = -p) < -Math.PI / 2 && (S += Math.PI) : "tangential" === b ? (S = Math.PI / 2 - p) > Math.PI / 2 ? S -= Math.PI : S < -Math.PI / 2 && (S += Math.PI) : "number" == typeof b && (S = b * Math.PI / 180), d.attr("rotation", S) + }, mk._initEvents = function (t, e, i, n) { + t.off("mouseover").off("mouseout").off("emphasis").off("normal"); + var o = this, a = function () { + o.onEmphasis(n) + }, r = function () { + o.onNormal() + }; + i.isAnimationEnabled() && t.on("mouseover", a).on("mouseout", r).on("emphasis", a).on("normal", r).on("downplay", function () { + o.onDownplay() + }).on("highlight", function () { + o.onHighlight() + }) + }, u(nm, sw); + gr.extend({ + type: "sunburst", init: function () { + }, render: function (t, e, i, n) { + function o(i, n) { + if (c || !i || i.getValue() || (i = null), i !== l && n !== l) if (n && n.piece) i ? (n.piece.updateData(!1, i, "normal", t, e), s.setItemGraphicEl(i.dataIndex, n.piece)) : a(n); else if (i) { + var o = new nm(i, t, e); + h.add(o), s.setItemGraphicEl(i.dataIndex, o) + } + } + + function a(t) { + t && t.piece && (h.remove(t.piece), t.piece = null) + } + + var r = this; + this.seriesModel = t, this.api = i, this.ecModel = e; + var s = t.getData(), l = s.tree.root, u = t.getViewRoot(), h = this.group, + c = t.get("renderLabelForZeroData"), d = []; + u.eachNode(function (t) { + d.push(t) + }); + var f = this._oldChildren || []; + if (function (t, e) { + function i(t) { + return t.getId() + } + + function n(i, n) { + o(null == i ? null : t[i], null == n ? null : e[n]) + } + + 0 === t.length && 0 === e.length || new Ss(e, t, i, i).add(n).update(n).remove(v(n, null)).execute() + }(d, f), function (i, n) { + if (n.depth > 0) { + r.virtualPiece ? r.virtualPiece.updateData(!1, i, "normal", t, e) : (r.virtualPiece = new nm(i, t, e), h.add(r.virtualPiece)), n.piece._onclickEvent && n.piece.off("click", n.piece._onclickEvent); + var o = function (t) { + r._rootToNode(n.parentNode) + }; + n.piece._onclickEvent = o, r.virtualPiece.on("click", o) + } else r.virtualPiece && (h.remove(r.virtualPiece), r.virtualPiece = null) + }(l, u), n && n.highlight && n.highlight.piece) { + var p = t.getShallow("highlightPolicy"); + n.highlight.piece.onEmphasis(p) + } else if (n && n.unhighlight) { + var g = this.virtualPiece; + !g && l.children.length && (g = l.children[0].piece), g && g.onNormal() + } + this._initEvents(), this._oldChildren = d + }, dispose: function () { + }, _initEvents: function () { + var t = this, e = function (e) { + var i = !1; + t.seriesModel.getViewRoot().eachNode(function (n) { + if (!i && n.piece && n.piece.childAt(0) === e.target) { + var o = n.getModel().get("nodeClick"); + if ("rootToNode" === o) t._rootToNode(n); else if ("link" === o) { + var a = n.getModel(), r = a.get("link"); + if (r) { + var s = a.get("target", !0) || "_blank"; + window.open(r, s) + } + } + i = !0 + } + }) + }; + this.group._onclickEvent && this.group.off("click", this.group._onclickEvent), this.group.on("click", e), this.group._onclickEvent = e + }, _rootToNode: function (t) { + t !== this.seriesModel.getViewRoot() && this.api.dispatchAction({ + type: "sunburstRootToNode", + from: this.uid, + seriesId: this.seriesModel.id, + targetNode: t + }) + }, containPoint: function (t, e) { + var i = e.getData().getItemLayout(0); + if (i) { + var n = t[0] - i.cx, o = t[1] - i.cy, a = Math.sqrt(n * n + o * o); + return a <= i.r && a >= i.r0 + } + } + }); + var vk = "sunburstRootToNode"; + cs({type: vk, update: "updateView"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "sunburst", query: t}, function (e, i) { + var n = Ic(t, [vk], e); + if (n) { + var o = e.getViewRoot(); + o && (t.direction = Dc(o, n.node) ? "rollUp" : "drillDown"), e.resetViewRoot(n.node) + } + }) + }); + var yk = "sunburstHighlight"; + cs({type: yk, update: "updateView"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "sunburst", query: t}, function (e, i) { + var n = Ic(t, [yk], e); + n && (t.highlight = n.node) + }) + }); + cs({type: "sunburstUnhighlight", update: "updateView"}, function (t, e) { + e.eachComponent({mainType: "series", subType: "sunburst", query: t}, function (e, i) { + t.unhighlight = !0 + }) + }); + var xk = Math.PI / 180; + ps(v(JD, "sunburst")), fs(v(function (t, e, i, n) { + e.eachSeriesByType(t, function (t) { + var e = t.get("center"), n = t.get("radius"); + y(n) || (n = [0, n]), y(e) || (e = [e, e]); + var o = i.getWidth(), a = i.getHeight(), r = Math.min(o, a), s = Do(e[0], o), l = Do(e[1], a), + u = Do(n[0], r / 2), h = Do(n[1], r / 2), c = -t.get("startAngle") * xk, f = t.get("minAngle") * xk, + p = t.getData().tree.root, g = t.getViewRoot(), m = g.depth, v = t.get("sort"); + null != v && sm(g, v); + var x = 0; + d(g.children, function (t) { + !isNaN(t.getValue()) && x++ + }); + var _ = g.getValue(), w = Math.PI / (_ || x) * 2, b = g.depth > 0, S = g.height - (b ? -1 : 1), + M = (h - u) / (S || 1), I = t.get("clockwise"), T = t.get("stillShowZeroSum"), D = I ? 1 : -1, + A = function (t, e) { + if (t) { + var i = e; + if (t !== p) { + var n = t.getValue(), o = 0 === _ && T ? w : n * w; + o < f && (o = f), i = e + D * o; + var a = t.depth - m - (b ? -1 : 1), h = u + M * a, c = u + M * (a + 1), g = t.getModel(); + null != g.get("r0") && (h = Do(g.get("r0"), r / 2)), null != g.get("r") && (c = Do(g.get("r"), r / 2)), t.setLayout({ + angle: o, + startAngle: e, + endAngle: i, + clockwise: I, + cx: s, + cy: l, + r0: h, + r: c + }) + } + if (t.children && t.children.length) { + var v = 0; + d(t.children, function (t) { + v += A(t, e + v) + }) + } + return i - e + } + }; + if (b) { + var C = u, L = u + M, k = 2 * Math.PI; + p.setLayout({angle: k, startAngle: c, endAngle: c + k, clockwise: I, cx: s, cy: l, r0: C, r: L}) + } + A(g, c) + }) + }, "sunburst")), hs(v(iA, "sunburst")); + var _k = ["itemStyle"], wk = ["emphasis", "itemStyle"], bk = ["label"], Sk = ["emphasis", "label"], Mk = "e\0\0", + Ik = { + cartesian2d: function (t) { + var e = t.grid.getRect(); + return { + coordSys: {type: "cartesian2d", x: e.x, y: e.y, width: e.width, height: e.height}, + api: { + coord: function (e) { + return t.dataToPoint(e) + }, size: m(um, t) + } + } + }, geo: function (t) { + var e = t.getBoundingRect(); + return { + coordSys: {type: "geo", x: e.x, y: e.y, width: e.width, height: e.height}, + api: { + coord: function (e) { + return t.dataToPoint(e) + }, size: m(hm, t) + } + } + }, singleAxis: function (t) { + var e = t.getRect(); + return { + coordSys: {type: "singleAxis", x: e.x, y: e.y, width: e.width, height: e.height}, + api: { + coord: function (e) { + return t.dataToPoint(e) + }, size: m(cm, t) + } + } + }, polar: function (t) { + var e = t.getRadiusAxis(), i = t.getAngleAxis(), n = e.getExtent(); + return n[0] > n[1] && n.reverse(), { + coordSys: {type: "polar", cx: t.cx, cy: t.cy, r: n[1], r0: n[0]}, + api: { + coord: m(function (n) { + var o = e.dataToRadius(n[0]), a = i.dataToAngle(n[1]), r = t.coordToPoint([o, a]); + return r.push(o, a * Math.PI / 180), r + }), size: m(dm, t) + } + } + }, calendar: function (t) { + var e = t.getRect(), i = t.getRangeInfo(); + return { + coordSys: { + type: "calendar", + x: e.x, + y: e.y, + width: e.width, + height: e.height, + cellWidth: t.getCellWidth(), + cellHeight: t.getCellHeight(), + rangeInfo: {start: i.start, end: i.end, weeks: i.weeks, dayCount: i.allDay} + }, api: { + coord: function (e, i) { + return t.dataToPoint(e, i) + } + } + } + } + }; + xs({ + type: "series.custom", + dependencies: ["grid", "polar", "geo", "singleAxis", "calendar"], + defaultOption: {coordinateSystem: "cartesian2d", zlevel: 0, z: 2, legendHoverLink: !0}, + getInitialData: function (t, e) { + return Xs(this.getSource(), this) + } + }), _s({ + type: "custom", _data: null, render: function (t, e, i) { + var n = this._data, o = t.getData(), a = this.group, r = mm(t, o, e, i); + this.group.removeAll(), o.diff(n).add(function (e) { + ym(null, e, r(e), t, a, o) + }).update(function (e, i) { + ym(n.getItemGraphicEl(i), e, r(e), t, a, o) + }).remove(function (t) { + var e = n.getItemGraphicEl(t); + e && a.remove(e) + }).execute(), this._data = o + }, incrementalPrepareRender: function (t, e, i) { + this.group.removeAll(), this._data = null + }, incrementalRender: function (t, e, i, n) { + for (var o = e.getData(), a = mm(e, o, i, n), r = t.start; r < t.end; r++) ym(null, r, a(r), e, this.group, o).traverse(function (t) { + t.isGroup || (t.incremental = !0, t.useHoverLayer = !0) + }) + }, dispose: B + }), us(function (t) { + var e = t.graphic; + y(e) ? e[0] && e[0].elements ? t.graphic = [t.graphic[0]] : t.graphic = [{elements: e}] : e && !e.elements && (t.graphic = [{elements: [e]}]) + }); + var Tk = vs({ + type: "graphic", + defaultOption: {elements: [], parentId: null}, + _elOptionsToUpdate: null, + mergeOption: function (t) { + var e = this.option.elements; + this.option.elements = null, Tk.superApply(this, "mergeOption", arguments), this.option.elements = e + }, + optionUpdated: function (t, e) { + var i = this.option, n = (e ? i : t).elements, o = i.elements = e ? [] : i.elements, a = []; + this._flatten(n, a); + var r = Di(o, a); + Ai(r); + var s = this._elOptionsToUpdate = []; + d(r, function (t, e) { + var i = t.option; + i && (s.push(i), Am(t, i), Cm(o, e, i), Lm(o[e], i)) + }, this); + for (var l = o.length - 1; l >= 0; l--) null == o[l] ? o.splice(l, 1) : delete o[l].$action + }, + _flatten: function (t, e, i) { + d(t, function (t) { + if (t) { + i && (t.parentOption = i), e.push(t); + var n = t.children; + "group" === t.type && n && this._flatten(n, e, t), delete t.children + } + }, this) + }, + useElOptionsToUpdate: function () { + var t = this._elOptionsToUpdate; + return this._elOptionsToUpdate = null, t + } + }); + ys({ + type: "graphic", init: function (t, e) { + this._elMap = R(), this._lastGraphicModel + }, render: function (t, e, i) { + t !== this._lastGraphicModel && this._clear(), this._lastGraphicModel = t, this._updateElements(t, i), this._relocate(t, i) + }, _updateElements: function (t, e) { + var i = t.useElOptionsToUpdate(); + if (i) { + var n = this._elMap, o = this.group; + d(i, function (t) { + var e = t.$action, i = t.id, a = n.get(i), r = t.parentId, s = null != r ? n.get(r) : o; + if ("text" === t.type) { + var l = t.style; + t.hv && t.hv[1] && (l.textVerticalAlign = l.textBaseline = null), !l.hasOwnProperty("textFill") && l.fill && (l.textFill = l.fill), !l.hasOwnProperty("textStroke") && l.stroke && (l.textStroke = l.stroke) + } + var u = Tm(t); + e && "merge" !== e ? "replace" === e ? (Im(a, n), Mm(i, s, u, n)) : "remove" === e && Im(a, n) : a ? a.attr(u) : Mm(i, s, u, n); + var h = n.get(i); + h && (h.__ecGraphicWidth = t.width, h.__ecGraphicHeight = t.height) + }) + } + }, _relocate: function (t, e) { + for (var i = t.option.elements, n = this.group, o = this._elMap, a = i.length - 1; a >= 0; a--) { + var r = i[a], s = o.get(r.id); + if (s) { + var l = s.parent; + ta(s, r, l === n ? {width: e.getWidth(), height: e.getHeight()} : { + width: l.__ecGraphicWidth || 0, + height: l.__ecGraphicHeight || 0 + }, null, {hv: r.hv, boundingMode: r.bounding}) + } + } + }, _clear: function () { + var t = this._elMap; + t.each(function (e) { + Im(e, t) + }), this._elMap = R() + }, dispose: function () { + this._clear() + } + }); + var Dk = vs({ + type: "legend.plain", + dependencies: ["series"], + layoutMode: {type: "box", ignoreSize: !0}, + init: function (t, e, i) { + this.mergeDefaultAndTheme(t, i), t.selected = t.selected || {} + }, + mergeOption: function (t) { + Dk.superCall(this, "mergeOption", t) + }, + optionUpdated: function () { + this._updateData(this.ecModel); + var t = this._data; + if (t[0] && "single" === this.get("selectedMode")) { + for (var e = !1, i = 0; i < t.length; i++) { + var n = t[i].get("name"); + if (this.isSelected(n)) { + this.select(n), e = !0; + break + } + } + !e && this.select(t[0].get("name")) + } + }, + _updateData: function (t) { + var e = [], i = []; + t.eachRawSeries(function (n) { + var o = n.name; + i.push(o); + var a; + if (n.legendDataProvider) { + var r = n.legendDataProvider(), s = r.mapArray(r.getName); + t.isSeriesFiltered(n) || (i = i.concat(s)), s.length ? e = e.concat(s) : a = !0 + } else a = !0; + a && Ci(n) && e.push(n.name) + }), this._availableNames = i; + var n = f(this.get("data") || e, function (t) { + return "string" != typeof t && "number" != typeof t || (t = {name: t}), new wo(t, this, this.ecModel) + }, this); + this._data = n + }, + getData: function () { + return this._data + }, + select: function (t) { + var e = this.option.selected; + "single" === this.get("selectedMode") && d(this._data, function (t) { + e[t.get("name")] = !1 + }), e[t] = !0 + }, + unSelect: function (t) { + "single" !== this.get("selectedMode") && (this.option.selected[t] = !1) + }, + toggleSelected: function (t) { + var e = this.option.selected; + e.hasOwnProperty(t) || (e[t] = !0), this[e[t] ? "unSelect" : "select"](t) + }, + isSelected: function (t) { + var e = this.option.selected; + return !(e.hasOwnProperty(t) && !e[t]) && l(this._availableNames, t) >= 0 + }, + defaultOption: { + zlevel: 0, + z: 4, + show: !0, + orient: "horizontal", + left: "center", + top: 0, + align: "auto", + backgroundColor: "rgba(0,0,0,0)", + borderColor: "#ccc", + borderRadius: 0, + borderWidth: 0, + padding: 5, + itemGap: 10, + itemWidth: 25, + itemHeight: 14, + inactiveColor: "#ccc", + textStyle: {color: "#333"}, + selectedMode: !0, + tooltip: {show: !1} + } + }); + cs("legendToggleSelect", "legendselectchanged", v(km, "toggleSelected")), cs("legendSelect", "legendselected", v(km, "select")), cs("legendUnSelect", "legendunselected", v(km, "unSelect")); + var Ak = v, Ck = d, Lk = sw, kk = ys({ + type: "legend.plain", newlineDisabled: !1, init: function () { + this.group.add(this._contentGroup = new Lk), this._backgroundEl + }, getContentGroup: function () { + return this._contentGroup + }, render: function (t, e, i) { + if (this.resetInner(), t.get("show", !0)) { + var n = t.get("align"); + n && "auto" !== n || (n = "right" === t.get("left") && "vertical" === t.get("orient") ? "right" : "left"), this.renderInner(n, t, e, i); + var o = t.getBoxLayoutParams(), a = {width: i.getWidth(), height: i.getHeight()}, s = t.get("padding"), + l = Qo(o, a, s), u = this.layoutInner(t, n, l), + h = Qo(r({width: u.width, height: u.height}, o), a, s); + this.group.attr("position", [h.x - u.x, h.y - u.y]), this.group.add(this._backgroundEl = Nm(u, t)) + } + }, resetInner: function () { + this.getContentGroup().removeAll(), this._backgroundEl && this.group.remove(this._backgroundEl) + }, renderInner: function (t, e, i, n) { + var o = this.getContentGroup(), a = R(), r = e.get("selectedMode"), s = []; + i.eachRawSeries(function (t) { + !t.get("legendHoverLink") && s.push(t.id) + }), Ck(e.getData(), function (l, u) { + var h = l.get("name"); + if (this.newlineDisabled || "" !== h && "\n" !== h) { + var c = i.getSeriesByName(h)[0]; + if (!a.get(h)) if (c) { + var d = c.getData(), f = d.getVisual("color"); + "function" == typeof f && (f = f(c.getDataParams(0))); + var p = d.getVisual("legendSymbol") || "roundRect", g = d.getVisual("symbol"); + this._createItem(h, u, l, e, p, g, t, f, r).on("click", Ak(Om, h, n)).on("mouseover", Ak(Em, c, null, n, s)).on("mouseout", Ak(Rm, c, null, n, s)), a.set(h, !0) + } else i.eachRawSeries(function (i) { + if (!a.get(h) && i.legendDataProvider) { + var o = i.legendDataProvider(), c = o.indexOfName(h); + if (c < 0) return; + var d = o.getItemVisual(c, "color"); + this._createItem(h, u, l, e, "roundRect", null, t, d, r).on("click", Ak(Om, h, n)).on("mouseover", Ak(Em, i, h, n, s)).on("mouseout", Ak(Rm, i, h, n, s)), a.set(h, !0) + } + }, this) + } else o.add(new Lk({newline: !0})) + }, this) + }, _createItem: function (t, e, i, n, o, r, s, l, u) { + var h = n.get("itemWidth"), c = n.get("itemHeight"), d = n.get("inactiveColor"), + f = n.get("symbolKeepAspect"), p = n.isSelected(t), g = new Lk, m = i.getModel("textStyle"), + v = i.get("icon"), y = i.getModel("tooltip"), x = y.parentModel; + if (o = v || o, g.add(Tl(o, 0, 0, h, c, p ? l : d, null == f || f)), !v && r && (r !== o || "none" == r)) { + var _ = .8 * c; + "none" === r && (r = "circle"), g.add(Tl(r, (h - _) / 2, (c - _) / 2, _, _, p ? l : d, null == f || f)) + } + var w = "left" === s ? h + 5 : -5, b = s, S = n.get("formatter"), M = t; + "string" == typeof S && S ? M = S.replace("{name}", null != t ? t : "") : "function" == typeof S && (M = S(t)), g.add(new fS({ + style: no({}, m, { + text: M, + x: w, + y: c / 2, + textFill: p ? m.getTextColor() : d, + textAlign: b, + textVerticalAlign: "middle" + }) + })); + var I = new SS({ + shape: g.getBoundingRect(), + invisible: !0, + tooltip: y.get("show") ? a({ + content: t, + formatter: x.get("formatter", !0) || function () { + return t + }, + formatterParams: {componentType: "legend", legendIndex: n.componentIndex, name: t, $vars: ["name"]} + }, y.option) : null + }); + return g.add(I), g.eachChild(function (t) { + t.silent = !0 + }), I.silent = !u, this.getContentGroup().add(g), eo(g), g.__legendDataIndex = e, g + }, layoutInner: function (t, e, i) { + var n = this.getContentGroup(); + sM(t.get("orient"), n, t.get("itemGap"), i.width, i.height); + var o = n.getBoundingRect(); + return n.attr("position", [-o.x, -o.y]), this.group.getBoundingRect() + } + }); + hs(function (t) { + var e = t.findComponents({mainType: "legend"}); + e && e.length && t.filterSeries(function (t) { + for (var i = 0; i < e.length; i++) if (!e[i].isSelected(t.name)) return !1; + return !0 + }) + }), hM.registerSubTypeDefaulter("legend", function () { + return "plain" + }); + var Pk = Dk.extend({ + type: "legend.scroll", + setScrollDataIndex: function (t) { + this.option.scrollDataIndex = t + }, + defaultOption: { + scrollDataIndex: 0, + pageButtonItemGap: 5, + pageButtonGap: null, + pageButtonPosition: "end", + pageFormatter: "{current}/{total}", + pageIcons: { + horizontal: ["M0,0L12,-10L12,10z", "M0,0L-12,-10L-12,10z"], + vertical: ["M0,0L20,0L10,-20z", "M0,0L20,0L10,20z"] + }, + pageIconColor: "#2f4554", + pageIconInactiveColor: "#aaa", + pageIconSize: 15, + pageTextStyle: {color: "#333"}, + animationDurationUpdate: 800 + }, + init: function (t, e, i, n) { + var o = na(t); + Pk.superCall(this, "init", t, e, i, n), zm(this, t, o) + }, + mergeOption: function (t, e) { + Pk.superCall(this, "mergeOption", t, e), zm(this, this.option, t) + }, + getOrient: function () { + return "vertical" === this.get("orient") ? {index: 1, name: "vertical"} : {index: 0, name: "horizontal"} + } + }), Nk = sw, Ok = ["width", "height"], Ek = ["x", "y"], Rk = kk.extend({ + type: "legend.scroll", newlineDisabled: !0, init: function () { + Rk.superCall(this, "init"), this._currentIndex = 0, this.group.add(this._containerGroup = new Nk), this._containerGroup.add(this.getContentGroup()), this.group.add(this._controllerGroup = new Nk), this._showController + }, resetInner: function () { + Rk.superCall(this, "resetInner"), this._controllerGroup.removeAll(), this._containerGroup.removeClipPath(), this._containerGroup.__rectSize = null + }, renderInner: function (t, e, i, n) { + function o(t, i) { + var o = t + "DataIndex", + l = _o(e.get("pageIcons", !0)[e.getOrient().name][i], {onclick: m(a._pageGo, a, o, e, n)}, { + x: -s[0] / 2, + y: -s[1] / 2, + width: s[0], + height: s[1] + }); + l.name = t, r.add(l) + } + + var a = this; + Rk.superCall(this, "renderInner", t, e, i, n); + var r = this._controllerGroup, s = e.get("pageIconSize", !0); + y(s) || (s = [s, s]), o("pagePrev", 0); + var l = e.getModel("pageTextStyle"); + r.add(new fS({ + name: "pageText", + style: { + textFill: l.getTextColor(), + font: l.getFont(), + textVerticalAlign: "middle", + textAlign: "center" + }, + silent: !0 + })), o("pageNext", 1) + }, layoutInner: function (t, e, i) { + var n = this.getContentGroup(), o = this._containerGroup, a = this._controllerGroup, + r = t.getOrient().index, s = Ok[r], l = Ok[1 - r], u = Ek[1 - r]; + sM(t.get("orient"), n, t.get("itemGap"), r ? i.width : null, r ? null : i.height), sM("horizontal", a, t.get("pageButtonItemGap", !0)); + var h = n.getBoundingRect(), c = a.getBoundingRect(), d = this._showController = h[s] > i[s], + f = [-h.x, -h.y]; + f[r] = n.position[r]; + var p = [0, 0], g = [-c.x, -c.y], m = D(t.get("pageButtonGap", !0), t.get("itemGap", !0)); + d && ("end" === t.get("pageButtonPosition", !0) ? g[r] += i[s] - c[s] : p[r] += c[s] + m), g[1 - r] += h[l] / 2 - c[l] / 2, n.attr("position", f), o.attr("position", p), a.attr("position", g); + var v = this.group.getBoundingRect(); + if ((v = { + x: 0, + y: 0 + })[s] = d ? i[s] : h[s], v[l] = Math.max(h[l], c[l]), v[u] = Math.min(0, c[u] + g[1 - r]), o.__rectSize = i[s], d) { + var y = {x: 0, y: 0}; + y[s] = Math.max(i[s] - c[s] - m, 0), y[l] = v[l], o.setClipPath(new SS({shape: y})), o.__rectSize = y[s] + } else a.eachChild(function (t) { + t.attr({invisible: !0, silent: !0}) + }); + var x = this._getPageInfo(t); + return null != x.pageIndex && fo(n, {position: x.contentPosition}, !!d && t), this._updatePageInfoView(t, x), v + }, _pageGo: function (t, e, i) { + var n = this._getPageInfo(e)[t]; + null != n && i.dispatchAction({type: "legendScroll", scrollDataIndex: n, legendId: e.id}) + }, _updatePageInfoView: function (t, e) { + var i = this._controllerGroup; + d(["pagePrev", "pageNext"], function (n) { + var o = null != e[n + "DataIndex"], a = i.childOfName(n); + a && (a.setStyle("fill", o ? t.get("pageIconColor", !0) : t.get("pageIconInactiveColor", !0)), a.cursor = o ? "pointer" : "default") + }); + var n = i.childOfName("pageText"), o = t.get("pageFormatter"), a = e.pageIndex, r = null != a ? a + 1 : 0, + s = e.pageCount; + n && o && n.setStyle("text", _(o) ? o.replace("{current}", r).replace("{total}", s) : o({ + current: r, + total: s + })) + }, _getPageInfo: function (t) { + function e(t) { + var e = t.getBoundingRect().clone(); + return e[f] += t.position[h], e + } + + var i, n, o, a, r = t.get("scrollDataIndex", !0), s = this.getContentGroup(), l = s.getBoundingRect(), + u = this._containerGroup.__rectSize, h = t.getOrient().index, c = Ok[h], d = Ok[1 - h], f = Ek[h], + p = s.position.slice(); + this._showController ? s.eachChild(function (t) { + t.__legendDataIndex === r && (a = t) + }) : a = s.childAt(0); + var g = u ? Math.ceil(l[c] / u) : 0; + if (a) { + var m = a.getBoundingRect(), v = a.position[h] + m[f]; + p[h] = -v - l[f], i = Math.floor(g * (v + m[f] + u / 2) / l[c]), i = l[c] && g ? Math.max(0, Math.min(g - 1, i)) : -1; + var y = {x: 0, y: 0}; + y[c] = u, y[d] = l[d], y[f] = -p[h] - l[f]; + var x, _ = s.children(); + if (s.eachChild(function (t, i) { + var n = e(t); + n.intersect(y) && (null == x && (x = i), o = t.__legendDataIndex), i === _.length - 1 && n[f] + n[c] <= y[f] + y[c] && (o = null) + }), null != x) { + var w = e(_[x]); + if (y[f] = w[f] + w[c] - y[c], x <= 0 && w[f] >= y[f]) n = null; else { + for (; x > 0 && e(_[x - 1]).intersect(y);) x--; + n = _[x].__legendDataIndex + } + } + } + return {contentPosition: p, pageIndex: i, pageCount: g, pagePrevDataIndex: n, pageNextDataIndex: o} + } + }); + cs("legendScroll", "legendscroll", function (t, e) { + var i = t.scrollDataIndex; + null != i && e.eachComponent({mainType: "legend", subType: "scroll", query: t}, function (t) { + t.setScrollDataIndex(i) + }) + }), vs({ + type: "tooltip", + dependencies: ["axisPointer"], + defaultOption: { + zlevel: 0, + z: 8, + show: !0, + showContent: !0, + trigger: "item", + triggerOn: "mousemove|click", + alwaysShowContent: !1, + displayMode: "single", + confine: !1, + showDelay: 0, + hideDelay: 100, + transitionDuration: .4, + enterable: !1, + backgroundColor: "rgba(50,50,50,0.7)", + borderColor: "#333", + borderRadius: 4, + borderWidth: 0, + padding: 5, + extraCssText: "", + axisPointer: { + type: "line", + axis: "auto", + animation: "auto", + animationDurationUpdate: 200, + animationEasingUpdate: "exponentialOut", + crossStyle: {color: "#999", width: 1, type: "dashed", textStyle: {}} + }, + textStyle: {color: "#fff", fontSize: 14} + } + }); + var zk = d, Bk = Ho, Vk = ["", "-webkit-", "-moz-", "-o-"]; + Wm.prototype = { + constructor: Wm, _enterable: !0, update: function () { + var t = this._container, e = t.currentStyle || document.defaultView.getComputedStyle(t), i = t.style; + "absolute" !== i.position && "absolute" !== e.position && (i.position = "relative") + }, show: function (t) { + clearTimeout(this._hideTimeout); + var e = this.el; + e.style.cssText = "position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;" + Gm(t) + ";left:" + this._x + "px;top:" + this._y + "px;" + (t.get("extraCssText") || ""), e.style.display = e.innerHTML ? "block" : "none", this._show = !0 + }, setContent: function (t) { + this.el.innerHTML = null == t ? "" : t + }, setEnterable: function (t) { + this._enterable = t + }, getSize: function () { + var t = this.el; + return [t.clientWidth, t.clientHeight] + }, moveTo: function (t, e) { + var i, n = this._zr; + n && n.painter && (i = n.painter.getViewportRootOffset()) && (t += i.offsetLeft, e += i.offsetTop); + var o = this.el.style; + o.left = t + "px", o.top = e + "px", this._x = t, this._y = e + }, hide: function () { + this.el.style.display = "none", this._show = !1 + }, hideLater: function (t) { + !this._show || this._inContent && this._enterable || (t ? (this._hideDelay = t, this._show = !1, this._hideTimeout = setTimeout(m(this.hide, this), t)) : this.hide()) + }, isShow: function () { + return this._show + } + }; + var Gk = m, Wk = d, Fk = Do, Hk = new SS({shape: {x: -1, y: -1, width: 2, height: 2}}); + ys({ + type: "tooltip", init: function (t, e) { + if (!a_.node) { + var i = new Wm(e.getDom(), e); + this._tooltipContent = i + } + }, render: function (t, e, i) { + if (!a_.node && !a_.wxa) { + this.group.removeAll(), this._tooltipModel = t, this._ecModel = e, this._api = i, this._lastDataByCoordSys = null, this._alwaysShowContent = t.get("alwaysShowContent"); + var n = this._tooltipContent; + n.update(), n.setEnterable(t.get("enterable")), this._initGlobalListener(), this._keepShow() + } + }, _initGlobalListener: function () { + var t = this._tooltipModel.get("triggerOn"); + Dg("itemTooltip", this._api, Gk(function (e, i, n) { + "none" !== t && (t.indexOf(e) >= 0 ? this._tryShow(i, n) : "leave" === e && this._hide(n)) + }, this)) + }, _keepShow: function () { + var t = this._tooltipModel, e = this._ecModel, i = this._api; + if (null != this._lastX && null != this._lastY && "none" !== t.get("triggerOn")) { + var n = this; + clearTimeout(this._refreshUpdateTimeout), this._refreshUpdateTimeout = setTimeout(function () { + n.manuallyShowTip(t, e, i, {x: n._lastX, y: n._lastY}) + }) + } + }, manuallyShowTip: function (t, e, i, n) { + if (n.from !== this.uid && !a_.node) { + var o = Hm(n, i); + this._ticket = ""; + var a = n.dataByCoordSys; + if (n.tooltip && null != n.x && null != n.y) { + var r = Hk; + r.position = [n.x, n.y], r.update(), r.tooltip = n.tooltip, this._tryShow({ + offsetX: n.x, + offsetY: n.y, + target: r + }, o) + } else if (a) this._tryShow({ + offsetX: n.x, + offsetY: n.y, + position: n.position, + event: {}, + dataByCoordSys: n.dataByCoordSys, + tooltipOption: n.tooltipOption + }, o); else if (null != n.seriesIndex) { + if (this._manuallyAxisShowTip(t, e, i, n)) return; + var s = KL(n, e), l = s.point[0], u = s.point[1]; + null != l && null != u && this._tryShow({ + offsetX: l, + offsetY: u, + position: n.position, + target: s.el, + event: {} + }, o) + } else null != n.x && null != n.y && (i.dispatchAction({ + type: "updateAxisPointer", + x: n.x, + y: n.y + }), this._tryShow({ + offsetX: n.x, + offsetY: n.y, + position: n.position, + target: i.getZr().findHover(n.x, n.y).target, + event: {} + }, o)) + } + }, manuallyHideTip: function (t, e, i, n) { + var o = this._tooltipContent; + !this._alwaysShowContent && this._tooltipModel && o.hideLater(this._tooltipModel.get("hideDelay")), this._lastX = this._lastY = null, n.from !== this.uid && this._hide(Hm(n, i)) + }, _manuallyAxisShowTip: function (t, e, i, n) { + var o = n.seriesIndex, a = n.dataIndex, r = e.getComponent("axisPointer").coordSysAxesInfo; + if (null != o && null != a && null != r) { + var s = e.getSeriesByIndex(o); + if (s && "axis" === (t = Fm([s.getData().getItemModel(a), s, (s.coordinateSystem || {}).model, t])).get("trigger")) return i.dispatchAction({ + type: "updateAxisPointer", + seriesIndex: o, + dataIndex: a, + position: n.position + }), !0 + } + }, _tryShow: function (t, e) { + var i = t.target; + if (this._tooltipModel) { + this._lastX = t.offsetX, this._lastY = t.offsetY; + var n = t.dataByCoordSys; + n && n.length ? this._showAxisTooltip(n, t) : i && null != i.dataIndex ? (this._lastDataByCoordSys = null, this._showSeriesItemTooltip(t, i, e)) : i && i.tooltip ? (this._lastDataByCoordSys = null, this._showComponentItemTooltip(t, i, e)) : (this._lastDataByCoordSys = null, this._hide(e)) + } + }, _showOrMove: function (t, e) { + var i = t.get("showDelay"); + e = m(e, this), clearTimeout(this._showTimout), i > 0 ? this._showTimout = setTimeout(e, i) : e() + }, _showAxisTooltip: function (t, e) { + var i = this._ecModel, n = this._tooltipModel, o = [e.offsetX, e.offsetY], a = [], r = [], + s = Fm([e.tooltipOption, n]); + Wk(t, function (t) { + Wk(t.dataByAxis, function (t) { + var e = i.getComponent(t.axisDim + "Axis", t.axisIndex), n = t.value, o = []; + if (e && null != n) { + var s = Hg(n, e.axis, i, t.seriesDataIndices, t.valueLabelOpt); + d(t.seriesDataIndices, function (a) { + var l = i.getSeriesByIndex(a.seriesIndex), u = a.dataIndexInside, + h = l && l.getDataParams(u); + h.axisDim = t.axisDim, h.axisIndex = t.axisIndex, h.axisType = t.axisType, h.axisId = t.axisId, h.axisValue = bl(e.axis, n), h.axisValueLabel = s, h && (r.push(h), o.push(l.formatTooltip(u, !0))) + }); + var l = s; + a.push((l ? Zo(l) + "
" : "") + o.join("
")) + } + }) + }, this), a.reverse(), a = a.join("

"); + var l = e.position; + this._showOrMove(s, function () { + this._updateContentNotChangedOnAxis(t) ? this._updatePosition(s, l, o[0], o[1], this._tooltipContent, r) : this._showTooltipContent(s, a, r, Math.random(), o[0], o[1], l) + }) + }, _showSeriesItemTooltip: function (t, e, i) { + var n = this._ecModel, o = e.seriesIndex, a = n.getSeriesByIndex(o), r = e.dataModel || a, s = e.dataIndex, + l = e.dataType, u = r.getData(), + h = Fm([u.getItemModel(s), r, a && (a.coordinateSystem || {}).model, this._tooltipModel]), + c = h.get("trigger"); + if (null == c || "item" === c) { + var d = r.getDataParams(s, l), f = r.formatTooltip(s, !1, l), p = "item_" + r.name + "_" + s; + this._showOrMove(h, function () { + this._showTooltipContent(h, f, d, p, t.offsetX, t.offsetY, t.position, t.target) + }), i({ + type: "showTip", + dataIndexInside: s, + dataIndex: u.getRawIndex(s), + seriesIndex: o, + from: this.uid + }) + } + }, _showComponentItemTooltip: function (t, e, i) { + var n = e.tooltip; + if ("string" == typeof n) { + var o = n; + n = {content: o, formatter: o} + } + var a = new wo(n, this._tooltipModel, this._ecModel), r = a.get("content"), s = Math.random(); + this._showOrMove(a, function () { + this._showTooltipContent(a, r, a.get("formatterParams") || {}, s, t.offsetX, t.offsetY, t.position, e) + }), i({type: "showTip", from: this.uid}) + }, _showTooltipContent: function (t, e, i, n, o, a, r, s) { + if (this._ticket = "", t.get("showContent") && t.get("show")) { + var l = this._tooltipContent, u = t.get("formatter"); + r = r || t.get("position"); + var h = e; + if (u && "string" == typeof u) h = Uo(u, i, !0); else if ("function" == typeof u) { + var c = Gk(function (e, n) { + e === this._ticket && (l.setContent(n), this._updatePosition(t, r, o, a, l, i, s)) + }, this); + this._ticket = n, h = u(i, n, c) + } + l.setContent(h), l.show(t), this._updatePosition(t, r, o, a, l, i, s) + } + }, _updatePosition: function (t, e, i, n, o, a, r) { + var s = this._api.getWidth(), l = this._api.getHeight(); + e = e || t.get("position"); + var u = o.getSize(), h = t.get("align"), c = t.get("verticalAlign"), d = r && r.getBoundingRect().clone(); + if (r && d.applyTransform(r.transform), "function" == typeof e && (e = e([i, n], a, o.el, d, { + viewSize: [s, l], + contentSize: u.slice() + })), y(e)) i = Fk(e[0], s), n = Fk(e[1], l); else if (w(e)) { + e.width = u[0], e.height = u[1]; + var f = Qo(e, {width: s, height: l}); + i = f.x, n = f.y, h = null, c = null + } else "string" == typeof e && r ? (i = (p = jm(e, d, u))[0], n = p[1]) : (i = (p = Zm(i, n, o.el, s, l, h ? null : 20, c ? null : 20))[0], n = p[1]); + if (h && (i -= Ym(h) ? u[0] / 2 : "right" === h ? u[0] : 0), c && (n -= Ym(c) ? u[1] / 2 : "bottom" === c ? u[1] : 0), t.get("confine")) { + var p = Um(i, n, o.el, s, l); + i = p[0], n = p[1] + } + o.moveTo(i, n) + }, _updateContentNotChangedOnAxis: function (t) { + var e = this._lastDataByCoordSys, i = !!e && e.length === t.length; + return i && Wk(e, function (e, n) { + var o = e.dataByAxis || {}, a = (t[n] || {}).dataByAxis || []; + (i &= o.length === a.length) && Wk(o, function (t, e) { + var n = a[e] || {}, o = t.seriesDataIndices || [], r = n.seriesDataIndices || []; + (i &= t.value === n.value && t.axisType === n.axisType && t.axisId === n.axisId && o.length === r.length) && Wk(o, function (t, e) { + var n = r[e]; + i &= t.seriesIndex === n.seriesIndex && t.dataIndex === n.dataIndex + }) + }) + }), this._lastDataByCoordSys = t, !!i + }, _hide: function (t) { + this._lastDataByCoordSys = null, t({type: "hideTip", from: this.uid}) + }, dispose: function (t, e) { + a_.node || a_.wxa || (this._tooltipContent.hide(), Ng("itemTooltip", e)) + } + }), cs({type: "showTip", event: "showTip", update: "tooltip:manuallyShowTip"}, function () { + }), cs({type: "hideTip", event: "hideTip", update: "tooltip:manuallyHideTip"}, function () { + }), Jm.prototype = { + constructor: Jm, pointToData: function (t, e) { + return this.polar.pointToData(t, e)["radius" === this.dim ? 0 : 1] + }, dataToRadius: qT.prototype.dataToCoord, radiusToData: qT.prototype.coordToData + }, u(Jm, qT), Qm.prototype = { + constructor: Qm, pointToData: function (t, e) { + return this.polar.pointToData(t, e)["radius" === this.dim ? 0 : 1] + }, dataToAngle: qT.prototype.dataToCoord, angleToData: qT.prototype.coordToData + }, u(Qm, qT); + var Zk = function (t) { + this.name = t || "", this.cx = 0, this.cy = 0, this._radiusAxis = new Jm, this._angleAxis = new Qm, this._radiusAxis.polar = this._angleAxis.polar = this + }; + Zk.prototype = { + type: "polar", + axisPointerEnabled: !0, + constructor: Zk, + dimensions: ["radius", "angle"], + model: null, + containPoint: function (t) { + var e = this.pointToCoord(t); + return this._radiusAxis.contain(e[0]) && this._angleAxis.contain(e[1]) + }, + containData: function (t) { + return this._radiusAxis.containData(t[0]) && this._angleAxis.containData(t[1]) + }, + getAxis: function (t) { + return this["_" + t + "Axis"] + }, + getAxes: function () { + return [this._radiusAxis, this._angleAxis] + }, + getAxesByScale: function (t) { + var e = [], i = this._angleAxis, n = this._radiusAxis; + return i.scale.type === t && e.push(i), n.scale.type === t && e.push(n), e + }, + getAngleAxis: function () { + return this._angleAxis + }, + getRadiusAxis: function () { + return this._radiusAxis + }, + getOtherAxis: function (t) { + var e = this._angleAxis; + return t === e ? this._radiusAxis : e + }, + getBaseAxis: function () { + return this.getAxesByScale("ordinal")[0] || this.getAxesByScale("time")[0] || this.getAngleAxis() + }, + getTooltipAxes: function (t) { + var e = null != t && "auto" !== t ? this.getAxis(t) : this.getBaseAxis(); + return {baseAxes: [e], otherAxes: [this.getOtherAxis(e)]} + }, + dataToPoint: function (t, e) { + return this.coordToPoint([this._radiusAxis.dataToRadius(t[0], e), this._angleAxis.dataToAngle(t[1], e)]) + }, + pointToData: function (t, e) { + var i = this.pointToCoord(t); + return [this._radiusAxis.radiusToData(i[0], e), this._angleAxis.angleToData(i[1], e)] + }, + pointToCoord: function (t) { + var e = t[0] - this.cx, i = t[1] - this.cy, n = this.getAngleAxis(), o = n.getExtent(), + a = Math.min(o[0], o[1]), r = Math.max(o[0], o[1]); + n.inverse ? a = r - 360 : r = a + 360; + var s = Math.sqrt(e * e + i * i); + e /= s, i /= s; + for (var l = Math.atan2(-i, e) / Math.PI * 180, u = l < a ? 1 : -1; l < a || l > r;) l += 360 * u; + return [s, l] + }, + coordToPoint: function (t) { + var e = t[0], i = t[1] / 180 * Math.PI; + return [Math.cos(i) * e + this.cx, -Math.sin(i) * e + this.cy] + } + }; + var Uk = hM.extend({ + type: "polarAxis", axis: null, getCoordSysModel: function () { + return this.ecModel.queryComponents({ + mainType: "polar", + index: this.option.polarIndex, + id: this.option.polarId + })[0] + } + }); + n(Uk.prototype, ET); + var Xk = { + angle: {startAngle: 90, clockwise: !0, splitNumber: 12, axisLabel: {rotate: !1}}, + radius: {splitNumber: 5} + }; + MD("angle", Uk, tv, Xk.angle), MD("radius", Uk, tv, Xk.radius), vs({ + type: "polar", + dependencies: ["polarAxis", "angleAxis"], + coordinateSystem: null, + findAxisModel: function (t) { + var e; + return this.ecModel.eachComponent(t, function (t) { + t.getCoordSysModel() === this && (e = t) + }, this), e + }, + defaultOption: {zlevel: 0, z: 0, center: ["50%", "50%"], radius: "80%"} + }); + var jk = { + dimensions: Zk.prototype.dimensions, create: function (t, e) { + var i = []; + return t.eachComponent("polar", function (t, n) { + var o = new Zk(n); + o.update = iv; + var a = o.getRadiusAxis(), r = o.getAngleAxis(), s = t.findAxisModel("radiusAxis"), + l = t.findAxisModel("angleAxis"); + nv(a, s), nv(r, l), ev(o, t, e), i.push(o), t.coordinateSystem = o, o.model = t + }), t.eachSeries(function (e) { + if ("polar" === e.get("coordinateSystem")) { + var i = t.queryComponents({mainType: "polar", index: e.get("polarIndex"), id: e.get("polarId")})[0]; + e.coordinateSystem = i.coordinateSystem + } + }), i + } + }; + Ca.register("polar", jk); + var Yk = ["axisLine", "axisLabel", "axisTick", "splitLine", "splitArea"]; + ED.extend({ + type: "angleAxis", axisPointerClass: "PolarAxisPointer", render: function (t, e) { + if (this.group.removeAll(), t.get("show")) { + var n = t.axis, o = n.polar, a = o.getRadiusAxis().getExtent(), r = n.getTicksCoords(), + s = f(n.getViewLabels(), function (t) { + return (t = i(t)).coord = n.dataToCoord(t.tickValue), t + }); + rv(s), rv(r), d(Yk, function (e) { + !t.get(e + ".show") || n.scale.isBlank() && "axisLine" !== e || this["_" + e](t, o, r, a, s) + }, this) + } + }, _axisLine: function (t, e, i, n) { + var o = t.getModel("axisLine.lineStyle"), + a = new pS({shape: {cx: e.cx, cy: e.cy, r: n[av(e)]}, style: o.getLineStyle(), z2: 1, silent: !0}); + a.style.fill = null, this.group.add(a) + }, _axisTick: function (t, e, i, n) { + var o = t.getModel("axisTick"), a = (o.get("inside") ? -1 : 1) * o.get("length"), s = n[av(e)], + l = f(i, function (t) { + return new MS({shape: ov(e, [s, s + a], t.coord)}) + }); + this.group.add(zS(l, {style: r(o.getModel("lineStyle").getLineStyle(), {stroke: t.get("axisLine.lineStyle.color")})})) + }, _axisLabel: function (t, e, i, n, o) { + var a = t.getCategories(!0), r = t.getModel("axisLabel"), s = r.get("margin"); + d(o, function (i, o) { + var l = r, u = i.tickValue, h = n[av(e)], c = e.coordToPoint([h + s, i.coord]), d = e.cx, f = e.cy, + p = Math.abs(c[0] - d) / h < .3 ? "center" : c[0] > d ? "left" : "right", + g = Math.abs(c[1] - f) / h < .3 ? "middle" : c[1] > f ? "top" : "bottom"; + a && a[u] && a[u].textStyle && (l = new wo(a[u].textStyle, r, r.ecModel)); + var m = new fS({silent: !0}); + this.group.add(m), no(m.style, l, { + x: c[0], + y: c[1], + textFill: l.getTextColor() || t.get("axisLine.lineStyle.color"), + text: i.formattedLabel, + textAlign: p, + textVerticalAlign: g + }) + }, this) + }, _splitLine: function (t, e, i, n) { + var o = t.getModel("splitLine").getModel("lineStyle"), a = o.get("color"), s = 0; + a = a instanceof Array ? a : [a]; + for (var l = [], u = 0; u < i.length; u++) { + var h = s++ % a.length; + l[h] = l[h] || [], l[h].push(new MS({shape: ov(e, n, i[u].coord)})) + } + for (u = 0; u < l.length; u++) this.group.add(zS(l[u], { + style: r({stroke: a[u % a.length]}, o.getLineStyle()), + silent: !0, + z: t.get("z") + })) + }, _splitArea: function (t, e, i, n) { + if (i.length) { + var o = t.getModel("splitArea").getModel("areaStyle"), a = o.get("color"), s = 0; + a = a instanceof Array ? a : [a]; + for (var l = [], u = Math.PI / 180, h = -i[0].coord * u, c = Math.min(n[0], n[1]), d = Math.max(n[0], n[1]), f = t.get("clockwise"), p = 1; p < i.length; p++) { + var g = s++ % a.length; + l[g] = l[g] || [], l[g].push(new vS({ + shape: { + cx: e.cx, + cy: e.cy, + r0: c, + r: d, + startAngle: h, + endAngle: -i[p].coord * u, + clockwise: f + }, silent: !0 + })), h = -i[p].coord * u + } + for (p = 0; p < l.length; p++) this.group.add(zS(l[p], { + style: r({fill: a[p % a.length]}, o.getAreaStyle()), + silent: !0 + })) + } + } + }); + var qk = ["axisLine", "axisTickLabel", "axisName"], Kk = ["splitLine", "splitArea"]; + ED.extend({ + type: "radiusAxis", axisPointerClass: "PolarAxisPointer", render: function (t, e) { + if (this.group.removeAll(), t.get("show")) { + var i = t.axis, n = i.polar, o = n.getAngleAxis(), a = i.getTicksCoords(), r = o.getExtent()[0], + s = i.getExtent(), l = sv(n, t, r), u = new LD(t, l); + d(qk, u.add, u), this.group.add(u.getGroup()), d(Kk, function (e) { + t.get(e + ".show") && !i.scale.isBlank() && this["_" + e](t, n, r, s, a) + }, this) + } + }, _splitLine: function (t, e, i, n, o) { + var a = t.getModel("splitLine").getModel("lineStyle"), s = a.get("color"), l = 0; + s = s instanceof Array ? s : [s]; + for (var u = [], h = 0; h < o.length; h++) { + var c = l++ % s.length; + u[c] = u[c] || [], u[c].push(new pS({shape: {cx: e.cx, cy: e.cy, r: o[h].coord}, silent: !0})) + } + for (h = 0; h < u.length; h++) this.group.add(zS(u[h], { + style: r({ + stroke: s[h % s.length], + fill: null + }, a.getLineStyle()), silent: !0 + })) + }, _splitArea: function (t, e, i, n, o) { + if (o.length) { + var a = t.getModel("splitArea").getModel("areaStyle"), s = a.get("color"), l = 0; + s = s instanceof Array ? s : [s]; + for (var u = [], h = o[0].coord, c = 1; c < o.length; c++) { + var d = l++ % s.length; + u[d] = u[d] || [], u[d].push(new vS({ + shape: { + cx: e.cx, + cy: e.cy, + r0: h, + r: o[c].coord, + startAngle: 0, + endAngle: 2 * Math.PI + }, silent: !0 + })), h = o[c].coord + } + for (c = 0; c < u.length; c++) this.group.add(zS(u[c], { + style: r({fill: s[c % s.length]}, a.getAreaStyle()), + silent: !0 + })) + } + } + }); + var $k = Og.extend({ + makeElOption: function (t, e, i, n, o) { + var a = i.axis; + "angle" === a.dim && (this.animationThreshold = Math.PI / 18); + var r, s = a.polar, l = s.getOtherAxis(a).getExtent(); + r = a["dataTo" + Ko(a.dim)](e); + var u = n.get("type"); + if (u && "none" !== u) { + var h = Gg(n), c = Jk[u](a, s, r, l, h); + c.style = h, t.graphicKey = c.type, t.pointer = c + } + Wg(t, i, n, o, lv(e, i, 0, s, n.get("label.margin"))) + } + }), Jk = { + line: function (t, e, i, n, o) { + return "angle" === t.dim ? { + type: "Line", + shape: Xg(e.coordToPoint([n[0], i]), e.coordToPoint([n[1], i])) + } : {type: "Circle", shape: {cx: e.cx, cy: e.cy, r: i}} + }, shadow: function (t, e, i, n, o) { + var a = Math.max(1, t.getBandWidth()), r = Math.PI / 180; + return "angle" === t.dim ? { + type: "Sector", + shape: Yg(e.cx, e.cy, n[0], n[1], (-i - a / 2) * r, (a / 2 - i) * r) + } : {type: "Sector", shape: Yg(e.cx, e.cy, i - a / 2, i + a / 2, 0, 2 * Math.PI)} + } + }; + ED.registerAxisPointerClass("PolarAxisPointer", $k), fs(v(function (t, e, i) { + var n = i.getWidth(), o = i.getHeight(), a = {}, r = $m(g(e.getSeriesByType(t), function (t) { + return !e.isSeriesFiltered(t) && t.coordinateSystem && "polar" === t.coordinateSystem.type + })); + e.eachSeriesByType(t, function (t) { + if ("polar" === t.coordinateSystem.type) { + var e = t.getData(), i = t.coordinateSystem, s = i.getBaseAxis(), l = qm(t), u = r[Km(s)][l], + h = u.offset, c = u.width, d = i.getOtherAxis(s), f = t.get("center") || ["50%", "50%"], + p = Do(f[0], n), g = Do(f[1], o), m = t.get("barMinHeight") || 0, v = t.get("barMinAngle") || 0; + a[l] = a[l] || []; + for (var y = e.mapDimension(d.dim), x = e.mapDimension(s.dim), _ = Zs(e, y), w = d.getExtent()[0], b = 0, S = e.count(); b < S; b++) { + var M = e.get(y, b), I = e.get(x, b); + if (!isNaN(M)) { + var T = M >= 0 ? "p" : "n", D = w; + _ && (a[l][I] || (a[l][I] = {p: w, n: w}), D = a[l][I][T]); + var A, C, L, k; + if ("radius" === d.dim) { + var P = d.dataToRadius(M) - w, N = s.dataToAngle(I); + Math.abs(P) < m && (P = (P < 0 ? -1 : 1) * m), A = D, C = D + P, k = (L = N - h) - c, _ && (a[l][I][T] = C) + } else { + var O = d.dataToAngle(M, !0) - w, E = s.dataToRadius(I); + Math.abs(O) < v && (O = (O < 0 ? -1 : 1) * v), C = (A = E + h) + c, L = D, k = D + O, _ && (a[l][I][T] = k) + } + e.setItemLayout(b, { + cx: p, + cy: g, + r0: A, + r: C, + startAngle: -L * Math.PI / 180, + endAngle: -k * Math.PI / 180 + }) + } + } + } + }, this) + }, "bar")), ys({type: "polar"}), h(hM.extend({ + type: "geo", + coordinateSystem: null, + layoutMode: "box", + init: function (t) { + hM.prototype.init.apply(this, arguments), Mi(t, "label", ["show"]) + }, + optionUpdated: function () { + var t = this.option, e = this; + t.regions = vA.getFilledRegions(t.regions, t.map, t.nameMap), this._optionModelMap = p(t.regions || [], function (t, i) { + return i.name && t.set(i.name, new wo(i, e)), t + }, R()), this.updateSelectedMap(t.regions) + }, + defaultOption: { + zlevel: 0, + z: 0, + show: !0, + left: "center", + top: "center", + aspectScale: .75, + silent: !1, + map: "", + boundingCoords: null, + center: null, + zoom: 1, + scaleLimit: null, + label: {show: !1, color: "#000"}, + itemStyle: {borderWidth: .5, borderColor: "#444", color: "#eee"}, + emphasis: {label: {show: !0, color: "rgb(100,0,0)"}, itemStyle: {color: "rgba(255,215,0,0.8)"}}, + regions: [] + }, + getRegionModel: function (t) { + return this._optionModelMap.get(t) || new wo(null, this, this.ecModel) + }, + getFormattedLabel: function (t, e) { + var i = this.getRegionModel(t).get("label." + e + ".formatter"), n = {name: t}; + return "function" == typeof i ? (n.status = e, i(n)) : "string" == typeof i ? i.replace("{a}", null != t ? t : "") : void 0 + }, + setZoom: function (t) { + this.option.zoom = t + }, + setCenter: function (t) { + this.option.center = t + } + }), YD), ys({ + type: "geo", init: function (t, e) { + var i = new Uh(e, !0); + this._mapDraw = i, this.group.add(i.group) + }, render: function (t, e, i, n) { + if (!n || "geoToggleSelect" !== n.type || n.from !== this.uid) { + var o = this._mapDraw; + t.get("show") ? o.draw(t, e, i, this, n) : this._mapDraw.group.removeAll(), this.group.silent = t.get("silent") + } + }, dispose: function () { + this._mapDraw && this._mapDraw.remove() + } + }), uv("toggleSelected", {type: "geoToggleSelect", event: "geoselectchanged"}), uv("select", { + type: "geoSelect", + event: "geoselected" + }), uv("unSelect", {type: "geoUnSelect", event: "geounselected"}); + var Qk = ["rect", "polygon", "keep", "clear"], tP = d, eP = { + lineX: mv(0), lineY: mv(1), rect: { + point: function (t, e, i) { + return t && i.boundingRect.contain(t[0], t[1]) + }, rect: function (t, e, i) { + return t && i.boundingRect.intersect(t) + } + }, polygon: { + point: function (t, e, i) { + return t && i.boundingRect.contain(t[0], t[1]) && Al(i.range, t[0], t[1]) + }, rect: function (t, e, i) { + var n = i.range; + if (!t || n.length <= 1) return !1; + var o = t.x, a = t.y, r = t.width, s = t.height, l = n[0]; + return !!(Al(n, o, a) || Al(n, o + r, a) || Al(n, o, a + s) || Al(n, o + r, a + s) || $t.create(t).contain(l[0], l[1]) || yv(o, a, o + r, a, n) || yv(o, a, o, a + s, n) || yv(o + r, a, o + r, a + s, n) || yv(o, a + s, o + r, a + s, n)) || void 0 + } + } + }, iP = d, nP = l, oP = v, aP = ["dataToPoint", "pointToData"], + rP = ["grid", "xAxis", "yAxis", "geo", "graph", "polar", "radiusAxis", "angleAxis", "bmap"], sP = bv.prototype; + sP.setOutputRanges = function (t, e) { + this.matchOutputRanges(t, e, function (t, e, i) { + if ((t.coordRanges || (t.coordRanges = [])).push(e), !t.coordRange) { + t.coordRange = e; + var n = cP[t.brushType](0, i, e); + t.__rangeOffset = {offset: dP[t.brushType](n.values, t.range, [1, 1]), xyMinMax: n.xyMinMax} + } + }) + }, sP.matchOutputRanges = function (t, e, i) { + iP(t, function (t) { + var n = this.findTargetInfo(t, e); + n && !0 !== n && d(n.coordSyses, function (n) { + var o = cP[t.brushType](1, n, t.range); + i(t, o.values, n, e) + }) + }, this) + }, sP.setInputRanges = function (t, e) { + iP(t, function (t) { + var i = this.findTargetInfo(t, e); + if (t.range = t.range || [], i && !0 !== i) { + t.panelId = i.panelId; + var n = cP[t.brushType](0, i.coordSys, t.coordRange), o = t.__rangeOffset; + t.range = o ? dP[t.brushType](n.values, o.offset, Dv(n.xyMinMax, o.xyMinMax)) : n.values + } + }, this) + }, sP.makePanelOpts = function (t, e) { + return f(this._targetInfoList, function (i) { + var n = i.getPanelRect(); + return { + panelId: i.panelId, + defaultBrushType: e && e(i), + clipPath: Ef(n), + isTargetByCursor: zf(n, t, i.coordSysModel), + getLinearBrushOtherExtent: Rf(n) + } + }) + }, sP.controlSeries = function (t, e, i) { + var n = this.findTargetInfo(t, i); + return !0 === n || n && nP(n.coordSyses, e.coordinateSystem) >= 0 + }, sP.findTargetInfo = function (t, e) { + for (var i = this._targetInfoList, n = Mv(e, t), o = 0; o < i.length; o++) { + var a = i[o], r = t.panelId; + if (r) { + if (a.panelId === r) return a + } else for (o = 0; o < uP.length; o++) if (uP[o](n, a)) return a + } + return !0 + }; + var lP = { + grid: function (t, e) { + var i = t.xAxisModels, n = t.yAxisModels, o = t.gridModels, a = R(), r = {}, s = {}; + (i || n || o) && (iP(i, function (t) { + var e = t.axis.grid.model; + a.set(e.id, e), r[e.id] = !0 + }), iP(n, function (t) { + var e = t.axis.grid.model; + a.set(e.id, e), s[e.id] = !0 + }), iP(o, function (t) { + a.set(t.id, t), r[t.id] = !0, s[t.id] = !0 + }), a.each(function (t) { + var o = t.coordinateSystem, a = []; + iP(o.getCartesians(), function (t, e) { + (nP(i, t.getAxis("x").model) >= 0 || nP(n, t.getAxis("y").model) >= 0) && a.push(t) + }), e.push({ + panelId: "grid--" + t.id, + gridModel: t, + coordSysModel: t, + coordSys: a[0], + coordSyses: a, + getPanelRect: hP.grid, + xAxisDeclared: r[t.id], + yAxisDeclared: s[t.id] + }) + })) + }, geo: function (t, e) { + iP(t.geoModels, function (t) { + var i = t.coordinateSystem; + e.push({ + panelId: "geo--" + t.id, + geoModel: t, + coordSysModel: t, + coordSys: i, + coordSyses: [i], + getPanelRect: hP.geo + }) + }) + } + }, uP = [function (t, e) { + var i = t.xAxisModel, n = t.yAxisModel, o = t.gridModel; + return !o && i && (o = i.axis.grid.model), !o && n && (o = n.axis.grid.model), o && o === e.gridModel + }, function (t, e) { + var i = t.geoModel; + return i && i === e.geoModel + }], hP = { + grid: function () { + return this.coordSys.grid.getRect().clone() + }, geo: function () { + var t = this.coordSys, e = t.getBoundingRect().clone(); + return e.applyTransform(go(t)), e + } + }, cP = { + lineX: oP(Iv, 0), lineY: oP(Iv, 1), rect: function (t, e, i) { + var n = e[aP[t]]([i[0][0], i[1][0]]), o = e[aP[t]]([i[0][1], i[1][1]]), + a = [Sv([n[0], o[0]]), Sv([n[1], o[1]])]; + return {values: a, xyMinMax: a} + }, polygon: function (t, e, i) { + var n = [[1 / 0, -1 / 0], [1 / 0, -1 / 0]]; + return { + values: f(i, function (i) { + var o = e[aP[t]](i); + return n[0][0] = Math.min(n[0][0], o[0]), n[1][0] = Math.min(n[1][0], o[1]), n[0][1] = Math.max(n[0][1], o[0]), n[1][1] = Math.max(n[1][1], o[1]), o + }), xyMinMax: n + } + } + }, dP = { + lineX: oP(Tv, 0), lineY: oP(Tv, 1), rect: function (t, e, i) { + return [[t[0][0] - i[0] * e[0][0], t[0][1] - i[0] * e[0][1]], [t[1][0] - i[1] * e[1][0], t[1][1] - i[1] * e[1][1]]] + }, polygon: function (t, e, i) { + return f(t, function (t, n) { + return [t[0] - i[0] * e[n][0], t[1] - i[1] * e[n][1]] + }) + } + }, fP = ["inBrush", "outOfBrush"], pP = "__ecBrushSelect", gP = "__ecInBrushSelectEvent", mP = LI.VISUAL.BRUSH; + fs(mP, function (t, e, i) { + t.eachComponent({mainType: "brush"}, function (e) { + i && "takeGlobalCursor" === i.type && e.setBrushOption("brush" === i.key ? i.brushOption : {brushType: !1}), (e.brushTargetManager = new bv(e.option, t)).setInputRanges(e.areas, t) + }) + }), ps(mP, function (t, e, n) { + var o, a, s = []; + t.eachComponent({mainType: "brush"}, function (e, n) { + function l(t) { + return "all" === m || v[t] + } + + function u(t) { + return !!t.length + } + + function h(t, e) { + var i = t.coordinateSystem; + w |= i.hasAxisBrushed(), l(e) && i.eachActiveState(t.getData(), function (t, e) { + "active" === t && (x[e] = 1) + }) + } + + function c(i, n, o) { + var a = Pv(i); + if (a && !Nv(e, n) && (d(b, function (n) { + a[n.brushType] && e.brushTargetManager.controlSeries(n, i, t) && o.push(n), w |= u(o) + }), l(n) && u(o))) { + var r = i.getData(); + r.each(function (t) { + kv(a, o, r, t) && (x[t] = 1) + }) + } + } + + var p = {brushId: e.id, brushIndex: n, brushName: e.name, areas: i(e.areas), selected: []}; + s.push(p); + var g = e.option, m = g.brushLink, v = [], x = [], _ = [], w = 0; + n || (o = g.throttleType, a = g.throttleDelay); + var b = f(e.areas, function (t) { + return Ov(r({boundingRect: vP[t.brushType](t)}, t)) + }), S = dv(e.option, fP, function (t) { + t.mappingMethod = "fixed" + }); + y(m) && d(m, function (t) { + v[t] = 1 + }), t.eachSeries(function (t, e) { + var i = _[e] = []; + "parallel" === t.subType ? h(t, e) : c(t, e, i) + }), t.eachSeries(function (t, e) { + var i = {seriesId: t.id, seriesIndex: e, seriesName: t.name, dataIndex: []}; + p.selected.push(i); + var n = Pv(t), o = _[e], a = t.getData(), r = l(e) ? function (t) { + return x[t] ? (i.dataIndex.push(a.getRawIndex(t)), "inBrush") : "outOfBrush" + } : function (t) { + return kv(n, o, a, t) ? (i.dataIndex.push(a.getRawIndex(t)), "inBrush") : "outOfBrush" + }; + (l(e) ? w : u(o)) && pv(fP, S, a, r) + }) + }), Cv(e, o, a, s, n) + }); + var vP = { + lineX: B, lineY: B, rect: function (t) { + return Ev(t.range) + }, polygon: function (t) { + for (var e, i = t.range, n = 0, o = i.length; n < o; n++) { + e = e || [[1 / 0, -1 / 0], [1 / 0, -1 / 0]]; + var a = i[n]; + a[0] < e[0][0] && (e[0][0] = a[0]), a[0] > e[0][1] && (e[0][1] = a[0]), a[1] < e[1][0] && (e[1][0] = a[1]), a[1] > e[1][1] && (e[1][1] = a[1]) + } + return e && Ev(e) + } + }, yP = ["#ddd"]; + vs({ + type: "brush", + dependencies: ["geo", "grid", "xAxis", "yAxis", "parallel", "series"], + defaultOption: { + toolbox: null, + brushLink: null, + seriesIndex: "all", + geoIndex: null, + xAxisIndex: null, + yAxisIndex: null, + brushType: "rect", + brushMode: "single", + transformable: !0, + brushStyle: {borderWidth: 1, color: "rgba(120,140,180,0.3)", borderColor: "rgba(120,140,180,0.8)"}, + throttleType: "fixRate", + throttleDelay: 0, + removeOnClick: !0, + z: 1e4 + }, + areas: [], + brushType: null, + brushOption: {}, + coordInfoList: [], + optionUpdated: function (t, e) { + var i = this.option; + !e && fv(i, t, ["inBrush", "outOfBrush"]); + var n = i.inBrush = i.inBrush || {}; + i.outOfBrush = i.outOfBrush || {color: yP}, n.hasOwnProperty("liftZ") || (n.liftZ = 5) + }, + setAreas: function (t) { + t && (this.areas = f(t, function (t) { + return Rv(this.option, t) + }, this)) + }, + setBrushOption: function (t) { + this.brushOption = Rv(this.option, t), this.brushType = this.brushOption.brushType + } + }); + ys({ + type: "brush", init: function (t, e) { + this.ecModel = t, this.api = e, this.model, (this._brushController = new Qd(e.getZr())).on("brush", m(this._onBrush, this)).mount() + }, render: function (t) { + return this.model = t, zv.apply(this, arguments) + }, updateTransform: zv, updateView: zv, dispose: function () { + this._brushController.dispose() + }, _onBrush: function (t, e) { + var n = this.model.id; + this.model.brushTargetManager.setOutputRanges(t, this.ecModel), (!e.isEnd || e.removeOnClick) && this.api.dispatchAction({ + type: "brush", + brushId: n, + areas: i(t), + $from: n + }) + } + }), cs({type: "brush", event: "brush"}, function (t, e) { + e.eachComponent({mainType: "brush", query: t}, function (e) { + e.setAreas(t.areas) + }) + }), cs({type: "brushSelect", event: "brushSelected", update: "none"}, function () { + }); + var xP = {}, _P = lI.toolbox.brush; + Gv.defaultOption = { + show: !0, type: ["rect", "polygon", "lineX", "lineY", "keep", "clear"], icon: { + rect: "M7.3,34.7 M0.4,10V-0.2h9.8 M89.6,10V-0.2h-9.8 M0.4,60v10.2h9.8 M89.6,60v10.2h-9.8 M12.3,22.4V10.5h13.1 M33.6,10.5h7.8 M49.1,10.5h7.8 M77.5,22.4V10.5h-13 M12.3,31.1v8.2 M77.7,31.1v8.2 M12.3,47.6v11.9h13.1 M33.6,59.5h7.6 M49.1,59.5 h7.7 M77.5,47.6v11.9h-13", + polygon: "M55.2,34.9c1.7,0,3.1,1.4,3.1,3.1s-1.4,3.1-3.1,3.1 s-3.1-1.4-3.1-3.1S53.5,34.9,55.2,34.9z M50.4,51c1.7,0,3.1,1.4,3.1,3.1c0,1.7-1.4,3.1-3.1,3.1c-1.7,0-3.1-1.4-3.1-3.1 C47.3,52.4,48.7,51,50.4,51z M55.6,37.1l1.5-7.8 M60.1,13.5l1.6-8.7l-7.8,4 M59,19l-1,5.3 M24,16.1l6.4,4.9l6.4-3.3 M48.5,11.6 l-5.9,3.1 M19.1,12.8L9.7,5.1l1.1,7.7 M13.4,29.8l1,7.3l6.6,1.6 M11.6,18.4l1,6.1 M32.8,41.9 M26.6,40.4 M27.3,40.2l6.1,1.6 M49.9,52.1l-5.6-7.6l-4.9-1.2", + lineX: "M15.2,30 M19.7,15.6V1.9H29 M34.8,1.9H40.4 M55.3,15.6V1.9H45.9 M19.7,44.4V58.1H29 M34.8,58.1H40.4 M55.3,44.4 V58.1H45.9 M12.5,20.3l-9.4,9.6l9.6,9.8 M3.1,29.9h16.5 M62.5,20.3l9.4,9.6L62.3,39.7 M71.9,29.9H55.4", + lineY: "M38.8,7.7 M52.7,12h13.2v9 M65.9,26.6V32 M52.7,46.3h13.2v-9 M24.9,12H11.8v9 M11.8,26.6V32 M24.9,46.3H11.8v-9 M48.2,5.1l-9.3-9l-9.4,9.2 M38.9-3.9V12 M48.2,53.3l-9.3,9l-9.4-9.2 M38.9,62.3V46.4", + keep: "M4,10.5V1h10.3 M20.7,1h6.1 M33,1h6.1 M55.4,10.5V1H45.2 M4,17.3v6.6 M55.6,17.3v6.6 M4,30.5V40h10.3 M20.7,40 h6.1 M33,40h6.1 M55.4,30.5V40H45.2 M21,18.9h62.9v48.6H21V18.9z", + clear: "M22,14.7l30.9,31 M52.9,14.7L22,45.7 M4.7,16.8V4.2h13.1 M26,4.2h7.8 M41.6,4.2h7.8 M70.3,16.8V4.2H57.2 M4.7,25.9v8.6 M70.3,25.9v8.6 M4.7,43.2v12.6h13.1 M26,55.8h7.8 M41.6,55.8h7.8 M70.3,43.2v12.6H57.2" + }, title: i(_P.title) + }; + var wP = Gv.prototype; + wP.render = wP.updateView = function (t, e, i) { + var n, o, a; + e.eachComponent({mainType: "brush"}, function (t) { + n = t.brushType, o = t.brushOption.brushMode || "single", a |= t.areas.length + }), this._brushType = n, this._brushMode = o, d(t.get("type", !0), function (e) { + t.setIconStatus(e, ("keep" === e ? "multiple" === o : "clear" === e ? a : e === n) ? "emphasis" : "normal") + }) + }, wP.getIcons = function () { + var t = this.model, e = t.get("icon", !0), i = {}; + return d(t.get("type", !0), function (t) { + e[t] && (i[t] = e[t]) + }), i + }, wP.onclick = function (t, e, i) { + var n = this._brushType, o = this._brushMode; + "clear" === i ? (e.dispatchAction({type: "axisAreaSelect", intervals: []}), e.dispatchAction({ + type: "brush", + command: "clear", + areas: [] + })) : e.dispatchAction({ + type: "takeGlobalCursor", + key: "brush", + brushOption: { + brushType: "keep" === i ? n : n !== i && i, + brushMode: "keep" === i ? "multiple" === o ? "single" : "multiple" : o + } + }) + }, Bv("brush", Gv), us(function (t, e) { + var i = t && t.brush; + if (y(i) || (i = i ? [i] : []), i.length) { + var n = []; + d(i, function (t) { + var e = t.hasOwnProperty("toolbox") ? t.toolbox : []; + e instanceof Array && (n = n.concat(e)) + }); + var o = t && t.toolbox; + y(o) && (o = o[0]), o || (o = {feature: {}}, t.toolbox = [o]); + var a = o.feature || (o.feature = {}), r = a.brush || (a.brush = {}), s = r.type || (r.type = []); + s.push.apply(s, n), hv(s), e && !s.length && s.push.apply(s, Qk) + } + }); + Wv.prototype = { + constructor: Wv, type: "calendar", dimensions: ["time", "value"], getDimensionsInfo: function () { + return [{name: "time", type: "time"}, "value"] + }, getRangeInfo: function () { + return this._rangeInfo + }, getModel: function () { + return this._model + }, getRect: function () { + return this._rect + }, getCellWidth: function () { + return this._sw + }, getCellHeight: function () { + return this._sh + }, getOrient: function () { + return this._orient + }, getFirstDayOfWeek: function () { + return this._firstDayOfWeek + }, getDateInfo: function (t) { + var e = (t = Ro(t)).getFullYear(), i = t.getMonth() + 1; + i = i < 10 ? "0" + i : i; + var n = t.getDate(); + n = n < 10 ? "0" + n : n; + var o = t.getDay(); + return o = Math.abs((o + 7 - this.getFirstDayOfWeek()) % 7), { + y: e, + m: i, + d: n, + day: o, + time: t.getTime(), + formatedDate: e + "-" + i + "-" + n, + date: t + } + }, getNextNDay: function (t, e) { + return 0 === (e = e || 0) ? this.getDateInfo(t) : ((t = new Date(this.getDateInfo(t).time)).setDate(t.getDate() + e), this.getDateInfo(t)) + }, update: function (t, e) { + function i(t, e) { + return null != t[e] && "auto" !== t[e] + } + + this._firstDayOfWeek = +this._model.getModel("dayLabel").get("firstDay"), this._orient = this._model.get("orient"), this._lineWidth = this._model.getModel("itemStyle").getItemStyle().lineWidth || 0, this._rangeInfo = this._getRangeInfo(this._initRangeOption()); + var n = this._rangeInfo.weeks || 1, o = ["width", "height"], a = this._model.get("cellSize").slice(), + r = this._model.getBoxLayoutParams(), s = "horizontal" === this._orient ? [n, 7] : [7, n]; + d([0, 1], function (t) { + i(a, t) && (r[o[t]] = a[t] * s[t]) + }); + var l = {width: e.getWidth(), height: e.getHeight()}, u = this._rect = Qo(r, l); + d([0, 1], function (t) { + i(a, t) || (a[t] = u[o[t]] / s[t]) + }), this._sw = a[0], this._sh = a[1] + }, dataToPoint: function (t, e) { + y(t) && (t = t[0]), null == e && (e = !0); + var i = this.getDateInfo(t), n = this._rangeInfo, o = i.formatedDate; + if (e && !(i.time >= n.start.time && i.time < n.end.time + 864e5)) return [NaN, NaN]; + var a = i.day, r = this._getRangeInfo([n.start.time, o]).nthWeek; + return "vertical" === this._orient ? [this._rect.x + a * this._sw + this._sw / 2, this._rect.y + r * this._sh + this._sh / 2] : [this._rect.x + r * this._sw + this._sw / 2, this._rect.y + a * this._sh + this._sh / 2] + }, pointToData: function (t) { + var e = this.pointToDate(t); + return e && e.time + }, dataToRect: function (t, e) { + var i = this.dataToPoint(t, e); + return { + contentShape: { + x: i[0] - (this._sw - this._lineWidth) / 2, + y: i[1] - (this._sh - this._lineWidth) / 2, + width: this._sw - this._lineWidth, + height: this._sh - this._lineWidth + }, + center: i, + tl: [i[0] - this._sw / 2, i[1] - this._sh / 2], + tr: [i[0] + this._sw / 2, i[1] - this._sh / 2], + br: [i[0] + this._sw / 2, i[1] + this._sh / 2], + bl: [i[0] - this._sw / 2, i[1] + this._sh / 2] + } + }, pointToDate: function (t) { + var e = Math.floor((t[0] - this._rect.x) / this._sw) + 1, + i = Math.floor((t[1] - this._rect.y) / this._sh) + 1, n = this._rangeInfo.range; + return "vertical" === this._orient ? this._getDateByWeeksAndDay(i, e - 1, n) : this._getDateByWeeksAndDay(e, i - 1, n) + }, convertToPixel: v(Fv, "dataToPoint"), convertFromPixel: v(Fv, "pointToData"), _initRangeOption: function () { + var t = this._model.get("range"), e = t; + if (y(e) && 1 === e.length && (e = e[0]), /^\d{4}$/.test(e) && (t = [e + "-01-01", e + "-12-31"]), /^\d{4}[\/|-]\d{1,2}$/.test(e)) { + var i = this.getDateInfo(e), n = i.date; + n.setMonth(n.getMonth() + 1); + var o = this.getNextNDay(n, -1); + t = [i.formatedDate, o.formatedDate] + } + /^\d{4}[\/|-]\d{1,2}[\/|-]\d{1,2}$/.test(e) && (t = [e, e]); + var a = this._getRangeInfo(t); + return a.start.time > a.end.time && t.reverse(), t + }, _getRangeInfo: function (t) { + var e; + (t = [this.getDateInfo(t[0]), this.getDateInfo(t[1])])[0].time > t[1].time && (e = !0, t.reverse()); + var i = Math.floor(t[1].time / 864e5) - Math.floor(t[0].time / 864e5) + 1, n = new Date(t[0].time), + o = n.getDate(), a = t[1].date.getDate(); + if (n.setDate(o + i - 1), n.getDate() !== a) for (var r = n.getTime() - t[1].time > 0 ? 1 : -1; n.getDate() !== a && (n.getTime() - t[1].time) * r > 0;) i -= r, n.setDate(o + i - 1); + var s = Math.floor((i + t[0].day + 6) / 7), l = e ? 1 - s : s - 1; + return e && t.reverse(), { + range: [t[0].formatedDate, t[1].formatedDate], + start: t[0], + end: t[1], + allDay: i, + weeks: s, + nthWeek: l, + fweek: t[0].day, + lweek: t[1].day + } + }, _getDateByWeeksAndDay: function (t, e, i) { + var n = this._getRangeInfo(i); + if (t > n.weeks || 0 === t && e < n.fweek || t === n.weeks && e > n.lweek) return !1; + var o = 7 * (t - 1) - n.fweek + e, a = new Date(n.start.time); + return a.setDate(n.start.d + o), this.getDateInfo(a) + } + }, Wv.dimensions = Wv.prototype.dimensions, Wv.getDimensionsInfo = Wv.prototype.getDimensionsInfo, Wv.create = function (t, e) { + var i = []; + return t.eachComponent("calendar", function (n) { + var o = new Wv(n, t, e); + i.push(o), n.coordinateSystem = o + }), t.eachSeries(function (t) { + "calendar" === t.get("coordinateSystem") && (t.coordinateSystem = i[t.get("calendarIndex") || 0]) + }), i + }, Ca.register("calendar", Wv); + var bP = hM.extend({ + type: "calendar", + coordinateSystem: null, + defaultOption: { + zlevel: 0, + z: 2, + left: 80, + top: 60, + cellSize: 20, + orient: "horizontal", + splitLine: {show: !0, lineStyle: {color: "#000", width: 1, type: "solid"}}, + itemStyle: {color: "#fff", borderWidth: 1, borderColor: "#ccc"}, + dayLabel: {show: !0, firstDay: 0, position: "start", margin: "50%", nameMap: "en", color: "#000"}, + monthLabel: { + show: !0, + position: "start", + margin: 5, + align: "center", + nameMap: "en", + formatter: null, + color: "#000" + }, + yearLabel: { + show: !0, + position: null, + margin: 30, + formatter: null, + color: "#ccc", + fontFamily: "sans-serif", + fontWeight: "bolder", + fontSize: 20 + } + }, + init: function (t, e, i, n) { + var o = na(t); + bP.superApply(this, "init", arguments), Hv(t, o) + }, + mergeOption: function (t, e) { + bP.superApply(this, "mergeOption", arguments), Hv(this.option, t) + } + }), SP = { + EN: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], + CN: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"] + }, MP = {EN: ["S", "M", "T", "W", "T", "F", "S"], CN: ["日", "一", "二", "三", "四", "五", "六"]}; + ys({ + type: "calendar", + _tlpoints: null, + _blpoints: null, + _firstDayOfMonth: null, + _firstDayPoints: null, + render: function (t, e, i) { + var n = this.group; + n.removeAll(); + var o = t.coordinateSystem, a = o.getRangeInfo(), r = o.getOrient(); + this._renderDayRect(t, a, n), this._renderLines(t, a, r, n), this._renderYearText(t, a, r, n), this._renderMonthText(t, r, n), this._renderWeekText(t, a, r, n) + }, + _renderDayRect: function (t, e, i) { + for (var n = t.coordinateSystem, o = t.getModel("itemStyle").getItemStyle(), a = n.getCellWidth(), r = n.getCellHeight(), s = e.start.time; s <= e.end.time; s = n.getNextNDay(s, 1).time) { + var l = n.dataToRect([s], !1).tl, + u = new SS({shape: {x: l[0], y: l[1], width: a, height: r}, cursor: "default", style: o}); + i.add(u) + } + }, + _renderLines: function (t, e, i, n) { + function o(e) { + a._firstDayOfMonth.push(r.getDateInfo(e)), a._firstDayPoints.push(r.dataToRect([e], !1).tl); + var o = a._getLinePointsOfOneWeek(t, e, i); + a._tlpoints.push(o[0]), a._blpoints.push(o[o.length - 1]), l && a._drawSplitline(o, s, n) + } + + var a = this, r = t.coordinateSystem, s = t.getModel("splitLine.lineStyle").getLineStyle(), + l = t.get("splitLine.show"), u = s.lineWidth; + this._tlpoints = [], this._blpoints = [], this._firstDayOfMonth = [], this._firstDayPoints = []; + for (var h = e.start, c = 0; h.time <= e.end.time; c++) { + o(h.formatedDate), 0 === c && (h = r.getDateInfo(e.start.y + "-" + e.start.m)); + var d = h.date; + d.setMonth(d.getMonth() + 1), h = r.getDateInfo(d) + } + o(r.getNextNDay(e.end.time, 1).formatedDate), l && this._drawSplitline(a._getEdgesPoints(a._tlpoints, u, i), s, n), l && this._drawSplitline(a._getEdgesPoints(a._blpoints, u, i), s, n) + }, + _getEdgesPoints: function (t, e, i) { + var n = [t[0].slice(), t[t.length - 1].slice()], o = "horizontal" === i ? 0 : 1; + return n[0][o] = n[0][o] - e / 2, n[1][o] = n[1][o] + e / 2, n + }, + _drawSplitline: function (t, e, i) { + var n = new bS({z2: 20, shape: {points: t}, style: e}); + i.add(n) + }, + _getLinePointsOfOneWeek: function (t, e, i) { + var n = t.coordinateSystem; + e = n.getDateInfo(e); + for (var o = [], a = 0; a < 7; a++) { + var r = n.getNextNDay(e.time, a), s = n.dataToRect([r.time], !1); + o[2 * r.day] = s.tl, o[2 * r.day + 1] = s["horizontal" === i ? "bl" : "tr"] + } + return o + }, + _formatterLabel: function (t, e) { + return "string" == typeof t && t ? Xo(t, e) : "function" == typeof t ? t(e) : e.nameMap + }, + _yearTextPositionControl: function (t, e, i, n, o) { + e = e.slice(); + var a = ["center", "bottom"]; + "bottom" === n ? (e[1] += o, a = ["center", "top"]) : "left" === n ? e[0] -= o : "right" === n ? (e[0] += o, a = ["center", "top"]) : e[1] -= o; + var r = 0; + return "left" !== n && "right" !== n || (r = Math.PI / 2), { + rotation: r, + position: e, + style: {textAlign: a[0], textVerticalAlign: a[1]} + } + }, + _renderYearText: function (t, e, i, n) { + var o = t.getModel("yearLabel"); + if (o.get("show")) { + var a = o.get("margin"), r = o.get("position"); + r || (r = "horizontal" !== i ? "top" : "left"); + var s = [this._tlpoints[this._tlpoints.length - 1], this._blpoints[0]], l = (s[0][0] + s[1][0]) / 2, + u = (s[0][1] + s[1][1]) / 2, h = "horizontal" === i ? 0 : 1, + c = {top: [l, s[h][1]], bottom: [l, s[1 - h][1]], left: [s[1 - h][0], u], right: [s[h][0], u]}, + d = e.start.y; + +e.end.y > +e.start.y && (d = d + "-" + e.end.y); + var f = o.get("formatter"), p = {start: e.start.y, end: e.end.y, nameMap: d}, + g = this._formatterLabel(f, p), m = new fS({z2: 30}); + no(m.style, o, {text: g}), m.attr(this._yearTextPositionControl(m, c[r], i, r, a)), n.add(m) + } + }, + _monthTextPositionControl: function (t, e, i, n, o) { + var a = "left", r = "top", s = t[0], l = t[1]; + return "horizontal" === i ? (l += o, e && (a = "center"), "start" === n && (r = "bottom")) : (s += o, e && (r = "middle"), "start" === n && (a = "right")), { + x: s, + y: l, + textAlign: a, + textVerticalAlign: r + } + }, + _renderMonthText: function (t, e, i) { + var n = t.getModel("monthLabel"); + if (n.get("show")) { + var o = n.get("nameMap"), r = n.get("margin"), s = n.get("position"), l = n.get("align"), + u = [this._tlpoints, this._blpoints]; + _(o) && (o = SP[o.toUpperCase()] || []); + var h = "start" === s ? 0 : 1, c = "horizontal" === e ? 0 : 1; + r = "start" === s ? -r : r; + for (var d = "center" === l, f = 0; f < u[h].length - 1; f++) { + var p = u[h][f].slice(), g = this._firstDayOfMonth[f]; + if (d) { + var m = this._firstDayPoints[f]; + p[c] = (m[c] + u[0][f + 1][c]) / 2 + } + var v = n.get("formatter"), y = o[+g.m - 1], + x = {yyyy: g.y, yy: (g.y + "").slice(2), MM: g.m, M: +g.m, nameMap: y}, + w = this._formatterLabel(v, x), b = new fS({z2: 30}); + a(no(b.style, n, {text: w}), this._monthTextPositionControl(p, d, e, s, r)), i.add(b) + } + } + }, + _weekTextPositionControl: function (t, e, i, n, o) { + var a = "center", r = "middle", s = t[0], l = t[1], u = "start" === i; + return "horizontal" === e ? (s = s + n + (u ? 1 : -1) * o[0] / 2, a = u ? "right" : "left") : (l = l + n + (u ? 1 : -1) * o[1] / 2, r = u ? "bottom" : "top"), { + x: s, + y: l, + textAlign: a, + textVerticalAlign: r + } + }, + _renderWeekText: function (t, e, i, n) { + var o = t.getModel("dayLabel"); + if (o.get("show")) { + var r = t.coordinateSystem, s = o.get("position"), l = o.get("nameMap"), u = o.get("margin"), + h = r.getFirstDayOfWeek(); + _(l) && (l = MP[l.toUpperCase()] || []); + var c = r.getNextNDay(e.end.time, 7 - e.lweek).time, d = [r.getCellWidth(), r.getCellHeight()]; + u = Do(u, d["horizontal" === i ? 0 : 1]), "start" === s && (c = r.getNextNDay(e.start.time, -(7 + e.fweek)).time, u = -u); + for (var f = 0; f < 7; f++) { + var p = r.getNextNDay(c, f), g = r.dataToRect([p.time], !1).center, m = f; + m = Math.abs((f + h) % 7); + var v = new fS({z2: 30}); + a(no(v.style, o, {text: l[m]}), this._weekTextPositionControl(g, i, s, u, d)), n.add(v) + } + } + } + }), vs({ + type: "title", + layoutMode: {type: "box", ignoreSize: !0}, + defaultOption: { + zlevel: 0, + z: 6, + show: !0, + text: "", + target: "blank", + subtext: "", + subtarget: "blank", + left: 0, + top: 0, + backgroundColor: "rgba(0,0,0,0)", + borderColor: "#ccc", + borderWidth: 0, + padding: 5, + itemGap: 10, + textStyle: {fontSize: 18, fontWeight: "bolder", color: "#333"}, + subtextStyle: {color: "#aaa"} + } + }), ys({ + type: "title", render: function (t, e, i) { + if (this.group.removeAll(), t.get("show")) { + var n = this.group, o = t.getModel("textStyle"), a = t.getModel("subtextStyle"), r = t.get("textAlign"), + s = t.get("textBaseline"), l = new fS({ + style: no({}, o, {text: t.get("text"), textFill: o.getTextColor()}, {disableBox: !0}), + z2: 10 + }), u = l.getBoundingRect(), h = t.get("subtext"), c = new fS({ + style: no({}, a, { + text: h, + textFill: a.getTextColor(), + y: u.height + t.get("itemGap"), + textVerticalAlign: "top" + }, {disableBox: !0}), z2: 10 + }), d = t.get("link"), f = t.get("sublink"); + l.silent = !d, c.silent = !f, d && l.on("click", function () { + window.open(d, "_" + t.get("target")) + }), f && c.on("click", function () { + window.open(f, "_" + t.get("subtarget")) + }), n.add(l), h && n.add(c); + var p = n.getBoundingRect(), g = t.getBoxLayoutParams(); + g.width = p.width, g.height = p.height; + var m = Qo(g, {width: i.getWidth(), height: i.getHeight()}, t.get("padding")); + r || ("middle" === (r = t.get("left") || t.get("right")) && (r = "center"), "right" === r ? m.x += m.width : "center" === r && (m.x += m.width / 2)), s || ("center" === (s = t.get("top") || t.get("bottom")) && (s = "middle"), "bottom" === s ? m.y += m.height : "middle" === s && (m.y += m.height / 2), s = s || "top"), n.attr("position", [m.x, m.y]); + var v = {textAlign: r, textVerticalAlign: s}; + l.setStyle(v), c.setStyle(v), p = n.getBoundingRect(); + var y = m.margin, x = t.getItemStyle(["color", "opacity"]); + x.fill = t.get("backgroundColor"); + var _ = new SS({ + shape: { + x: p.x - y[3], + y: p.y - y[0], + width: p.width + y[1] + y[3], + height: p.height + y[0] + y[2], + r: t.get("borderRadius") + }, style: x, silent: !0 + }); + Wn(_), n.add(_) + } + } + }), hM.registerSubTypeDefaulter("dataZoom", function () { + return "slider" + }); + var IP = ["cartesian2d", "polar", "singleAxis"], TP = function (t, e) { + var i = f(t = t.slice(), Ko), n = f(e = (e || []).slice(), Ko); + return function (o, a) { + d(t, function (t, r) { + for (var s = {name: t, capital: i[r]}, l = 0; l < e.length; l++) s[e[l]] = t + n[l]; + o.call(a, s) + }) + } + }(["x", "y", "z", "radius", "angle", "single"], ["axisIndex", "axis", "index", "id"]), DP = d, AP = Co, + CP = function (t, e, i, n) { + this._dimName = t, this._axisIndex = e, this._valueWindow, this._percentWindow, this._dataExtent, this._minMaxSpan, this.ecModel = n, this._dataZoomModel = i + }; + CP.prototype = { + constructor: CP, hostedBy: function (t) { + return this._dataZoomModel === t + }, getDataValueWindow: function () { + return this._valueWindow.slice() + }, getDataPercentWindow: function () { + return this._percentWindow.slice() + }, getTargetSeriesModels: function () { + var t = [], e = this.ecModel; + return e.eachSeries(function (i) { + if (Zv(i.get("coordinateSystem"))) { + var n = this._dimName, o = e.queryComponents({ + mainType: n + "Axis", + index: i.get(n + "AxisIndex"), + id: i.get(n + "AxisId") + })[0]; + this._axisIndex === (o && o.componentIndex) && t.push(i) + } + }, this), t + }, getAxisModel: function () { + return this.ecModel.getComponent(this._dimName + "Axis", this._axisIndex) + }, getOtherAxisModel: function () { + var t, e, i = this._dimName, n = this.ecModel, o = this.getAxisModel(); + "x" === i || "y" === i ? (e = "gridIndex", t = "x" === i ? "y" : "x") : (e = "polarIndex", t = "angle" === i ? "radius" : "angle"); + var a; + return n.eachComponent(t + "Axis", function (t) { + (t.get(e) || 0) === (o.get(e) || 0) && (a = t) + }), a + }, getMinMaxSpan: function () { + return i(this._minMaxSpan) + }, calculateDataWindow: function (t) { + var e = this._dataExtent, i = this.getAxisModel().axis.scale, n = this._dataZoomModel.getRangePropMode(), + o = [0, 100], a = [t.start, t.end], r = []; + return DP(["startValue", "endValue"], function (e) { + r.push(null != t[e] ? i.parse(t[e]) : null) + }), DP([0, 1], function (t) { + var s = r[t], l = a[t]; + "percent" === n[t] ? (null == l && (l = o[t]), s = i.parse(To(l, o, e, !0))) : l = To(s, e, o, !0), r[t] = s, a[t] = l + }), {valueWindow: AP(r), percentWindow: AP(a)} + }, reset: function (t) { + if (t === this._dataZoomModel) { + var e = this.getTargetSeriesModels(); + this._dataExtent = Xv(this, this._dimName, e); + var i = this.calculateDataWindow(t.option); + this._valueWindow = i.valueWindow, this._percentWindow = i.percentWindow, qv(this), Yv(this) + } + }, restore: function (t) { + t === this._dataZoomModel && (this._valueWindow = this._percentWindow = null, Yv(this, !0)) + }, filterData: function (t, e) { + function i(t) { + return t >= r[0] && t <= r[1] + } + + if (t === this._dataZoomModel) { + var n = this._dimName, o = this.getTargetSeriesModels(), a = t.get("filterMode"), r = this._valueWindow; + "none" !== a && DP(o, function (t) { + var e = t.getData(), o = e.mapDimension(n, !0); + "weakFilter" === a ? e.filterSelf(function (t) { + for (var i, n, a, s = 0; s < o.length; s++) { + var l = e.get(o[s], t), u = !isNaN(l), h = l < r[0], c = l > r[1]; + if (u && !h && !c) return !0; + u && (a = !0), h && (i = !0), c && (n = !0) + } + return a && i && n + }) : DP(o, function (n) { + if ("empty" === a) t.setData(e.map(n, function (t) { + return i(t) ? t : NaN + })); else { + var o = {}; + o[n] = r, e.selectRange(o) + } + }), DP(o, function (t) { + e.setApproximateExtent(r, t) + }) + }) + } + } + }; + var LP = d, kP = TP, PP = vs({ + type: "dataZoom", + dependencies: ["xAxis", "yAxis", "zAxis", "radiusAxis", "angleAxis", "singleAxis", "series"], + defaultOption: { + zlevel: 0, + z: 4, + orient: null, + xAxisIndex: null, + yAxisIndex: null, + filterMode: "filter", + throttle: null, + start: 0, + end: 100, + startValue: null, + endValue: null, + minSpan: null, + maxSpan: null, + minValueSpan: null, + maxValueSpan: null, + rangeMode: null + }, + init: function (t, e, i) { + this._dataIntervalByAxis = {}, this._dataInfo = {}, this._axisProxies = {}, this.textStyleModel, this._autoThrottle = !0, this._rangePropMode = ["percent", "percent"]; + var n = Kv(t); + this.mergeDefaultAndTheme(t, i), this.doInit(n) + }, + mergeOption: function (t) { + var e = Kv(t); + n(this.option, t, !0), this.doInit(e) + }, + doInit: function (t) { + var e = this.option; + a_.canvasSupported || (e.realtime = !1), this._setDefaultThrottle(t), $v(this, t), LP([["start", "startValue"], ["end", "endValue"]], function (t, i) { + "value" === this._rangePropMode[i] && (e[t[0]] = null) + }, this), this.textStyleModel = this.getModel("textStyle"), this._resetTarget(), this._giveAxisProxies() + }, + _giveAxisProxies: function () { + var t = this._axisProxies; + this.eachTargetAxis(function (e, i, n, o) { + var a = this.dependentModels[e.axis][i], + r = a.__dzAxisProxy || (a.__dzAxisProxy = new CP(e.name, i, this, o)); + t[e.name + "_" + i] = r + }, this) + }, + _resetTarget: function () { + var t = this.option, e = this._judgeAutoMode(); + kP(function (e) { + var i = e.axisIndex; + t[i] = Si(t[i]) + }, this), "axisIndex" === e ? this._autoSetAxisIndex() : "orient" === e && this._autoSetOrient() + }, + _judgeAutoMode: function () { + var t = this.option, e = !1; + kP(function (i) { + null != t[i.axisIndex] && (e = !0) + }, this); + var i = t.orient; + return null == i && e ? "orient" : e ? void 0 : (null == i && (t.orient = "horizontal"), "axisIndex") + }, + _autoSetAxisIndex: function () { + var t = !0, e = this.get("orient", !0), i = this.option, n = this.dependentModels; + if (t) { + var o = "vertical" === e ? "y" : "x"; + n[o + "Axis"].length ? (i[o + "AxisIndex"] = [0], t = !1) : LP(n.singleAxis, function (n) { + t && n.get("orient", !0) === e && (i.singleAxisIndex = [n.componentIndex], t = !1) + }) + } + t && kP(function (e) { + if (t) { + var n = [], o = this.dependentModels[e.axis]; + if (o.length && !n.length) for (var a = 0, r = o.length; a < r; a++) "category" === o[a].get("type") && n.push(a); + i[e.axisIndex] = n, n.length && (t = !1) + } + }, this), t && this.ecModel.eachSeries(function (t) { + this._isSeriesHasAllAxesTypeOf(t, "value") && kP(function (e) { + var n = i[e.axisIndex], o = t.get(e.axisIndex), a = t.get(e.axisId); + l(n, o = t.ecModel.queryComponents({ + mainType: e.axis, + index: o, + id: a + })[0].componentIndex) < 0 && n.push(o) + }) + }, this) + }, + _autoSetOrient: function () { + var t; + this.eachTargetAxis(function (e) { + !t && (t = e.name) + }, this), this.option.orient = "y" === t ? "vertical" : "horizontal" + }, + _isSeriesHasAllAxesTypeOf: function (t, e) { + var i = !0; + return kP(function (n) { + var o = t.get(n.axisIndex), a = this.dependentModels[n.axis][o]; + a && a.get("type") === e || (i = !1) + }, this), i + }, + _setDefaultThrottle: function (t) { + if (t.hasOwnProperty("throttle") && (this._autoThrottle = !1), this._autoThrottle) { + var e = this.ecModel.option; + this.option.throttle = e.animation && e.animationDurationUpdate > 0 ? 100 : 20 + } + }, + getFirstTargetAxisModel: function () { + var t; + return kP(function (e) { + if (null == t) { + var i = this.get(e.axisIndex); + i.length && (t = this.dependentModels[e.axis][i[0]]) + } + }, this), t + }, + eachTargetAxis: function (t, e) { + var i = this.ecModel; + kP(function (n) { + LP(this.get(n.axisIndex), function (o) { + t.call(e, n, o, this, i) + }, this) + }, this) + }, + getAxisProxy: function (t, e) { + return this._axisProxies[t + "_" + e] + }, + getAxisModel: function (t, e) { + var i = this.getAxisProxy(t, e); + return i && i.getAxisModel() + }, + setRawRange: function (t, e) { + var i = this.option; + LP([["start", "startValue"], ["end", "endValue"]], function (e) { + null == t[e[0]] && null == t[e[1]] || (i[e[0]] = t[e[0]], i[e[1]] = t[e[1]]) + }, this), !e && $v(this, t) + }, + getPercentRange: function () { + var t = this.findRepresentativeAxisProxy(); + if (t) return t.getDataPercentWindow() + }, + getValueRange: function (t, e) { + if (null != t || null != e) return this.getAxisProxy(t, e).getDataValueWindow(); + var i = this.findRepresentativeAxisProxy(); + return i ? i.getDataValueWindow() : void 0 + }, + findRepresentativeAxisProxy: function (t) { + if (t) return t.__dzAxisProxy; + var e = this._axisProxies; + for (var i in e) if (e.hasOwnProperty(i) && e[i].hostedBy(this)) return e[i]; + for (var i in e) if (e.hasOwnProperty(i) && !e[i].hostedBy(this)) return e[i] + }, + getRangePropMode: function () { + return this._rangePropMode.slice() + } + }), NP = $M.extend({ + type: "dataZoom", render: function (t, e, i, n) { + this.dataZoomModel = t, this.ecModel = e, this.api = i + }, getTargetCoordInfo: function () { + function t(t, e, i, n) { + for (var o, a = 0; a < i.length; a++) if (i[a].model === t) { + o = i[a]; + break + } + o || i.push(o = {model: t, axisModels: [], coordIndex: n}), o.axisModels.push(e) + } + + var e = this.dataZoomModel, i = this.ecModel, n = {}; + return e.eachTargetAxis(function (e, o) { + var a = i.getComponent(e.axis, o); + if (a) { + var r = a.getCoordSysModel(); + r && t(r, a, n[r.mainType] || (n[r.mainType] = []), r.componentIndex) + } + }, this), n + } + }), OP = (PP.extend({ + type: "dataZoom.slider", + layoutMode: "box", + defaultOption: { + show: !0, + right: "ph", + top: "ph", + width: "ph", + height: "ph", + left: null, + bottom: null, + backgroundColor: "rgba(47,69,84,0)", + dataBackground: { + lineStyle: {color: "#2f4554", width: .5, opacity: .3}, + areaStyle: {color: "rgba(47,69,84,0.3)", opacity: .3} + }, + borderColor: "#ddd", + fillerColor: "rgba(167,183,204,0.4)", + handleIcon: "M8.2,13.6V3.9H6.3v9.7H3.1v14.9h3.3v9.7h1.8v-9.7h3.3V13.6H8.2z M9.7,24.4H4.8v-1.4h4.9V24.4z M9.7,19.1H4.8v-1.4h4.9V19.1z", + handleSize: "100%", + handleStyle: {color: "#a7b7cc"}, + labelPrecision: null, + labelFormatter: null, + showDetail: !0, + showDataShadow: "auto", + realtime: !0, + zoomLock: !1, + textStyle: {color: "#333"} + } + }), SS), EP = To, RP = Co, zP = m, BP = d, VP = "horizontal", GP = 5, + WP = ["line", "bar", "candlestick", "scatter"], FP = NP.extend({ + type: "dataZoom.slider", init: function (t, e) { + this._displayables = {}, this._orient, this._range, this._handleEnds, this._size, this._handleWidth, this._handleHeight, this._location, this._dragging, this._dataShadowInfo, this.api = e + }, render: function (t, e, i, n) { + FP.superApply(this, "render", arguments), wr(this, "_dispatchZoomAction", this.dataZoomModel.get("throttle"), "fixRate"), this._orient = t.get("orient"), !1 !== this.dataZoomModel.get("show") ? (n && "dataZoom" === n.type && n.from === this.uid || this._buildView(), this._updateView()) : this.group.removeAll() + }, remove: function () { + FP.superApply(this, "remove", arguments), br(this, "_dispatchZoomAction") + }, dispose: function () { + FP.superApply(this, "dispose", arguments), br(this, "_dispatchZoomAction") + }, _buildView: function () { + var t = this.group; + t.removeAll(), this._resetLocation(), this._resetInterval(); + var e = this._displayables.barGroup = new sw; + this._renderBackground(), this._renderHandle(), this._renderDataShadow(), t.add(e), this._positionGroup() + }, _resetLocation: function () { + var t = this.dataZoomModel, e = this.api, i = this._findCoordRect(), + n = {width: e.getWidth(), height: e.getHeight()}, o = this._orient === VP ? { + right: n.width - i.x - i.width, + top: n.height - 30 - 7, + width: i.width, + height: 30 + } : {right: 7, top: i.y, width: 30, height: i.height}, a = na(t.option); + d(["right", "top", "width", "height"], function (t) { + "ph" === a[t] && (a[t] = o[t]) + }); + var r = Qo(a, n, t.padding); + this._location = { + x: r.x, + y: r.y + }, this._size = [r.width, r.height], "vertical" === this._orient && this._size.reverse() + }, _positionGroup: function () { + var t = this.group, e = this._location, i = this._orient, n = this.dataZoomModel.getFirstTargetAxisModel(), + o = n && n.get("inverse"), a = this._displayables.barGroup, + r = (this._dataShadowInfo || {}).otherAxisInverse; + a.attr(i !== VP || o ? i === VP && o ? {scale: r ? [-1, 1] : [-1, -1]} : "vertical" !== i || o ? { + scale: r ? [-1, -1] : [-1, 1], + rotation: Math.PI / 2 + } : {scale: r ? [1, -1] : [1, 1], rotation: Math.PI / 2} : {scale: r ? [1, 1] : [1, -1]}); + var s = t.getBoundingRect([a]); + t.attr("position", [e.x - s.x, e.y - s.y]) + }, _getViewExtent: function () { + return [0, this._size[0]] + }, _renderBackground: function () { + var t = this.dataZoomModel, e = this._size, i = this._displayables.barGroup; + i.add(new OP({ + silent: !0, + shape: {x: 0, y: 0, width: e[0], height: e[1]}, + style: {fill: t.get("backgroundColor")}, + z2: -40 + })), i.add(new OP({ + shape: {x: 0, y: 0, width: e[0], height: e[1]}, + style: {fill: "transparent"}, + z2: 0, + onclick: m(this._onClickPanelClick, this) + })) + }, _renderDataShadow: function () { + var t = this._dataShadowInfo = this._prepareDataShadowInfo(); + if (t) { + var e = this._size, i = t.series, n = i.getRawData(), + o = i.getShadowDim ? i.getShadowDim() : t.otherDim; + if (null != o) { + var a = n.getDataExtent(o), s = .3 * (a[1] - a[0]); + a = [a[0] - s, a[1] + s]; + var l, u = [0, e[1]], h = [0, e[0]], c = [[e[0], 0], [0, 0]], d = [], f = h[1] / (n.count() - 1), + p = 0, g = Math.round(n.count() / e[0]); + n.each([o], function (t, e) { + if (g > 0 && e % g) p += f; else { + var i = null == t || isNaN(t) || "" === t, n = i ? 0 : EP(t, a, u, !0); + i && !l && e ? (c.push([c[c.length - 1][0], 0]), d.push([d[d.length - 1][0], 0])) : !i && l && (c.push([p, 0]), d.push([p, 0])), c.push([p, n]), d.push([p, n]), p += f, l = i + } + }); + var m = this.dataZoomModel; + this._displayables.barGroup.add(new wS({ + shape: {points: c}, + style: r({fill: m.get("dataBackgroundColor")}, m.getModel("dataBackground.areaStyle").getAreaStyle()), + silent: !0, + z2: -20 + })), this._displayables.barGroup.add(new bS({ + shape: {points: d}, + style: m.getModel("dataBackground.lineStyle").getLineStyle(), + silent: !0, + z2: -19 + })) + } + } + }, _prepareDataShadowInfo: function () { + var t = this.dataZoomModel, e = t.get("showDataShadow"); + if (!1 !== e) { + var i, n = this.ecModel; + return t.eachTargetAxis(function (o, a) { + d(t.getAxisProxy(o.name, a).getTargetSeriesModels(), function (t) { + if (!(i || !0 !== e && l(WP, t.get("type")) < 0)) { + var r, s = n.getComponent(o.axis, a).axis, u = Jv(o.name), h = t.coordinateSystem; + null != u && h.getOtherAxis && (r = h.getOtherAxis(s).inverse), u = t.getData().mapDimension(u), i = { + thisAxis: s, + series: t, + thisDim: o.name, + otherDim: u, + otherAxisInverse: r + } + } + }, this) + }, this), i + } + }, _renderHandle: function () { + var t = this._displayables, e = t.handles = [], i = t.handleLabels = [], n = this._displayables.barGroup, + o = this._size, a = this.dataZoomModel; + n.add(t.filler = new OP({ + draggable: !0, + cursor: Qv(this._orient), + drift: zP(this._onDragMove, this, "all"), + onmousemove: function (t) { + zw(t.event) + }, + ondragstart: zP(this._showDataInfo, this, !0), + ondragend: zP(this._onDragEnd, this), + onmouseover: zP(this._showDataInfo, this, !0), + onmouseout: zP(this._showDataInfo, this, !1), + style: {fill: a.get("fillerColor"), textPosition: "inside"} + })), n.add(new OP(Wn({ + silent: !0, + shape: {x: 0, y: 0, width: o[0], height: o[1]}, + style: { + stroke: a.get("dataBackgroundColor") || a.get("borderColor"), + lineWidth: 1, + fill: "rgba(0,0,0,0)" + } + }))), BP([0, 1], function (t) { + var o = _o(a.get("handleIcon"), { + cursor: Qv(this._orient), + draggable: !0, + drift: zP(this._onDragMove, this, t), + onmousemove: function (t) { + zw(t.event) + }, + ondragend: zP(this._onDragEnd, this), + onmouseover: zP(this._showDataInfo, this, !0), + onmouseout: zP(this._showDataInfo, this, !1) + }, {x: -1, y: 0, width: 2, height: 2}), r = o.getBoundingRect(); + this._handleHeight = Do(a.get("handleSize"), this._size[1]), this._handleWidth = r.width / r.height * this._handleHeight, o.setStyle(a.getModel("handleStyle").getItemStyle()); + var s = a.get("handleColor"); + null != s && (o.style.fill = s), n.add(e[t] = o); + var l = a.textStyleModel; + this.group.add(i[t] = new fS({ + silent: !0, + invisible: !0, + style: { + x: 0, + y: 0, + text: "", + textVerticalAlign: "middle", + textAlign: "center", + textFill: l.getTextColor(), + textFont: l.getFont() + }, + z2: 10 + })) + }, this) + }, _resetInterval: function () { + var t = this._range = this.dataZoomModel.getPercentRange(), e = this._getViewExtent(); + this._handleEnds = [EP(t[0], [0, 100], e, !0), EP(t[1], [0, 100], e, !0)] + }, _updateInterval: function (t, e) { + var i = this.dataZoomModel, n = this._handleEnds, o = this._getViewExtent(), + a = i.findRepresentativeAxisProxy().getMinMaxSpan(), r = [0, 100]; + PC(e, n, o, i.get("zoomLock") ? "all" : t, null != a.minSpan ? EP(a.minSpan, r, o, !0) : null, null != a.maxSpan ? EP(a.maxSpan, r, o, !0) : null); + var s = this._range, l = this._range = RP([EP(n[0], o, r, !0), EP(n[1], o, r, !0)]); + return !s || s[0] !== l[0] || s[1] !== l[1] + }, _updateView: function (t) { + var e = this._displayables, i = this._handleEnds, n = RP(i.slice()), o = this._size; + BP([0, 1], function (t) { + var n = e.handles[t], a = this._handleHeight; + n.attr({scale: [a / 2, a / 2], position: [i[t], o[1] / 2 - a / 2]}) + }, this), e.filler.setShape({x: n[0], y: 0, width: n[1] - n[0], height: o[1]}), this._updateDataInfo(t) + }, _updateDataInfo: function (t) { + function e(t) { + var e = go(n.handles[t].parent, this.group), i = vo(0 === t ? "right" : "left", e), + s = this._handleWidth / 2 + GP, l = mo([c[t] + (0 === t ? -s : s), this._size[1] / 2], e); + o[t].setStyle({ + x: l[0], + y: l[1], + textVerticalAlign: a === VP ? "middle" : i, + textAlign: a === VP ? i : "center", + text: r[t] + }) + } + + var i = this.dataZoomModel, n = this._displayables, o = n.handleLabels, a = this._orient, r = ["", ""]; + if (i.get("showDetail")) { + var s = i.findRepresentativeAxisProxy(); + if (s) { + var l = s.getAxisModel().axis, u = this._range, + h = t ? s.calculateDataWindow({start: u[0], end: u[1]}).valueWindow : s.getDataValueWindow(); + r = [this._formatLabel(h[0], l), this._formatLabel(h[1], l)] + } + } + var c = RP(this._handleEnds.slice()); + e.call(this, 0), e.call(this, 1) + }, _formatLabel: function (t, e) { + var i = this.dataZoomModel, n = i.get("labelFormatter"), o = i.get("labelPrecision"); + null != o && "auto" !== o || (o = e.getPixelPrecision()); + var a = null == t || isNaN(t) ? "" : "category" === e.type || "time" === e.type ? e.scale.getLabel(Math.round(t)) : t.toFixed(Math.min(o, 20)); + return x(n) ? n(t, a) : _(n) ? n.replace("{value}", a) : a + }, _showDataInfo: function (t) { + t = this._dragging || t; + var e = this._displayables.handleLabels; + e[0].attr("invisible", !t), e[1].attr("invisible", !t) + }, _onDragMove: function (t, e, i) { + this._dragging = !0; + var n = mo([e, i], this._displayables.barGroup.getLocalTransform(), !0), o = this._updateInterval(t, n[0]), + a = this.dataZoomModel.get("realtime"); + this._updateView(!a), o && a && this._dispatchZoomAction() + }, _onDragEnd: function () { + this._dragging = !1, this._showDataInfo(!1), !this.dataZoomModel.get("realtime") && this._dispatchZoomAction() + }, _onClickPanelClick: function (t) { + var e = this._size, i = this._displayables.barGroup.transformCoordToLocal(t.offsetX, t.offsetY); + if (!(i[0] < 0 || i[0] > e[0] || i[1] < 0 || i[1] > e[1])) { + var n = this._handleEnds, o = (n[0] + n[1]) / 2, a = this._updateInterval("all", i[0] - o); + this._updateView(), a && this._dispatchZoomAction() + } + }, _dispatchZoomAction: function () { + var t = this._range; + this.api.dispatchAction({ + type: "dataZoom", + from: this.uid, + dataZoomId: this.dataZoomModel.id, + start: t[0], + end: t[1] + }) + }, _findCoordRect: function () { + var t; + if (BP(this.getTargetCoordInfo(), function (e) { + if (!t && e.length) { + var i = e[0].model.coordinateSystem; + t = i.getRect && i.getRect() + } + }), !t) { + var e = this.api.getWidth(), i = this.api.getHeight(); + t = {x: .2 * e, y: .2 * i, width: .6 * e, height: .6 * i} + } + return t + } + }); + PP.extend({ + type: "dataZoom.inside", + defaultOption: { + disabled: !1, + zoomLock: !1, + zoomOnMouseWheel: !0, + moveOnMouseMove: !0, + preventDefaultMouseMove: !0 + } + }); + var HP = v, ZP = "\0_ec_dataZoom_roams", UP = m, XP = NP.extend({ + type: "dataZoom.inside", init: function (t, e) { + this._range + }, render: function (t, e, i, n) { + XP.superApply(this, "render", arguments), this._range = t.getPercentRange(), d(this.getTargetCoordInfo(), function (e, n) { + var o = f(e, function (t) { + return iy(t.model) + }); + d(e, function (e) { + var a = e.model, r = t.option; + ty(i, { + coordId: iy(a), + allCoordIds: o, + containsPoint: function (t, e, i) { + return a.coordinateSystem.containPoint([e, i]) + }, + dataZoomId: t.id, + throttleRate: t.get("throttle", !0), + panGetRange: UP(this._onPan, this, e, n), + zoomGetRange: UP(this._onZoom, this, e, n), + zoomLock: r.zoomLock, + disabled: r.disabled, + roamControllerOpt: { + zoomOnMouseWheel: r.zoomOnMouseWheel, + moveOnMouseMove: r.moveOnMouseMove, + preventDefaultMouseMove: r.preventDefaultMouseMove + } + }) + }, this) + }, this) + }, dispose: function () { + ey(this.api, this.dataZoomModel.id), XP.superApply(this, "dispose", arguments), this._range = null + }, _onPan: function (t, e, i, n, o, a, r, s, l) { + var u = this._range, h = u.slice(), c = t.axisModels[0]; + if (c) { + var d = jP[e]([a, r], [s, l], c, i, t), f = d.signal * (h[1] - h[0]) * d.pixel / d.pixelLength; + return PC(f, h, [0, 100], "all"), this._range = h, u[0] !== h[0] || u[1] !== h[1] ? h : void 0 + } + }, _onZoom: function (t, e, i, n, o, a) { + var r = this._range, s = r.slice(), l = t.axisModels[0]; + if (l) { + var u = jP[e](null, [o, a], l, i, t), + h = (u.signal > 0 ? u.pixelStart + u.pixelLength - u.pixel : u.pixel - u.pixelStart) / u.pixelLength * (s[1] - s[0]) + s[0]; + n = Math.max(1 / n, 0), s[0] = (s[0] - h) * n + h, s[1] = (s[1] - h) * n + h; + var c = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); + return PC(0, s, [0, 100], 0, c.minSpan, c.maxSpan), this._range = s, r[0] !== s[0] || r[1] !== s[1] ? s : void 0 + } + } + }), jP = { + grid: function (t, e, i, n, o) { + var a = i.axis, r = {}, s = o.model.coordinateSystem.getRect(); + return t = t || [0, 0], "x" === a.dim ? (r.pixel = e[0] - t[0], r.pixelLength = s.width, r.pixelStart = s.x, r.signal = a.inverse ? 1 : -1) : (r.pixel = e[1] - t[1], r.pixelLength = s.height, r.pixelStart = s.y, r.signal = a.inverse ? -1 : 1), r + }, polar: function (t, e, i, n, o) { + var a = i.axis, r = {}, s = o.model.coordinateSystem, l = s.getRadiusAxis().getExtent(), + u = s.getAngleAxis().getExtent(); + return t = t ? s.pointToCoord(t) : [0, 0], e = s.pointToCoord(e), "radiusAxis" === i.mainType ? (r.pixel = e[0] - t[0], r.pixelLength = l[1] - l[0], r.pixelStart = l[0], r.signal = a.inverse ? 1 : -1) : (r.pixel = e[1] - t[1], r.pixelLength = u[1] - u[0], r.pixelStart = u[0], r.signal = a.inverse ? -1 : 1), r + }, singleAxis: function (t, e, i, n, o) { + var a = i.axis, r = o.model.coordinateSystem.getRect(), s = {}; + return t = t || [0, 0], "horizontal" === a.orient ? (s.pixel = e[0] - t[0], s.pixelLength = r.width, s.pixelStart = r.x, s.signal = a.inverse ? 1 : -1) : (s.pixel = e[1] - t[1], s.pixelLength = r.height, s.pixelStart = r.y, s.signal = a.inverse ? -1 : 1), s + } + }; + hs({ + getTargetSeries: function (t) { + var e = R(); + return t.eachComponent("dataZoom", function (t) { + t.eachTargetAxis(function (t, i, n) { + d(n.getAxisProxy(t.name, i).getTargetSeriesModels(), function (t) { + e.set(t.uid, t) + }) + }) + }), e + }, modifyOutputEnd: !0, overallReset: function (t, e) { + t.eachComponent("dataZoom", function (t) { + t.eachTargetAxis(function (t, i, n) { + n.getAxisProxy(t.name, i).reset(n, e) + }), t.eachTargetAxis(function (t, i, n) { + n.getAxisProxy(t.name, i).filterData(n, e) + }) + }), t.eachComponent("dataZoom", function (t) { + var e = t.findRepresentativeAxisProxy(), i = e.getDataPercentWindow(), n = e.getDataValueWindow(); + t.setRawRange({start: i[0], end: i[1], startValue: n[0], endValue: n[1]}, !0) + }) + } + }), cs("dataZoom", function (t, e) { + var i = Uv(m(e.eachComponent, e, "dataZoom"), TP, function (t, e) { + return t.get(e.axisIndex) + }), n = []; + e.eachComponent({mainType: "dataZoom", query: t}, function (t, e) { + n.push.apply(n, i(t).nodes) + }), d(n, function (e, i) { + e.setRawRange({start: t.start, end: t.end, startValue: t.startValue, endValue: t.endValue}) + }) + }); + var YP = d, qP = function (t) { + var e = t && t.visualMap; + y(e) || (e = e ? [e] : []), YP(e, function (t) { + if (t) { + cy(t, "splitList") && !cy(t, "pieces") && (t.pieces = t.splitList, delete t.splitList); + var e = t.pieces; + e && y(e) && YP(e, function (t) { + w(t) && (cy(t, "start") && !cy(t, "min") && (t.min = t.start), cy(t, "end") && !cy(t, "max") && (t.max = t.end)) + }) + } + }) + }; + hM.registerSubTypeDefaulter("visualMap", function (t) { + return t.categories || (t.pieces ? t.pieces.length > 0 : t.splitNumber > 0) && !t.calculable ? "piecewise" : "continuous" + }); + var KP = LI.VISUAL.COMPONENT; + ps(KP, { + createOnAllSeries: !0, reset: function (t, e) { + var i = []; + return e.eachComponent("visualMap", function (e) { + var n = t.pipelineContext; + !e.isTargetSeries(t) || n && n.large || i.push(gv(e.stateList, e.targetVisuals, m(e.getValueState, e), e.getDataDimension(t.getData()))) + }), i + } + }), ps(KP, { + createOnAllSeries: !0, reset: function (t, e) { + var i = t.getData(), n = []; + e.eachComponent("visualMap", function (e) { + if (e.isTargetSeries(t)) { + var o = e.getVisualMeta(m(dy, null, t, e)) || {stops: [], outerColors: []}, + a = e.getDataDimension(i), r = i.getDimensionInfo(a); + null != r && (o.dimension = r.index, n.push(o)) + } + }), t.getData().setVisual("visualMeta", n) + } + }); + var $P = { + get: function (t, e, n) { + var o = i((JP[t] || {})[e]); + return n && y(o) ? o[o.length - 1] : o + } + }, JP = { + color: {active: ["#006edd", "#e0ffff"], inactive: ["rgba(0,0,0,0)"]}, + colorHue: {active: [0, 360], inactive: [0, 0]}, + colorSaturation: {active: [.3, 1], inactive: [0, 0]}, + colorLightness: {active: [.9, .5], inactive: [0, 0]}, + colorAlpha: {active: [.3, 1], inactive: [0, 0]}, + opacity: {active: [.3, 1], inactive: [0, 0]}, + symbol: {active: ["circle", "roundRect", "diamond"], inactive: ["none"]}, + symbolSize: {active: [10, 50], inactive: [0, 0]} + }, QP = ZA.mapVisual, tN = ZA.eachVisual, eN = y, iN = d, nN = Co, oN = To, aN = B, rN = vs({ + type: "visualMap", + dependencies: ["series"], + stateList: ["inRange", "outOfRange"], + replacableOptionKeys: ["inRange", "outOfRange", "target", "controller", "color"], + dataBound: [-1 / 0, 1 / 0], + layoutMode: {type: "box", ignoreSize: !0}, + defaultOption: { + show: !0, + zlevel: 0, + z: 4, + seriesIndex: "all", + min: 0, + max: 200, + dimension: null, + inRange: null, + outOfRange: null, + left: 0, + right: null, + top: null, + bottom: 0, + itemWidth: null, + itemHeight: null, + inverse: !1, + orient: "vertical", + backgroundColor: "rgba(0,0,0,0)", + borderColor: "#ccc", + contentColor: "#5793f3", + inactiveColor: "#aaa", + borderWidth: 0, + padding: 5, + textGap: 10, + precision: 0, + color: null, + formatter: null, + text: null, + textStyle: {color: "#333"} + }, + init: function (t, e, i) { + this._dataExtent, this.targetVisuals = {}, this.controllerVisuals = {}, this.textStyleModel, this.itemSize, this.mergeDefaultAndTheme(t, i) + }, + optionUpdated: function (t, e) { + var i = this.option; + a_.canvasSupported || (i.realtime = !1), !e && fv(i, t, this.replacableOptionKeys), this.textStyleModel = this.getModel("textStyle"), this.resetItemSize(), this.completeVisualOption() + }, + resetVisual: function (t) { + var e = this.stateList; + t = m(t, this), this.controllerVisuals = dv(this.option.controller, e, t), this.targetVisuals = dv(this.option.target, e, t) + }, + getTargetSeriesIndices: function () { + var t = this.option.seriesIndex, e = []; + return null == t || "all" === t ? this.ecModel.eachSeries(function (t, i) { + e.push(i) + }) : e = Si(t), e + }, + eachTargetSeries: function (t, e) { + d(this.getTargetSeriesIndices(), function (i) { + t.call(e, this.ecModel.getSeriesByIndex(i)) + }, this) + }, + isTargetSeries: function (t) { + var e = !1; + return this.eachTargetSeries(function (i) { + i === t && (e = !0) + }), e + }, + formatValueText: function (t, e, i) { + function n(t) { + return t === l[0] ? "min" : t === l[1] ? "max" : (+t).toFixed(Math.min(s, 20)) + } + + var o, a, r = this.option, s = r.precision, l = this.dataBound, u = r.formatter; + return i = i || ["<", ">"], y(t) && (t = t.slice(), o = !0), a = e ? t : o ? [n(t[0]), n(t[1])] : n(t), _(u) ? u.replace("{value}", o ? a[0] : a).replace("{value2}", o ? a[1] : a) : x(u) ? o ? u(t[0], t[1]) : u(t) : o ? t[0] === l[0] ? i[0] + " " + a[1] : t[1] === l[1] ? i[1] + " " + a[0] : a[0] + " - " + a[1] : a + }, + resetExtent: function () { + var t = this.option, e = nN([t.min, t.max]); + this._dataExtent = e + }, + getDataDimension: function (t) { + var e = this.option.dimension, i = t.dimensions; + if (null != e || i.length) { + if (null != e) return t.getDimension(e); + for (var n = t.dimensions, o = n.length - 1; o >= 0; o--) { + var a = n[o]; + if (!t.getDimensionInfo(a).isCalculationCoord) return a + } + } + }, + getExtent: function () { + return this._dataExtent.slice() + }, + completeVisualOption: function () { + function t(t) { + eN(o.color) && !t.inRange && (t.inRange = {color: o.color.slice().reverse()}), t.inRange = t.inRange || {color: e.get("gradientColor")}, iN(this.stateList, function (e) { + var i = t[e]; + if (_(i)) { + var n = $P.get(i, "active", l); + n ? (t[e] = {}, t[e][i] = n) : delete t[e] + } + }, this) + } + + var e = this.ecModel, o = this.option, a = {inRange: o.inRange, outOfRange: o.outOfRange}, + r = o.target || (o.target = {}), s = o.controller || (o.controller = {}); + n(r, a), n(s, a); + var l = this.isCategory(); + t.call(this, r), t.call(this, s), function (t, e, i) { + var n = t[e], o = t[i]; + n && !o && (o = t[i] = {}, iN(n, function (t, e) { + if (ZA.isValidType(e)) { + var i = $P.get(e, "inactive", l); + null != i && (o[e] = i, "color" !== e || o.hasOwnProperty("opacity") || o.hasOwnProperty("colorAlpha") || (o.opacity = [0, 0])) + } + })) + }.call(this, r, "inRange", "outOfRange"), function (t) { + var e = (t.inRange || {}).symbol || (t.outOfRange || {}).symbol, + n = (t.inRange || {}).symbolSize || (t.outOfRange || {}).symbolSize, o = this.get("inactiveColor"); + iN(this.stateList, function (a) { + var r = this.itemSize, s = t[a]; + s || (s = t[a] = {color: l ? o : [o]}), null == s.symbol && (s.symbol = e && i(e) || (l ? "roundRect" : ["roundRect"])), null == s.symbolSize && (s.symbolSize = n && i(n) || (l ? r[0] : [r[0], r[0]])), s.symbol = QP(s.symbol, function (t) { + return "none" === t || "square" === t ? "roundRect" : t + }); + var u = s.symbolSize; + if (null != u) { + var h = -1 / 0; + tN(u, function (t) { + t > h && (h = t) + }), s.symbolSize = QP(u, function (t) { + return oN(t, [0, h], [0, r[0]], !0) + }) + } + }, this) + }.call(this, s) + }, + resetItemSize: function () { + this.itemSize = [parseFloat(this.get("itemWidth")), parseFloat(this.get("itemHeight"))] + }, + isCategory: function () { + return !!this.option.categories + }, + setSelected: aN, + getValueState: aN, + getVisualMeta: aN + }), sN = [20, 140], lN = rN.extend({ + type: "visualMap.continuous", + defaultOption: { + align: "auto", + calculable: !1, + range: null, + realtime: !0, + itemHeight: null, + itemWidth: null, + hoverLink: !0, + hoverLinkDataSize: null, + hoverLinkOnHandle: null + }, + optionUpdated: function (t, e) { + lN.superApply(this, "optionUpdated", arguments), this.resetExtent(), this.resetVisual(function (t) { + t.mappingMethod = "linear", t.dataExtent = this.getExtent() + }), this._resetRange() + }, + resetItemSize: function () { + lN.superApply(this, "resetItemSize", arguments); + var t = this.itemSize; + "horizontal" === this._orient && t.reverse(), (null == t[0] || isNaN(t[0])) && (t[0] = sN[0]), (null == t[1] || isNaN(t[1])) && (t[1] = sN[1]) + }, + _resetRange: function () { + var t = this.getExtent(), e = this.option.range; + !e || e.auto ? (t.auto = 1, this.option.range = t) : y(e) && (e[0] > e[1] && e.reverse(), e[0] = Math.max(e[0], t[0]), e[1] = Math.min(e[1], t[1])) + }, + completeVisualOption: function () { + rN.prototype.completeVisualOption.apply(this, arguments), d(this.stateList, function (t) { + var e = this.option.controller[t].symbolSize; + e && e[0] !== e[1] && (e[0] = 0) + }, this) + }, + setSelected: function (t) { + this.option.range = t.slice(), this._resetRange() + }, + getSelected: function () { + var t = this.getExtent(), e = Co((this.get("range") || []).slice()); + return e[0] > t[1] && (e[0] = t[1]), e[1] > t[1] && (e[1] = t[1]), e[0] < t[0] && (e[0] = t[0]), e[1] < t[0] && (e[1] = t[0]), e + }, + getValueState: function (t) { + var e = this.option.range, i = this.getExtent(); + return (e[0] <= i[0] || e[0] <= t) && (e[1] >= i[1] || t <= e[1]) ? "inRange" : "outOfRange" + }, + findTargetDataIndices: function (t) { + var e = []; + return this.eachTargetSeries(function (i) { + var n = [], o = i.getData(); + o.each(this.getDataDimension(o), function (e, i) { + t[0] <= e && e <= t[1] && n.push(i) + }, this), e.push({seriesId: i.id, dataIndex: n}) + }, this), e + }, + getVisualMeta: function (t) { + function e(e, i) { + o.push({value: e, color: t(e, i)}) + } + + for (var i = fy(0, 0, this.getExtent()), n = fy(0, 0, this.option.range.slice()), o = [], a = 0, r = 0, s = n.length, l = i.length; r < l && (!n.length || i[r] <= n[0]); r++) i[r] < n[a] && e(i[r], "outOfRange"); + for (u = 1; a < s; a++, u = 0) u && o.length && e(n[a], "outOfRange"), e(n[a], "inRange"); + for (var u = 1; r < l; r++) (!n.length || n[n.length - 1] < i[r]) && (u && (o.length && e(o[o.length - 1].value, "outOfRange"), u = 0), e(i[r], "outOfRange")); + var h = o.length; + return {stops: o, outerColors: [h ? o[0].color : "transparent", h ? o[h - 1].color : "transparent"]} + } + }), uN = ys({ + type: "visualMap", autoPositionValues: {left: 1, right: 1, top: 1, bottom: 1}, init: function (t, e) { + this.ecModel = t, this.api = e, this.visualMapModel + }, render: function (t, e, i, n) { + this.visualMapModel = t, !1 !== t.get("show") ? this.doRender.apply(this, arguments) : this.group.removeAll() + }, renderBackground: function (t) { + var e = this.visualMapModel, i = KS(e.get("padding") || 0), n = t.getBoundingRect(); + t.add(new SS({ + z2: -1, + silent: !0, + shape: {x: n.x - i[3], y: n.y - i[0], width: n.width + i[3] + i[1], height: n.height + i[0] + i[2]}, + style: {fill: e.get("backgroundColor"), stroke: e.get("borderColor"), lineWidth: e.get("borderWidth")} + })) + }, getControllerVisual: function (t, e, i) { + function n(t) { + return s[t] + } + + function o(t, e) { + s[t] = e + } + + var a = (i = i || {}).forceState, r = this.visualMapModel, s = {}; + if ("symbol" === e && (s.symbol = r.get("itemSymbol")), "color" === e) { + var l = r.get("contentColor"); + s.color = l + } + var u = r.controllerVisuals[a || r.getValueState(t)]; + return d(ZA.prepareVisualTypes(u), function (a) { + var r = u[a]; + i.convertOpacityToAlpha && "opacity" === a && (a = "colorAlpha", r = u.__alphaForOpacity), ZA.dependsOn(a, e) && r && r.applyVisual(t, n, o) + }), s[e] + }, positionGroup: function (t) { + var e = this.visualMapModel, i = this.api; + ta(t, e.getBoxLayoutParams(), {width: i.getWidth(), height: i.getHeight()}) + }, doRender: B + }), hN = To, cN = d, dN = Math.min, fN = Math.max, pN = 12, gN = 6, mN = uN.extend({ + type: "visualMap.continuous", init: function () { + mN.superApply(this, "init", arguments), this._shapes = {}, this._dataInterval = [], this._handleEnds = [], this._orient, this._useHandle, this._hoverLinkDataIndices = [], this._dragging, this._hovering + }, doRender: function (t, e, i, n) { + n && "selectDataRange" === n.type && n.from === this.uid || this._buildView() + }, _buildView: function () { + this.group.removeAll(); + var t = this.visualMapModel, e = this.group; + this._orient = t.get("orient"), this._useHandle = t.get("calculable"), this._resetInterval(), this._renderBar(e); + var i = t.get("text"); + this._renderEndsText(e, i, 0), this._renderEndsText(e, i, 1), this._updateView(!0), this.renderBackground(e), this._updateView(), this._enableHoverLinkToSeries(), this._enableHoverLinkFromSeries(), this.positionGroup(e) + }, _renderEndsText: function (t, e, i) { + if (e) { + var n = e[1 - i]; + n = null != n ? n + "" : ""; + var o = this.visualMapModel, a = o.get("textGap"), r = o.itemSize, s = this._shapes.barGroup, + l = this._applyTransform([r[0] / 2, 0 === i ? -a : r[1] + a], s), + u = this._applyTransform(0 === i ? "bottom" : "top", s), h = this._orient, + c = this.visualMapModel.textStyleModel; + this.group.add(new fS({ + style: { + x: l[0], + y: l[1], + textVerticalAlign: "horizontal" === h ? "middle" : u, + textAlign: "horizontal" === h ? u : "center", + text: n, + textFont: c.getFont(), + textFill: c.getTextColor() + } + })) + } + }, _renderBar: function (t) { + var e = this.visualMapModel, i = this._shapes, n = e.itemSize, o = this._orient, a = this._useHandle, + r = py(e, this.api, n), s = i.barGroup = this._createBarGroup(r); + s.add(i.outOfRange = my()), s.add(i.inRange = my(null, a ? wy(this._orient) : null, m(this._dragHandle, this, "all", !1), m(this._dragHandle, this, "all", !0))); + var l = e.textStyleModel.getTextRect("国"), u = fN(l.width, l.height); + a && (i.handleThumbs = [], i.handleLabels = [], i.handleLabelPoints = [], this._createHandle(s, 0, n, u, o, r), this._createHandle(s, 1, n, u, o, r)), this._createIndicator(s, n, u, o), t.add(s) + }, _createHandle: function (t, e, i, n, o) { + var a = m(this._dragHandle, this, e, !1), r = m(this._dragHandle, this, e, !0), + s = my(vy(e, n), wy(this._orient), a, r); + s.position[0] = i[0], t.add(s); + var l = this.visualMapModel.textStyleModel, u = new fS({ + draggable: !0, drift: a, onmousemove: function (t) { + zw(t.event) + }, ondragend: r, style: {x: 0, y: 0, text: "", textFont: l.getFont(), textFill: l.getTextColor()} + }); + this.group.add(u); + var h = ["horizontal" === o ? n / 2 : 1.5 * n, "horizontal" === o ? 0 === e ? -1.5 * n : 1.5 * n : 0 === e ? -n / 2 : n / 2], + c = this._shapes; + c.handleThumbs[e] = s, c.handleLabelPoints[e] = h, c.handleLabels[e] = u + }, _createIndicator: function (t, e, i, n) { + var o = my([[0, 0]], "move"); + o.position[0] = e[0], o.attr({invisible: !0, silent: !0}), t.add(o); + var a = this.visualMapModel.textStyleModel, r = new fS({ + silent: !0, + invisible: !0, + style: {x: 0, y: 0, text: "", textFont: a.getFont(), textFill: a.getTextColor()} + }); + this.group.add(r); + var s = ["horizontal" === n ? i / 2 : gN + 3, 0], l = this._shapes; + l.indicator = o, l.indicatorLabel = r, l.indicatorLabelPoint = s + }, _dragHandle: function (t, e, i, n) { + if (this._useHandle) { + if (this._dragging = !e, !e) { + var o = this._applyTransform([i, n], this._shapes.barGroup, !0); + this._updateInterval(t, o[1]), this._updateView() + } + e === !this.visualMapModel.get("realtime") && this.api.dispatchAction({ + type: "selectDataRange", + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: this._dataInterval.slice() + }), e ? !this._hovering && this._clearHoverLinkToSeries() : _y(this.visualMapModel) && this._doHoverLinkToSeries(this._handleEnds[t], !1) + } + }, _resetInterval: function () { + var t = this.visualMapModel, e = this._dataInterval = t.getSelected(), i = t.getExtent(), + n = [0, t.itemSize[1]]; + this._handleEnds = [hN(e[0], i, n, !0), hN(e[1], i, n, !0)] + }, _updateInterval: function (t, e) { + e = e || 0; + var i = this.visualMapModel, n = this._handleEnds, o = [0, i.itemSize[1]]; + PC(e, n, o, t, 0); + var a = i.getExtent(); + this._dataInterval = [hN(n[0], o, a, !0), hN(n[1], o, a, !0)] + }, _updateView: function (t) { + var e = this.visualMapModel, i = e.getExtent(), n = this._shapes, o = [0, e.itemSize[1]], + a = t ? o : this._handleEnds, r = this._createBarVisual(this._dataInterval, i, a, "inRange"), + s = this._createBarVisual(i, i, o, "outOfRange"); + n.inRange.setStyle({ + fill: r.barColor, + opacity: r.opacity + }).setShape("points", r.barPoints), n.outOfRange.setStyle({ + fill: s.barColor, + opacity: s.opacity + }).setShape("points", s.barPoints), this._updateHandle(a, r) + }, _createBarVisual: function (t, e, i, n) { + var o = {forceState: n, convertOpacityToAlpha: !0}, a = this._makeColorGradient(t, o), + r = [this.getControllerVisual(t[0], "symbolSize", o), this.getControllerVisual(t[1], "symbolSize", o)], + s = this._createBarPoints(i, r); + return {barColor: new LS(0, 0, 0, 1, a), barPoints: s, handlesColor: [a[0].color, a[a.length - 1].color]} + }, _makeColorGradient: function (t, e) { + var i = [], n = (t[1] - t[0]) / 100; + i.push({color: this.getControllerVisual(t[0], "color", e), offset: 0}); + for (var o = 1; o < 100; o++) { + var a = t[0] + n * o; + if (a > t[1]) break; + i.push({color: this.getControllerVisual(a, "color", e), offset: o / 100}) + } + return i.push({color: this.getControllerVisual(t[1], "color", e), offset: 1}), i + }, _createBarPoints: function (t, e) { + var i = this.visualMapModel.itemSize; + return [[i[0] - e[0], t[0]], [i[0], t[0]], [i[0], t[1]], [i[0] - e[1], t[1]]] + }, _createBarGroup: function (t) { + var e = this._orient, i = this.visualMapModel.get("inverse"); + return new sw("horizontal" !== e || i ? "horizontal" === e && i ? { + scale: "bottom" === t ? [-1, 1] : [1, 1], + rotation: -Math.PI / 2 + } : "vertical" !== e || i ? {scale: "left" === t ? [1, 1] : [-1, 1]} : {scale: "left" === t ? [1, -1] : [-1, -1]} : { + scale: "bottom" === t ? [1, 1] : [-1, 1], + rotation: Math.PI / 2 + }) + }, _updateHandle: function (t, e) { + if (this._useHandle) { + var i = this._shapes, n = this.visualMapModel, o = i.handleThumbs, a = i.handleLabels; + cN([0, 1], function (r) { + var s = o[r]; + s.setStyle("fill", e.handlesColor[r]), s.position[1] = t[r]; + var l = mo(i.handleLabelPoints[r], go(s, this.group)); + a[r].setStyle({ + x: l[0], + y: l[1], + text: n.formatValueText(this._dataInterval[r]), + textVerticalAlign: "middle", + textAlign: this._applyTransform("horizontal" === this._orient ? 0 === r ? "bottom" : "top" : "left", i.barGroup) + }) + }, this) + } + }, _showIndicator: function (t, e, i, n) { + var o = this.visualMapModel, a = o.getExtent(), r = o.itemSize, s = [0, r[1]], l = hN(t, a, s, !0), + u = this._shapes, h = u.indicator; + if (h) { + h.position[1] = l, h.attr("invisible", !1), h.setShape("points", yy(!!i, n, l, r[1])); + var c = {convertOpacityToAlpha: !0}, d = this.getControllerVisual(t, "color", c); + h.setStyle("fill", d); + var f = mo(u.indicatorLabelPoint, go(h, this.group)), p = u.indicatorLabel; + p.attr("invisible", !1); + var g = this._applyTransform("left", u.barGroup), m = this._orient; + p.setStyle({ + text: (i || "") + o.formatValueText(e), + textVerticalAlign: "horizontal" === m ? g : "middle", + textAlign: "horizontal" === m ? "center" : g, + x: f[0], + y: f[1] + }) + } + }, _enableHoverLinkToSeries: function () { + var t = this; + this._shapes.barGroup.on("mousemove", function (e) { + if (t._hovering = !0, !t._dragging) { + var i = t.visualMapModel.itemSize, + n = t._applyTransform([e.offsetX, e.offsetY], t._shapes.barGroup, !0, !0); + n[1] = dN(fN(0, n[1]), i[1]), t._doHoverLinkToSeries(n[1], 0 <= n[0] && n[0] <= i[0]) + } + }).on("mouseout", function () { + t._hovering = !1, !t._dragging && t._clearHoverLinkToSeries() + }) + }, _enableHoverLinkFromSeries: function () { + var t = this.api.getZr(); + this.visualMapModel.option.hoverLink ? (t.on("mouseover", this._hoverLinkFromSeriesMouseOver, this), t.on("mouseout", this._hideIndicator, this)) : this._clearHoverLinkFromSeries() + }, _doHoverLinkToSeries: function (t, e) { + var i = this.visualMapModel, n = i.itemSize; + if (i.option.hoverLink) { + var o = [0, n[1]], a = i.getExtent(); + t = dN(fN(o[0], t), o[1]); + var r = xy(i, a, o), s = [t - r, t + r], l = hN(t, o, a, !0), + u = [hN(s[0], o, a, !0), hN(s[1], o, a, !0)]; + s[0] < o[0] && (u[0] = -1 / 0), s[1] > o[1] && (u[1] = 1 / 0), e && (u[0] === -1 / 0 ? this._showIndicator(l, u[1], "< ", r) : u[1] === 1 / 0 ? this._showIndicator(l, u[0], "> ", r) : this._showIndicator(l, l, "≈ ", r)); + var h = this._hoverLinkDataIndices, c = []; + (e || _y(i)) && (c = this._hoverLinkDataIndices = i.findTargetDataIndices(u)); + var d = ki(h, c); + this._dispatchHighDown("downplay", gy(d[0])), this._dispatchHighDown("highlight", gy(d[1])) + } + }, _hoverLinkFromSeriesMouseOver: function (t) { + var e = t.target, i = this.visualMapModel; + if (e && null != e.dataIndex) { + var n = this.ecModel.getSeriesByIndex(e.seriesIndex); + if (i.isTargetSeries(n)) { + var o = n.getData(e.dataType), a = o.get(i.getDataDimension(o), e.dataIndex, !0); + isNaN(a) || this._showIndicator(a, a) + } + } + }, _hideIndicator: function () { + var t = this._shapes; + t.indicator && t.indicator.attr("invisible", !0), t.indicatorLabel && t.indicatorLabel.attr("invisible", !0) + }, _clearHoverLinkToSeries: function () { + this._hideIndicator(); + var t = this._hoverLinkDataIndices; + this._dispatchHighDown("downplay", gy(t)), t.length = 0 + }, _clearHoverLinkFromSeries: function () { + this._hideIndicator(); + var t = this.api.getZr(); + t.off("mouseover", this._hoverLinkFromSeriesMouseOver), t.off("mouseout", this._hideIndicator) + }, _applyTransform: function (t, e, i, n) { + var o = go(e, n ? null : this.group); + return BS[y(t) ? "applyTransform" : "transformDirection"](t, o, i) + }, _dispatchHighDown: function (t, e) { + e && e.length && this.api.dispatchAction({type: t, batch: e}) + }, dispose: function () { + this._clearHoverLinkFromSeries(), this._clearHoverLinkToSeries() + }, remove: function () { + this._clearHoverLinkFromSeries(), this._clearHoverLinkToSeries() + } + }); + cs({type: "selectDataRange", event: "dataRangeSelected", update: "update"}, function (t, e) { + e.eachComponent({mainType: "visualMap", query: t}, function (e) { + e.setSelected(t.selected) + }) + }), us(qP); + var vN = rN.extend({ + type: "visualMap.piecewise", + defaultOption: { + selected: null, + minOpen: !1, + maxOpen: !1, + align: "auto", + itemWidth: 20, + itemHeight: 14, + itemSymbol: "roundRect", + pieceList: null, + categories: null, + splitNumber: 5, + selectedMode: "multiple", + itemGap: 10, + hoverLink: !0, + showLabel: null + }, + optionUpdated: function (t, e) { + vN.superApply(this, "optionUpdated", arguments), this._pieceList = [], this.resetExtent(); + var n = this._mode = this._determineMode(); + yN[this._mode].call(this), this._resetSelected(t, e); + var o = this.option.categories; + this.resetVisual(function (t, e) { + "categories" === n ? (t.mappingMethod = "category", t.categories = i(o)) : (t.dataExtent = this.getExtent(), t.mappingMethod = "piecewise", t.pieceList = f(this._pieceList, function (t) { + var t = i(t); + return "inRange" !== e && (t.visual = null), t + })) + }) + }, + completeVisualOption: function () { + function t(t, e, i) { + return t && t[e] && (w(t[e]) ? t[e].hasOwnProperty(i) : t[e] === i) + } + + var e = this.option, i = {}, n = ZA.listVisualTypes(), o = this.isCategory(); + d(e.pieces, function (t) { + d(n, function (e) { + t.hasOwnProperty(e) && (i[e] = 1) + }) + }), d(i, function (i, n) { + var a = 0; + d(this.stateList, function (i) { + a |= t(e, i, n) || t(e.target, i, n) + }, this), !a && d(this.stateList, function (t) { + (e[t] || (e[t] = {}))[n] = $P.get(n, "inRange" === t ? "active" : "inactive", o) + }) + }, this), rN.prototype.completeVisualOption.apply(this, arguments) + }, + _resetSelected: function (t, e) { + var i = this.option, n = this._pieceList, o = (e ? i : t).selected || {}; + if (i.selected = o, d(n, function (t, e) { + var i = this.getSelectedMapKey(t); + o.hasOwnProperty(i) || (o[i] = !0) + }, this), "single" === i.selectedMode) { + var a = !1; + d(n, function (t, e) { + var i = this.getSelectedMapKey(t); + o[i] && (a ? o[i] = !1 : a = !0) + }, this) + } + }, + getSelectedMapKey: function (t) { + return "categories" === this._mode ? t.value + "" : t.index + "" + }, + getPieceList: function () { + return this._pieceList + }, + _determineMode: function () { + var t = this.option; + return t.pieces && t.pieces.length > 0 ? "pieces" : this.option.categories ? "categories" : "splitNumber" + }, + setSelected: function (t) { + this.option.selected = i(t) + }, + getValueState: function (t) { + var e = ZA.findPieceIndex(t, this._pieceList); + return null != e && this.option.selected[this.getSelectedMapKey(this._pieceList[e])] ? "inRange" : "outOfRange" + }, + findTargetDataIndices: function (t) { + var e = []; + return this.eachTargetSeries(function (i) { + var n = [], o = i.getData(); + o.each(this.getDataDimension(o), function (e, i) { + ZA.findPieceIndex(e, this._pieceList) === t && n.push(i) + }, this), e.push({seriesId: i.id, dataIndex: n}) + }, this), e + }, + getRepresentValue: function (t) { + var e; + if (this.isCategory()) e = t.value; else if (null != t.value) e = t.value; else { + var i = t.interval || []; + e = i[0] === -1 / 0 && i[1] === 1 / 0 ? 0 : (i[0] + i[1]) / 2 + } + return e + }, + getVisualMeta: function (t) { + function e(e, a) { + var r = o.getRepresentValue({interval: e}); + a || (a = o.getValueState(r)); + var s = t(r, a); + e[0] === -1 / 0 ? n[0] = s : e[1] === 1 / 0 ? n[1] = s : i.push({value: e[0], color: s}, { + value: e[1], + color: s + }) + } + + if (!this.isCategory()) { + var i = [], n = [], o = this, a = this._pieceList.slice(); + if (a.length) { + var r = a[0].interval[0]; + r !== -1 / 0 && a.unshift({interval: [-1 / 0, r]}), (r = a[a.length - 1].interval[1]) !== 1 / 0 && a.push({interval: [r, 1 / 0]}) + } else a.push({interval: [-1 / 0, 1 / 0]}); + var s = -1 / 0; + return d(a, function (t) { + var i = t.interval; + i && (i[0] > s && e([s, i[0]], "outOfRange"), e(i.slice()), s = i[1]) + }, this), {stops: i, outerColors: n} + } + } + }), yN = { + splitNumber: function () { + var t = this.option, e = this._pieceList, i = Math.min(t.precision, 20), n = this.getExtent(), + o = t.splitNumber; + o = Math.max(parseInt(o, 10), 1), t.splitNumber = o; + for (var a = (n[1] - n[0]) / o; +a.toFixed(i) !== a && i < 5;) i++; + t.precision = i, a = +a.toFixed(i); + var r = 0; + t.minOpen && e.push({index: r++, interval: [-1 / 0, n[0]], close: [0, 0]}); + for (var s = n[0], l = r + o; r < l; s += a) { + var u = r === o - 1 ? n[1] : s + a; + e.push({index: r++, interval: [s, u], close: [1, 1]}) + } + t.maxOpen && e.push({index: r++, interval: [n[1], 1 / 0], close: [0, 0]}), Go(e), d(e, function (t) { + t.text = this.formatValueText(t.interval) + }, this) + }, categories: function () { + var t = this.option; + d(t.categories, function (t) { + this._pieceList.push({text: this.formatValueText(t, !0), value: t}) + }, this), by(t, this._pieceList) + }, pieces: function () { + var t = this.option, e = this._pieceList; + d(t.pieces, function (t, i) { + w(t) || (t = {value: t}); + var n = {text: "", index: i}; + if (null != t.label && (n.text = t.label), t.hasOwnProperty("value")) { + var o = n.value = t.value; + n.interval = [o, o], n.close = [1, 1] + } else { + for (var a = n.interval = [], r = n.close = [0, 0], s = [1, 0, 1], l = [-1 / 0, 1 / 0], u = [], h = 0; h < 2; h++) { + for (var c = [["gte", "gt", "min"], ["lte", "lt", "max"]][h], d = 0; d < 3 && null == a[h]; d++) a[h] = t[c[d]], r[h] = s[d], u[h] = 2 === d; + null == a[h] && (a[h] = l[h]) + } + u[0] && a[1] === 1 / 0 && (r[0] = 0), u[1] && a[0] === -1 / 0 && (r[1] = 0), a[0] === a[1] && r[0] && r[1] && (n.value = a[0]) + } + n.visual = ZA.retrieveVisuals(t), e.push(n) + }, this), by(t, e), Go(e), d(e, function (t) { + var e = t.close, i = [["<", "≤"][e[1]], [">", "≥"][e[0]]]; + t.text = t.text || this.formatValueText(null != t.value ? t.value : t.interval, !1, i) + }, this) + } + }; + uN.extend({ + type: "visualMap.piecewise", doRender: function () { + var t = this.group; + t.removeAll(); + var e = this.visualMapModel, i = e.get("textGap"), n = e.textStyleModel, o = n.getFont(), + a = n.getTextColor(), r = this._getItemAlign(), s = e.itemSize, l = this._getViewData(), u = l.endsText, + h = T(e.get("showLabel", !0), !u); + u && this._renderEndsText(t, u[0], s, h, r), d(l.viewPieceList, function (n) { + var l = n.piece, u = new sw; + u.onclick = m(this._onItemClick, this, l), this._enableHoverLink(u, n.indexInModelPieceList); + var c = e.getRepresentValue(l); + if (this._createItemSymbol(u, c, [0, 0, s[0], s[1]]), h) { + var d = this.visualMapModel.getValueState(c); + u.add(new fS({ + style: { + x: "right" === r ? -i : s[0] + i, + y: s[1] / 2, + text: l.text, + textVerticalAlign: "middle", + textAlign: r, + textFont: o, + textFill: a, + opacity: "outOfRange" === d ? .5 : 1 + } + })) + } + t.add(u) + }, this), u && this._renderEndsText(t, u[1], s, h, r), sM(e.get("orient"), t, e.get("itemGap")), this.renderBackground(t), this.positionGroup(t) + }, _enableHoverLink: function (t, e) { + function i(t) { + var i = this.visualMapModel; + i.option.hoverLink && this.api.dispatchAction({type: t, batch: gy(i.findTargetDataIndices(e))}) + } + + t.on("mouseover", m(i, this, "highlight")).on("mouseout", m(i, this, "downplay")) + }, _getItemAlign: function () { + var t = this.visualMapModel, e = t.option; + if ("vertical" === e.orient) return py(t, this.api, t.itemSize); + var i = e.align; + return i && "auto" !== i || (i = "left"), i + }, _renderEndsText: function (t, e, i, n, o) { + if (e) { + var a = new sw, r = this.visualMapModel.textStyleModel; + a.add(new fS({ + style: { + x: n ? "right" === o ? i[0] : 0 : i[0] / 2, + y: i[1] / 2, + textVerticalAlign: "middle", + textAlign: n ? o : "center", + text: e, + textFont: r.getFont(), + textFill: r.getTextColor() + } + })), t.add(a) + } + }, _getViewData: function () { + var t = this.visualMapModel, e = f(t.getPieceList(), function (t, e) { + return {piece: t, indexInModelPieceList: e} + }), i = t.get("text"), n = t.get("orient"), o = t.get("inverse"); + return ("horizontal" === n ? o : !o) ? e.reverse() : i && (i = i.slice().reverse()), { + viewPieceList: e, + endsText: i + } + }, _createItemSymbol: function (t, e, i) { + t.add(Tl(this.getControllerVisual(e, "symbol"), i[0], i[1], i[2], i[3], this.getControllerVisual(e, "color"))) + }, _onItemClick: function (t) { + var e = this.visualMapModel, n = e.option, o = i(n.selected), a = e.getSelectedMapKey(t); + "single" === n.selectedMode ? (o[a] = !0, d(o, function (t, e) { + o[e] = e === a + })) : o[a] = !o[a], this.api.dispatchAction({ + type: "selectDataRange", + from: this.uid, + visualMapId: this.visualMapModel.id, + selected: o + }) + } + }); + us(qP); + var xN = Fo, _N = Zo, wN = vs({ + type: "marker", dependencies: ["series", "grid", "polar", "geo"], init: function (t, e, i, n) { + this.mergeDefaultAndTheme(t, i), this.mergeOption(t, i, n.createdBySelf, !0) + }, isAnimationEnabled: function () { + if (a_.node) return !1; + var t = this.__hostSeries; + return this.getShallow("animation") && t && t.isAnimationEnabled() + }, mergeOption: function (t, e, i, n) { + var o = this.constructor, r = this.mainType + "Model"; + i || e.eachSeries(function (t) { + var i = t.get(this.mainType, !0), s = t[r]; + i && i.data ? (s ? s.mergeOption(i, e, !0) : (n && Sy(i), d(i.data, function (t) { + t instanceof Array ? (Sy(t[0]), Sy(t[1])) : Sy(t) + }), a(s = new o(i, this, e), { + mainType: this.mainType, + seriesIndex: t.seriesIndex, + name: t.name, + createdBySelf: !0 + }), s.__hostSeries = t), t[r] = s) : t[r] = null + }, this) + }, formatTooltip: function (t) { + var e = this.getData(), i = this.getRawValue(t), n = y(i) ? f(i, xN).join(", ") : xN(i), o = e.getName(t), + a = _N(this.name); + return (null != i || o) && (a += "
"), o && (a += _N(o), null != i && (a += " : ")), null != i && (a += _N(n)), a + }, getData: function () { + return this._data + }, setData: function (t) { + this._data = t + } + }); + h(wN, XM), wN.extend({ + type: "markPoint", + defaultOption: { + zlevel: 0, + z: 5, + symbol: "pin", + symbolSize: 50, + tooltip: {trigger: "item"}, + label: {show: !0, position: "inside"}, + itemStyle: {borderWidth: 2}, + emphasis: {label: {show: !0}} + } + }); + var bN = l, SN = v, MN = {min: SN(Ty, "min"), max: SN(Ty, "max"), average: SN(Ty, "average")}, IN = ys({ + type: "marker", init: function () { + this.markerGroupMap = R() + }, render: function (t, e, i) { + var n = this.markerGroupMap; + n.each(function (t) { + t.__keep = !1 + }); + var o = this.type + "Model"; + e.eachSeries(function (t) { + var n = t[o]; + n && this.renderSeries(t, n, e, i) + }, this), n.each(function (t) { + !t.__keep && this.group.remove(t.group) + }, this) + }, renderSeries: function () { + } + }); + IN.extend({ + type: "markPoint", updateTransform: function (t, e, i) { + e.eachSeries(function (t) { + var e = t.markPointModel; + e && (Ny(e.getData(), t, i), this.markerGroupMap.get(t.id).updateLayout(e)) + }, this) + }, renderSeries: function (t, e, i, n) { + var o = t.coordinateSystem, a = t.id, r = t.getData(), s = this.markerGroupMap, + l = s.get(a) || s.set(a, new Ql), u = Oy(o, t, e); + e.setData(u), Ny(e.getData(), t, n), u.each(function (t) { + var i = u.getItemModel(t), n = i.getShallow("symbolSize"); + "function" == typeof n && (n = n(e.getRawValue(t), e.getDataParams(t))), u.setItemVisual(t, { + symbolSize: n, + color: i.get("itemStyle.color") || r.getVisual("color"), + symbol: i.getShallow("symbol") + }) + }), l.updateData(u), this.group.add(l.group), u.eachItemGraphicEl(function (t) { + t.traverse(function (t) { + t.dataModel = e + }) + }), l.__keep = !0, l.group.silent = e.get("silent") || t.get("silent") + } + }), us(function (t) { + t.markPoint = t.markPoint || {} + }), wN.extend({ + type: "markLine", + defaultOption: { + zlevel: 0, + z: 5, + symbol: ["circle", "arrow"], + symbolSize: [8, 16], + precision: 2, + tooltip: {trigger: "item"}, + label: {show: !0, position: "end"}, + lineStyle: {type: "dashed"}, + emphasis: {label: {show: !0}, lineStyle: {width: 3}}, + animationEasing: "linear" + } + }); + var TN = function (t, e, o, r) { + var s = t.getData(), l = r.type; + if (!y(r) && ("min" === l || "max" === l || "average" === l || "median" === l || null != r.xAxis || null != r.yAxis)) { + var u, h; + if (null != r.yAxis || null != r.xAxis) u = null != r.yAxis ? "y" : "x", e.getAxis(u), h = T(r.yAxis, r.xAxis); else { + var c = Ay(r, s, e, t); + u = c.valueDataDim, c.valueAxis, h = Py(s, u, l) + } + var d = "x" === u ? 0 : 1, f = 1 - d, p = i(r), g = {}; + p.type = null, p.coord = [], g.coord = [], p.coord[f] = -1 / 0, g.coord[f] = 1 / 0; + var m = o.get("precision"); + m >= 0 && "number" == typeof h && (h = +h.toFixed(Math.min(m, 20))), p.coord[d] = g.coord[d] = h, r = [p, g, { + type: l, + valueIndex: r.valueIndex, + value: h + }] + } + return r = [Dy(t, r[0]), Dy(t, r[1]), a({}, r[2])], r[2].type = r[2].type || "", n(r[2], r[0]), n(r[2], r[1]), r + }; + IN.extend({ + type: "markLine", updateTransform: function (t, e, i) { + e.eachSeries(function (t) { + var e = t.markLineModel; + if (e) { + var n = e.getData(), o = e.__from, a = e.__to; + o.each(function (e) { + By(o, e, !0, t, i), By(a, e, !1, t, i) + }), n.each(function (t) { + n.setItemLayout(t, [o.getItemLayout(t), a.getItemLayout(t)]) + }), this.markerGroupMap.get(t.id).updateLayout() + } + }, this) + }, renderSeries: function (t, e, i, n) { + function o(e, i, o) { + var a = e.getItemModel(i); + By(e, i, o, t, n), e.setItemVisual(i, { + symbolSize: a.get("symbolSize") || g[o ? 0 : 1], + symbol: a.get("symbol", !0) || p[o ? 0 : 1], + color: a.get("itemStyle.color") || s.getVisual("color") + }) + } + + var a = t.coordinateSystem, r = t.id, s = t.getData(), l = this.markerGroupMap, + u = l.get(r) || l.set(r, new Sd); + this.group.add(u.group); + var h = Vy(a, t, e), c = h.from, d = h.to, f = h.line; + e.__from = c, e.__to = d, e.setData(f); + var p = e.get("symbol"), g = e.get("symbolSize"); + y(p) || (p = [p, p]), "number" == typeof g && (g = [g, g]), h.from.each(function (t) { + o(c, t, !0), o(d, t, !1) + }), f.each(function (t) { + var e = f.getItemModel(t).get("lineStyle.color"); + f.setItemVisual(t, {color: e || c.getItemVisual(t, "color")}), f.setItemLayout(t, [c.getItemLayout(t), d.getItemLayout(t)]), f.setItemVisual(t, { + fromSymbolSize: c.getItemVisual(t, "symbolSize"), + fromSymbol: c.getItemVisual(t, "symbol"), + toSymbolSize: d.getItemVisual(t, "symbolSize"), + toSymbol: d.getItemVisual(t, "symbol") + }) + }), u.updateData(f), h.line.eachItemGraphicEl(function (t, i) { + t.traverse(function (t) { + t.dataModel = e + }) + }), u.__keep = !0, u.group.silent = e.get("silent") || t.get("silent") + } + }), us(function (t) { + t.markLine = t.markLine || {} + }), wN.extend({ + type: "markArea", + defaultOption: { + zlevel: 0, + z: 1, + tooltip: {trigger: "item"}, + animation: !1, + label: {show: !0, position: "top"}, + itemStyle: {borderWidth: 0}, + emphasis: {label: {show: !0, position: "top"}} + } + }); + var DN = function (t, e, i, n) { + var a = Dy(t, n[0]), r = Dy(t, n[1]), s = T, l = a.coord, u = r.coord; + l[0] = s(l[0], -1 / 0), l[1] = s(l[1], -1 / 0), u[0] = s(u[0], 1 / 0), u[1] = s(u[1], 1 / 0); + var h = o([{}, a, r]); + return h.coord = [a.coord, r.coord], h.x0 = a.x, h.y0 = a.y, h.x1 = r.x, h.y1 = r.y, h + }, AN = [["x0", "y0"], ["x1", "y0"], ["x1", "y1"], ["x0", "y1"]]; + IN.extend({ + type: "markArea", updateTransform: function (t, e, i) { + e.eachSeries(function (t) { + var e = t.markAreaModel; + if (e) { + var n = e.getData(); + n.each(function (e) { + var o = f(AN, function (o) { + return Hy(n, e, o, t, i) + }); + n.setItemLayout(e, o), n.getItemGraphicEl(e).setShape("points", o) + }) + } + }, this) + }, renderSeries: function (t, e, i, n) { + var o = t.coordinateSystem, a = t.id, s = t.getData(), l = this.markerGroupMap, + u = l.get(a) || l.set(a, {group: new sw}); + this.group.add(u.group), u.__keep = !0; + var h = Zy(o, t, e); + e.setData(h), h.each(function (e) { + h.setItemLayout(e, f(AN, function (i) { + return Hy(h, e, i, t, n) + })), h.setItemVisual(e, {color: s.getVisual("color")}) + }), h.diff(u.__data).add(function (t) { + var e = new wS({shape: {points: h.getItemLayout(t)}}); + h.setItemGraphicEl(t, e), u.group.add(e) + }).update(function (t, i) { + var n = u.__data.getItemGraphicEl(i); + fo(n, {shape: {points: h.getItemLayout(t)}}, e, t), u.group.add(n), h.setItemGraphicEl(t, n) + }).remove(function (t) { + var e = u.__data.getItemGraphicEl(t); + u.group.remove(e) + }).execute(), h.eachItemGraphicEl(function (t, i) { + var n = h.getItemModel(i), o = n.getModel("label"), a = n.getModel("emphasis.label"), + s = h.getItemVisual(i, "color"); + t.useStyle(r(n.getModel("itemStyle").getItemStyle(), { + fill: Rt(s, .4), + stroke: s + })), t.hoverStyle = n.getModel("emphasis.itemStyle").getItemStyle(), io(t.style, t.hoverStyle, o, a, { + labelFetcher: e, + labelDataIndex: i, + defaultText: h.getName(i) || "", + isRectText: !0, + autoColor: s + }), eo(t, {}), t.dataModel = e + }), u.__data = h, u.group.silent = e.get("silent") || t.get("silent") + } + }), us(function (t) { + t.markArea = t.markArea || {} + }); + hM.registerSubTypeDefaulter("timeline", function () { + return "slider" + }), cs({type: "timelineChange", event: "timelineChanged", update: "prepareAndUpdate"}, function (t, e) { + var i = e.getComponent("timeline"); + return i && null != t.currentIndex && (i.setCurrentIndex(t.currentIndex), !i.get("loop", !0) && i.isIndexMax() && i.setPlayState(!1)), e.resetOption("timeline"), r({currentIndex: i.option.currentIndex}, t) + }), cs({type: "timelinePlayChange", event: "timelinePlayChanged", update: "update"}, function (t, e) { + var i = e.getComponent("timeline"); + i && null != t.playState && i.setPlayState(t.playState) + }); + var CN = hM.extend({ + type: "timeline", + layoutMode: "box", + defaultOption: { + zlevel: 0, + z: 4, + show: !0, + axisType: "time", + realtime: !0, + left: "20%", + top: null, + right: "20%", + bottom: 0, + width: null, + height: 40, + padding: 5, + controlPosition: "left", + autoPlay: !1, + rewind: !1, + loop: !0, + playInterval: 2e3, + currentIndex: 0, + itemStyle: {}, + label: {color: "#000"}, + data: [] + }, + init: function (t, e, i) { + this._data, this._names, this.mergeDefaultAndTheme(t, i), this._initData() + }, + mergeOption: function (t) { + CN.superApply(this, "mergeOption", arguments), this._initData() + }, + setCurrentIndex: function (t) { + null == t && (t = this.option.currentIndex); + var e = this._data.count(); + this.option.loop ? t = (t % e + e) % e : (t >= e && (t = e - 1), t < 0 && (t = 0)), this.option.currentIndex = t + }, + getCurrentIndex: function () { + return this.option.currentIndex + }, + isIndexMax: function () { + return this.getCurrentIndex() >= this._data.count() - 1 + }, + setPlayState: function (t) { + this.option.autoPlay = !!t + }, + getPlayState: function () { + return !!this.option.autoPlay + }, + _initData: function () { + var t = this.option, e = t.data || [], n = t.axisType, o = this._names = []; + if ("category" === n) { + var a = []; + d(e, function (t, e) { + var n, r = Ii(t); + w(t) ? (n = i(t)).value = e : n = e, a.push(n), _(r) || null != r && !isNaN(r) || (r = ""), o.push(r + "") + }), e = a + } + var r = {category: "ordinal", time: "time"}[n] || "number"; + (this._data = new sT([{name: "value", type: r}], this)).initData(e, o) + }, + getData: function () { + return this._data + }, + getCategories: function () { + if ("category" === this.get("axisType")) return this._names.slice() + } + }); + h(CN.extend({ + type: "timeline.slider", defaultOption: { + backgroundColor: "rgba(0,0,0,0)", + borderColor: "#ccc", + borderWidth: 0, + orient: "horizontal", + inverse: !1, + tooltip: {trigger: "item"}, + symbol: "emptyCircle", + symbolSize: 10, + lineStyle: {show: !0, width: 2, color: "#304654"}, + label: {position: "auto", show: !0, interval: "auto", rotate: 0, color: "#304654"}, + itemStyle: {color: "#304654", borderWidth: 1}, + checkpointStyle: { + symbol: "circle", + symbolSize: 13, + color: "#c23531", + borderWidth: 5, + borderColor: "rgba(194,53,49, 0.5)", + animation: !0, + animationDuration: 300, + animationEasing: "quinticInOut" + }, + controlStyle: { + show: !0, + showPlayBtn: !0, + showPrevBtn: !0, + showNextBtn: !0, + itemSize: 22, + itemGap: 12, + position: "left", + playIcon: "path://M31.6,53C17.5,53,6,41.5,6,27.4S17.5,1.8,31.6,1.8C45.7,1.8,57.2,13.3,57.2,27.4S45.7,53,31.6,53z M31.6,3.3 C18.4,3.3,7.5,14.1,7.5,27.4c0,13.3,10.8,24.1,24.1,24.1C44.9,51.5,55.7,40.7,55.7,27.4C55.7,14.1,44.9,3.3,31.6,3.3z M24.9,21.3 c0-2.2,1.6-3.1,3.5-2l10.5,6.1c1.899,1.1,1.899,2.9,0,4l-10.5,6.1c-1.9,1.1-3.5,0.2-3.5-2V21.3z", + stopIcon: "path://M30.9,53.2C16.8,53.2,5.3,41.7,5.3,27.6S16.8,2,30.9,2C45,2,56.4,13.5,56.4,27.6S45,53.2,30.9,53.2z M30.9,3.5C17.6,3.5,6.8,14.4,6.8,27.6c0,13.3,10.8,24.1,24.101,24.1C44.2,51.7,55,40.9,55,27.6C54.9,14.4,44.1,3.5,30.9,3.5z M36.9,35.8c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H36c0.5,0,0.9,0.4,0.9,1V35.8z M27.8,35.8 c0,0.601-0.4,1-0.9,1h-1.3c-0.5,0-0.9-0.399-0.9-1V19.5c0-0.6,0.4-1,0.9-1H27c0.5,0,0.9,0.4,0.9,1L27.8,35.8L27.8,35.8z", + nextIcon: "path://M18.6,50.8l22.5-22.5c0.2-0.2,0.3-0.4,0.3-0.7c0-0.3-0.1-0.5-0.3-0.7L18.7,4.4c-0.1-0.1-0.2-0.3-0.2-0.5 c0-0.4,0.3-0.8,0.8-0.8c0.2,0,0.5,0.1,0.6,0.3l23.5,23.5l0,0c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-0.1,0.1L19.7,52 c-0.1,0.1-0.3,0.2-0.5,0.2c-0.4,0-0.8-0.3-0.8-0.8C18.4,51.2,18.5,51,18.6,50.8z", + prevIcon: "path://M43,52.8L20.4,30.3c-0.2-0.2-0.3-0.4-0.3-0.7c0-0.3,0.1-0.5,0.3-0.7L42.9,6.4c0.1-0.1,0.2-0.3,0.2-0.5 c0-0.4-0.3-0.8-0.8-0.8c-0.2,0-0.5,0.1-0.6,0.3L18.3,28.8l0,0c-0.2,0.2-0.3,0.4-0.3,0.7c0,0.3,0.1,0.5,0.3,0.7l0.1,0.1L41.9,54 c0.1,0.1,0.3,0.2,0.5,0.2c0.4,0,0.8-0.3,0.8-0.8C43.2,53.2,43.1,53,43,52.8z", + color: "#304654", + borderColor: "#304654", + borderWidth: 1 + }, + emphasis: { + label: {show: !0, color: "#c23531"}, + itemStyle: {color: "#c23531"}, + controlStyle: {color: "#c23531", borderColor: "#c23531", borderWidth: 2} + }, + data: [] + } + }), XM); + var LN = $M.extend({type: "timeline"}), kN = function (t, e, i, n) { + qT.call(this, t, e, i), this.type = n || "value", this.model = null + }; + kN.prototype = { + constructor: kN, getLabelModel: function () { + return this.model.getModel("label") + }, isHorizontal: function () { + return "horizontal" === this.model.get("orient") + } + }, u(kN, qT); + var PN = m, NN = d, ON = Math.PI; + LN.extend({ + type: "timeline.slider", init: function (t, e) { + this.api = e, this._axis, this._viewRect, this._timer, this._currentPointer, this._mainGroup, this._labelGroup + }, render: function (t, e, i, n) { + if (this.model = t, this.api = i, this.ecModel = e, this.group.removeAll(), t.get("show", !0)) { + var o = this._layout(t, i), a = this._createGroup("mainGroup"), r = this._createGroup("labelGroup"), + s = this._axis = this._createAxis(o, t); + t.formatTooltip = function (t) { + return Zo(s.scale.getLabel(t)) + }, NN(["AxisLine", "AxisTick", "Control", "CurrentPointer"], function (e) { + this["_render" + e](o, a, s, t) + }, this), this._renderAxisLabel(o, r, s, t), this._position(o, t) + } + this._doPlayStop() + }, remove: function () { + this._clearTimer(), this.group.removeAll() + }, dispose: function () { + this._clearTimer() + }, _layout: function (t, e) { + var i = t.get("label.position"), n = t.get("orient"), o = Yy(t, e); + null == i || "auto" === i ? i = "horizontal" === n ? o.y + o.height / 2 < e.getHeight() / 2 ? "-" : "+" : o.x + o.width / 2 < e.getWidth() / 2 ? "+" : "-" : isNaN(i) && (i = { + horizontal: { + top: "-", + bottom: "+" + }, vertical: {left: "-", right: "+"} + }[n][i]); + var a = {horizontal: "center", vertical: i >= 0 || "+" === i ? "left" : "right"}, + r = {horizontal: i >= 0 || "+" === i ? "top" : "bottom", vertical: "middle"}, + s = {horizontal: 0, vertical: ON / 2}, l = "vertical" === n ? o.height : o.width, + u = t.getModel("controlStyle"), h = u.get("show", !0), c = h ? u.get("itemSize") : 0, + d = h ? u.get("itemGap") : 0, f = c + d, p = t.get("label.rotate") || 0; + p = p * ON / 180; + var g, m, v, y, x = u.get("position", !0), _ = h && u.get("showPlayBtn", !0), + w = h && u.get("showPrevBtn", !0), b = h && u.get("showNextBtn", !0), S = 0, M = l; + return "left" === x || "bottom" === x ? (_ && (g = [0, 0], S += f), w && (m = [S, 0], S += f), b && (v = [M - c, 0], M -= f)) : (_ && (g = [M - c, 0], M -= f), w && (m = [0, 0], S += f), b && (v = [M - c, 0], M -= f)), y = [S, M], t.get("inverse") && y.reverse(), { + viewRect: o, + mainLength: l, + orient: n, + rotation: s[n], + labelRotation: p, + labelPosOpt: i, + labelAlign: t.get("label.align") || a[n], + labelBaseline: t.get("label.verticalAlign") || t.get("label.baseline") || r[n], + playPosition: g, + prevBtnPosition: m, + nextBtnPosition: v, + axisExtent: y, + controlSize: c, + controlGap: d + } + }, _position: function (t, e) { + function i(t) { + var e = t.position; + t.origin = [c[0][0] - e[0], c[1][0] - e[1]] + } + + function n(t) { + return [[t.x, t.x + t.width], [t.y, t.y + t.height]] + } + + function o(t, e, i, n, o) { + t[n] += i[n][o] - e[n][o] + } + + var a = this._mainGroup, r = this._labelGroup, s = t.viewRect; + if ("vertical" === t.orient) { + var l = st(), u = s.x, h = s.y + s.height; + ct(l, l, [-u, -h]), dt(l, l, -ON / 2), ct(l, l, [u, h]), (s = s.clone()).applyTransform(l) + } + var c = n(s), d = n(a.getBoundingRect()), f = n(r.getBoundingRect()), p = a.position, g = r.position; + g[0] = p[0] = c[0][0]; + var m = t.labelPosOpt; + if (isNaN(m)) o(p, d, c, 1, v = "+" === m ? 0 : 1), o(g, f, c, 1, 1 - v); else { + var v = m >= 0 ? 0 : 1; + o(p, d, c, 1, v), g[1] = p[1] + m + } + a.attr("position", p), r.attr("position", g), a.rotation = r.rotation = t.rotation, i(a), i(r) + }, _createAxis: function (t, e) { + var i = e.getData(), n = e.get("axisType"), o = xl(e, n); + o.getTicks = function () { + return i.mapArray(["value"], function (t) { + return t + }) + }; + var a = i.getDataExtent("value"); + o.setExtent(a[0], a[1]), o.niceTicks(); + var r = new kN("value", o, t.axisExtent, n); + return r.model = e, r + }, _createGroup: function (t) { + var e = this["_" + t] = new sw; + return this.group.add(e), e + }, _renderAxisLine: function (t, e, i, n) { + var o = i.getExtent(); + n.get("lineStyle.show") && e.add(new MS({ + shape: {x1: o[0], y1: 0, x2: o[1], y2: 0}, + style: a({lineCap: "round"}, n.getModel("lineStyle").getLineStyle()), + silent: !0, + z2: 1 + })) + }, _renderAxisTick: function (t, e, i, n) { + var o = n.getData(), a = i.scale.getTicks(); + NN(a, function (t) { + var a = i.dataToCoord(t), r = o.getItemModel(t), s = r.getModel("itemStyle"), + l = r.getModel("emphasis.itemStyle"), + u = {position: [a, 0], onclick: PN(this._changeTimeline, this, t)}, h = Ky(r, s, e, u); + eo(h, l.getItemStyle()), r.get("tooltip") ? (h.dataIndex = t, h.dataModel = n) : h.dataIndex = h.dataModel = null + }, this) + }, _renderAxisLabel: function (t, e, i, n) { + if (i.getLabelModel().get("show")) { + var o = n.getData(), a = i.getViewLabels(); + NN(a, function (n) { + var a = n.tickValue, r = o.getItemModel(a), s = r.getModel("label"), + l = r.getModel("emphasis.label"), u = i.dataToCoord(n.tickValue), h = new fS({ + position: [u, 0], + rotation: t.labelRotation - t.rotation, + onclick: PN(this._changeTimeline, this, a), + silent: !1 + }); + no(h.style, s, { + text: n.formattedLabel, + textAlign: t.labelAlign, + textVerticalAlign: t.labelBaseline + }), e.add(h), eo(h, no({}, l)) + }, this) + } + }, _renderControl: function (t, e, i, n) { + function o(t, i, o, h) { + if (t) { + var c = qy(n, i, u, { + position: t, + origin: [a / 2, 0], + rotation: h ? -r : 0, + rectHover: !0, + style: s, + onclick: o + }); + e.add(c), eo(c, l) + } + } + + var a = t.controlSize, r = t.rotation, s = n.getModel("controlStyle").getItemStyle(), + l = n.getModel("emphasis.controlStyle").getItemStyle(), u = [0, -a / 2, a, a], h = n.getPlayState(), + c = n.get("inverse", !0); + o(t.nextBtnPosition, "controlStyle.nextIcon", PN(this._changeTimeline, this, c ? "-" : "+")), o(t.prevBtnPosition, "controlStyle.prevIcon", PN(this._changeTimeline, this, c ? "+" : "-")), o(t.playPosition, "controlStyle." + (h ? "stopIcon" : "playIcon"), PN(this._handlePlayClick, this, !h), !0) + }, _renderCurrentPointer: function (t, e, i, n) { + var o = n.getData(), a = n.getCurrentIndex(), r = o.getItemModel(a).getModel("checkpointStyle"), s = this, + l = { + onCreate: function (t) { + t.draggable = !0, t.drift = PN(s._handlePointerDrag, s), t.ondragend = PN(s._handlePointerDragend, s), $y(t, a, i, n, !0) + }, onUpdate: function (t) { + $y(t, a, i, n) + } + }; + this._currentPointer = Ky(r, r, this._mainGroup, {}, this._currentPointer, l) + }, _handlePlayClick: function (t) { + this._clearTimer(), this.api.dispatchAction({type: "timelinePlayChange", playState: t, from: this.uid}) + }, _handlePointerDrag: function (t, e, i) { + this._clearTimer(), this._pointerChangeTimeline([i.offsetX, i.offsetY]) + }, _handlePointerDragend: function (t) { + this._pointerChangeTimeline([t.offsetX, t.offsetY], !0) + }, _pointerChangeTimeline: function (t, e) { + var i = this._toAxisCoord(t)[0], n = Co(this._axis.getExtent().slice()); + i > n[1] && (i = n[1]), i < n[0] && (i = n[0]), this._currentPointer.position[0] = i, this._currentPointer.dirty(); + var o = this._findNearestTick(i), a = this.model; + (e || o !== a.getCurrentIndex() && a.get("realtime")) && this._changeTimeline(o) + }, _doPlayStop: function () { + this._clearTimer(), this.model.getPlayState() && (this._timer = setTimeout(PN(function () { + var t = this.model; + this._changeTimeline(t.getCurrentIndex() + (t.get("rewind", !0) ? -1 : 1)) + }, this), this.model.get("playInterval"))) + }, _toAxisCoord: function (t) { + return mo(t, this._mainGroup.getLocalTransform(), !0) + }, _findNearestTick: function (t) { + var e, i = this.model.getData(), n = 1 / 0, o = this._axis; + return i.each(["value"], function (i, a) { + var r = o.dataToCoord(i), s = Math.abs(r - t); + s < n && (n = s, e = a) + }), e + }, _clearTimer: function () { + this._timer && (clearTimeout(this._timer), this._timer = null) + }, _changeTimeline: function (t) { + var e = this.model.getCurrentIndex(); + "+" === t ? t = e + 1 : "-" === t && (t = e - 1), this.api.dispatchAction({ + type: "timelineChange", + currentIndex: t, + from: this.uid + }) + } + }), us(function (t) { + var e = t && t.timeline; + y(e) || (e = e ? [e] : []), d(e, function (t) { + t && Uy(t) + }) + }); + var EN = vs({ + type: "toolbox", + layoutMode: {type: "box", ignoreSize: !0}, + optionUpdated: function () { + EN.superApply(this, "optionUpdated", arguments), d(this.option.feature, function (t, e) { + var i = Vv(e); + i && n(t, i.defaultOption) + }) + }, + defaultOption: { + show: !0, + z: 6, + zlevel: 0, + orient: "horizontal", + left: "right", + top: "top", + backgroundColor: "transparent", + borderColor: "#ccc", + borderRadius: 0, + borderWidth: 0, + padding: 5, + itemSize: 15, + itemGap: 8, + showTitle: !0, + iconStyle: {borderColor: "#666", color: "none"}, + emphasis: {iconStyle: {borderColor: "#3E98C5"}} + } + }); + ys({ + type: "toolbox", render: function (t, e, i, n) { + function o(o, r) { + var s, c = h[o], d = h[r], f = new wo(l[c], t, t.ecModel); + if (c && !d) { + if (Jy(c)) s = {model: f, onclick: f.option.onclick, featureName: c}; else { + var p = Vv(c); + if (!p) return; + s = new p(f, e, i) + } + u[c] = s + } else { + if (!(s = u[d])) return; + s.model = f, s.ecModel = e, s.api = i + } + c || !d ? f.get("show") && !s.unusable ? (a(f, s, c), f.setIconStatus = function (t, e) { + var i = this.option, n = this.iconPaths; + i.iconStatus = i.iconStatus || {}, i.iconStatus[t] = e, n[t] && n[t].trigger(e) + }, s.render && s.render(f, e, i, n)) : s.remove && s.remove(e, i) : s.dispose && s.dispose(e, i) + } + + function a(n, o, a) { + var l = n.getModel("iconStyle"), u = n.getModel("emphasis.iconStyle"), + h = o.getIcons ? o.getIcons() : n.get("icon"), c = n.get("title") || {}; + if ("string" == typeof h) { + var f = h, p = c; + c = {}, (h = {})[a] = f, c[a] = p + } + var g = n.iconPaths = {}; + d(h, function (a, h) { + var d = _o(a, {}, {x: -s / 2, y: -s / 2, width: s, height: s}); + d.setStyle(l.getItemStyle()), d.hoverStyle = u.getItemStyle(), eo(d), t.get("showTitle") && (d.__title = c[h], d.on("mouseover", function () { + var t = u.getItemStyle(); + d.setStyle({ + text: c[h], + textPosition: t.textPosition || "bottom", + textFill: t.fill || t.stroke || "#000", + textAlign: t.textAlign || "center" + }) + }).on("mouseout", function () { + d.setStyle({textFill: null}) + })), d.trigger(n.get("iconStatus." + h) || "normal"), r.add(d), d.on("click", m(o.onclick, o, e, i, h)), g[h] = d + }) + } + + var r = this.group; + if (r.removeAll(), t.get("show")) { + var s = +t.get("itemSize"), l = t.get("feature") || {}, u = this._features || (this._features = {}), + h = []; + d(l, function (t, e) { + h.push(e) + }), new Ss(this._featureNames || [], h).add(o).update(o).remove(v(o, null)).execute(), this._featureNames = h, Pm(r, t, i), r.add(Nm(r.getBoundingRect(), t)), r.eachChild(function (t) { + var e = t.__title, n = t.hoverStyle; + if (n && e) { + var o = me(e, ke(n)), a = t.position[0] + r.position[0], l = !1; + t.position[1] + r.position[1] + s + o.height > i.getHeight() && (n.textPosition = "top", l = !0); + var u = l ? -5 - o.height : s + 8; + a + o.width / 2 > i.getWidth() ? (n.textPosition = ["100%", u], n.textAlign = "right") : a - o.width / 2 < 0 && (n.textPosition = [0, u], n.textAlign = "left") + } + }) + } + }, updateView: function (t, e, i, n) { + d(this._features, function (t) { + t.updateView && t.updateView(t.model, e, i, n) + }) + }, remove: function (t, e) { + d(this._features, function (i) { + i.remove && i.remove(t, e) + }), this.group.removeAll() + }, dispose: function (t, e) { + d(this._features, function (i) { + i.dispose && i.dispose(t, e) + }) + } + }); + var RN = lI.toolbox.saveAsImage; + Qy.defaultOption = { + show: !0, + icon: "M4.7,22.9L29.3,45.5L54.7,23.4M4.6,43.6L4.6,58L53.8,58L53.8,43.6M29.2,45.1L29.2,0", + title: RN.title, + type: "png", + name: "", + excludeComponents: ["toolbox"], + pixelRatio: 1, + lang: RN.lang.slice() + }, Qy.prototype.unusable = !a_.canvasSupported, Qy.prototype.onclick = function (t, e) { + var i = this.model, n = i.get("name") || t.get("title.0.text") || "echarts", o = document.createElement("a"), + a = i.get("type", !0) || "png"; + o.download = n + "." + a, o.target = "_blank"; + var r = e.getConnectedDataURL({ + type: a, + backgroundColor: i.get("backgroundColor", !0) || t.get("backgroundColor") || "#fff", + excludeComponents: i.get("excludeComponents"), + pixelRatio: i.get("pixelRatio") + }); + if (o.href = r, "function" != typeof MouseEvent || a_.browser.ie || a_.browser.edge) if (window.navigator.msSaveOrOpenBlob) { + for (var s = atob(r.split(",")[1]), l = s.length, u = new Uint8Array(l); l--;) u[l] = s.charCodeAt(l); + var h = new Blob([u]); + window.navigator.msSaveOrOpenBlob(h, n + "." + a) + } else { + var c = i.get("lang"), + d = ''; + window.open().document.write(d) + } else { + var f = new MouseEvent("click", {view: window, bubbles: !0, cancelable: !1}); + o.dispatchEvent(f) + } + }, Bv("saveAsImage", Qy); + var zN = lI.toolbox.magicType; + tx.defaultOption = { + show: !0, + type: [], + icon: { + line: "M4.1,28.9h7.1l9.3-22l7.4,38l9.7-19.7l3,12.8h14.9M4.1,58h51.4", + bar: "M6.7,22.9h10V48h-10V22.9zM24.9,13h10v35h-10V13zM43.2,2h10v46h-10V2zM3.1,58h53.7", + stack: "M8.2,38.4l-8.4,4.1l30.6,15.3L60,42.5l-8.1-4.1l-21.5,11L8.2,38.4z M51.9,30l-8.1,4.2l-13.4,6.9l-13.9-6.9L8.2,30l-8.4,4.2l8.4,4.2l22.2,11l21.5-11l8.1-4.2L51.9,30z M51.9,21.7l-8.1,4.2L35.7,30l-5.3,2.8L24.9,30l-8.4-4.1l-8.3-4.2l-8.4,4.2L8.2,30l8.3,4.2l13.9,6.9l13.4-6.9l8.1-4.2l8.1-4.1L51.9,21.7zM30.4,2.2L-0.2,17.5l8.4,4.1l8.3,4.2l8.4,4.2l5.5,2.7l5.3-2.7l8.1-4.2l8.1-4.2l8.1-4.1L30.4,2.2z", + tiled: "M2.3,2.2h22.8V25H2.3V2.2z M35,2.2h22.8V25H35V2.2zM2.3,35h22.8v22.8H2.3V35z M35,35h22.8v22.8H35V35z" + }, + title: i(zN.title), + option: {}, + seriesIndex: {} + }; + var BN = tx.prototype; + BN.getIcons = function () { + var t = this.model, e = t.get("icon"), i = {}; + return d(t.get("type"), function (t) { + e[t] && (i[t] = e[t]) + }), i + }; + var VN = { + line: function (t, e, i, o) { + if ("bar" === t) return n({ + id: e, + type: "line", + data: i.get("data"), + stack: i.get("stack"), + markPoint: i.get("markPoint"), + markLine: i.get("markLine") + }, o.get("option.line") || {}, !0) + }, bar: function (t, e, i, o) { + if ("line" === t) return n({ + id: e, + type: "bar", + data: i.get("data"), + stack: i.get("stack"), + markPoint: i.get("markPoint"), + markLine: i.get("markLine") + }, o.get("option.bar") || {}, !0) + }, stack: function (t, e, i, o) { + if ("line" === t || "bar" === t) return n({ + id: e, + stack: "__ec_magicType_stack__" + }, o.get("option.stack") || {}, !0) + }, tiled: function (t, e, i, o) { + if ("line" === t || "bar" === t) return n({id: e, stack: ""}, o.get("option.tiled") || {}, !0) + } + }, GN = [["line", "bar"], ["stack", "tiled"]]; + BN.onclick = function (t, e, i) { + var n = this.model, o = n.get("seriesIndex." + i); + if (VN[i]) { + var a = {series: []}; + d(GN, function (t) { + l(t, i) >= 0 && d(t, function (t) { + n.setIconStatus(t, "normal") + }) + }), n.setIconStatus(i, "emphasis"), t.eachComponent({ + mainType: "series", + query: null == o ? null : {seriesIndex: o} + }, function (e) { + var o = e.subType, s = e.id, l = VN[i](o, s, e, n); + l && (r(l, e.option), a.series.push(l)); + var u = e.coordinateSystem; + if (u && "cartesian2d" === u.type && ("line" === i || "bar" === i)) { + var h = u.getAxesByScale("ordinal")[0]; + if (h) { + var c = h.dim + "Axis", d = t.queryComponents({ + mainType: c, + index: e.get(name + "Index"), + id: e.get(name + "Id") + })[0].componentIndex; + a[c] = a[c] || []; + for (var f = 0; f <= d; f++) a[c][d] = a[c][d] || {}; + a[c][d].boundaryGap = "bar" === i + } + } + }), e.dispatchAction({type: "changeMagicType", currentType: i, newOption: a}) + } + }, cs({type: "changeMagicType", event: "magicTypeChanged", update: "prepareAndUpdate"}, function (t, e) { + e.mergeOption(t.newOption) + }), Bv("magicType", tx); + var WN = lI.toolbox.dataView, FN = new Array(60).join("-"), HN = "\t", ZN = new RegExp("[" + HN + "]+", "g"); + hx.defaultOption = { + show: !0, + readOnly: !1, + optionToContent: null, + contentToOption: null, + icon: "M17.5,17.3H33 M17.5,17.3H33 M45.4,29.5h-28 M11.5,2v56H51V14.8L38.4,2H11.5z M38.4,2.2v12.7H51 M45.4,41.7h-28", + title: i(WN.title), + lang: i(WN.lang), + backgroundColor: "#fff", + textColor: "#000", + textareaColor: "#fff", + textareaBorderColor: "#333", + buttonColor: "#c23531", + buttonTextColor: "#fff" + }, hx.prototype.onclick = function (t, e) { + function i() { + n.removeChild(a), x._dom = null + } + + var n = e.getDom(), o = this.model; + this._dom && n.removeChild(this._dom); + var a = document.createElement("div"); + a.style.cssText = "position:absolute;left:5px;top:5px;bottom:5px;right:5px;", a.style.backgroundColor = o.get("backgroundColor") || "#fff"; + var r = document.createElement("h4"), s = o.get("lang") || []; + r.innerHTML = s[0] || o.get("title"), r.style.cssText = "margin: 10px 20px;", r.style.color = o.get("textColor"); + var l = document.createElement("div"), u = document.createElement("textarea"); + l.style.cssText = "display:block;width:100%;overflow:auto;"; + var h = o.get("optionToContent"), c = o.get("contentToOption"), d = ox(t); + if ("function" == typeof h) { + var f = h(e.getOption()); + "string" == typeof f ? l.innerHTML = f : M(f) && l.appendChild(f) + } else l.appendChild(u), u.readOnly = o.get("readOnly"), u.style.cssText = "width:100%;height:100%;font-family:monospace;font-size:14px;line-height:1.6rem;", u.style.color = o.get("textColor"), u.style.borderColor = o.get("textareaBorderColor"), u.style.backgroundColor = o.get("textareaColor"), u.value = d.value; + var p = d.meta, g = document.createElement("div"); + g.style.cssText = "position:absolute;bottom:0;left:0;right:0;"; + var m = "float:right;margin-right:20px;border:none;cursor:pointer;padding:2px 5px;font-size:12px;border-radius:3px", + v = document.createElement("div"), y = document.createElement("div"); + m += ";background-color:" + o.get("buttonColor"), m += ";color:" + o.get("buttonTextColor"); + var x = this; + ui(v, "click", i), ui(y, "click", function () { + var t; + try { + t = "function" == typeof c ? c(l, e.getOption()) : ux(u.value, p) + } catch (t) { + throw i(), new Error("Data view format error " + t) + } + t && e.dispatchAction({type: "changeDataView", newOption: t}), i() + }), v.innerHTML = s[1], y.innerHTML = s[2], y.style.cssText = m, v.style.cssText = m, !o.get("readOnly") && g.appendChild(y), g.appendChild(v), ui(u, "keydown", function (t) { + if (9 === (t.keyCode || t.which)) { + var e = this.value, i = this.selectionStart, n = this.selectionEnd; + this.value = e.substring(0, i) + HN + e.substring(n), this.selectionStart = this.selectionEnd = i + 1, zw(t) + } + }), a.appendChild(r), a.appendChild(l), a.appendChild(g), l.style.height = n.clientHeight - 80 + "px", n.appendChild(a), this._dom = a + }, hx.prototype.remove = function (t, e) { + this._dom && e.getDom().removeChild(this._dom) + }, hx.prototype.dispose = function (t, e) { + this.remove(t, e) + }, Bv("dataView", hx), cs({ + type: "changeDataView", + event: "dataViewChanged", + update: "prepareAndUpdate" + }, function (t, e) { + var i = []; + d(t.newOption.series, function (t) { + var n = e.getSeriesByName(t.name)[0]; + if (n) { + var o = n.get("data"); + i.push({name: t.name, data: cx(t.data, o)}) + } else i.push(a({type: "scatter"}, t)) + }), e.mergeOption(r({series: i}, t.newOption)) + }); + var UN = d, XN = "\0_ec_hist_store"; + PP.extend({type: "dataZoom.select"}), NP.extend({type: "dataZoom.select"}); + var jN = lI.toolbox.dataZoom, YN = d, qN = "\0_ec_\0toolbox-dataZoom_"; + vx.defaultOption = { + show: !0, + icon: { + zoom: "M0,13.5h26.9 M13.5,26.9V0 M32.1,13.5H58V58H13.5 V32.1", + back: "M22,1.4L9.9,13.5l12.3,12.3 M10.3,13.5H54.9v44.6 H10.3v-26" + }, + title: i(jN.title) + }; + var KN = vx.prototype; + KN.render = function (t, e, i, n) { + this.model = t, this.ecModel = e, this.api = i, _x(t, e, this, n, i), xx(t, e) + }, KN.onclick = function (t, e, i) { + $N[i].call(this) + }, KN.remove = function (t, e) { + this._brushController.unmount() + }, KN.dispose = function (t, e) { + this._brushController.dispose() + }; + var $N = { + zoom: function () { + var t = !this._isZoomActive; + this.api.dispatchAction({type: "takeGlobalCursor", key: "dataZoomSelect", dataZoomSelectActive: t}) + }, back: function () { + this._dispatchZoomAction(fx(this.ecModel)) + } + }; + KN._onBrush = function (t, e) { + function i(t, e, i) { + var r = e.getAxis(t), s = r.model, l = n(t, s, a), u = l.findRepresentativeAxisProxy(s).getMinMaxSpan(); + null == u.minValueSpan && null == u.maxValueSpan || (i = PC(0, i.slice(), r.scale.getExtent(), 0, u.minValueSpan, u.maxValueSpan)), l && (o[l.id] = { + dataZoomId: l.id, + startValue: i[0], + endValue: i[1] + }) + } + + function n(t, e, i) { + var n; + return i.eachComponent({mainType: "dataZoom", subType: "select"}, function (i) { + i.getAxisModel(t, e.componentIndex) && (n = i) + }), n + } + + if (e.isEnd && t.length) { + var o = {}, a = this.ecModel; + this._brushController.updateCovers([]), new bv(yx(this.model.option), a, {include: ["grid"]}).matchOutputRanges(t, a, function (t, e, n) { + if ("cartesian2d" === n.type) { + var o = t.brushType; + "rect" === o ? (i("x", n, e[0]), i("y", n, e[1])) : i({lineX: "x", lineY: "y"}[o], n, e) + } + }), dx(a, o), this._dispatchZoomAction(o) + } + }, KN._dispatchZoomAction = function (t) { + var e = []; + YN(t, function (t, n) { + e.push(i(t)) + }), e.length && this.api.dispatchAction({type: "dataZoom", from: this.uid, batch: e}) + }, Bv("dataZoom", vx), us(function (t) { + function e(t, e) { + if (e) { + var o = t + "Index", a = e[o]; + null == a || "all" == a || y(a) || (a = !1 === a || "none" === a ? [] : [a]), i(t, function (e, i) { + if (null == a || "all" == a || -1 !== l(a, i)) { + var r = {type: "select", $fromToolbox: !0, id: qN + t + i}; + r[o] = i, n.push(r) + } + }) + } + } + + function i(e, i) { + var n = t[e]; + y(n) || (n = n ? [n] : []), YN(n, i) + } + + if (t) { + var n = t.dataZoom || (t.dataZoom = []); + y(n) || (t.dataZoom = n = [n]); + var o = t.toolbox; + if (o && (y(o) && (o = o[0]), o && o.feature)) { + var a = o.feature.dataZoom; + e("xAxis", a), e("yAxis", a) + } + } + }); + var JN = lI.toolbox.restore; + bx.defaultOption = { + show: !0, + icon: "M3.8,33.4 M47,18.9h9.8V8.7 M56.3,20.1 C52.1,9,40.5,0.6,26.8,2.1C12.6,3.7,1.6,16.2,2.1,30.6 M13,41.1H3.1v10.2 M3.7,39.9c4.2,11.1,15.8,19.5,29.5,18 c14.2-1.6,25.2-14.1,24.7-28.5", + title: JN.title + }, bx.prototype.onclick = function (t, e, i) { + px(t), e.dispatchAction({type: "restore", from: this.uid}) + }, Bv("restore", bx), cs({type: "restore", event: "restore", update: "prepareAndUpdate"}, function (t, e) { + e.resetOption("recreate") + }); + var QN, tO = "urn:schemas-microsoft-com:vml", eO = "undefined" == typeof window ? null : window, iO = !1, + nO = eO && eO.document; + if (nO && !a_.canvasSupported) try { + !nO.namespaces.zrvml && nO.namespaces.add("zrvml", tO), QN = function (t) { + return nO.createElement("') + } + } catch (t) { + QN = function (t) { + return nO.createElement("<" + t + ' xmlns="' + tO + '" class="zrvml">') + } + } + var oO = Hb.CMD, aO = Math.round, rO = Math.sqrt, sO = Math.abs, lO = Math.cos, uO = Math.sin, hO = Math.max; + if (!a_.canvasSupported) { + var cO = 21600, dO = cO / 2, fO = function (t) { + t.style.cssText = "position:absolute;left:0;top:0;width:1px;height:1px;", t.coordsize = cO + "," + cO, t.coordorigin = "0,0" + }, pO = function (t) { + return String(t).replace(/&/g, "&").replace(/"/g, """) + }, gO = function (t, e, i) { + return "rgb(" + [t, e, i].join(",") + ")" + }, mO = function (t, e) { + e && t && e.parentNode !== t && t.appendChild(e) + }, vO = function (t, e) { + e && t && e.parentNode === t && t.removeChild(e) + }, yO = function (t, e, i) { + return 1e5 * (parseFloat(t) || 0) + 1e3 * (parseFloat(e) || 0) + i + }, xO = function (t, e) { + return "string" == typeof t ? t.lastIndexOf("%") >= 0 ? parseFloat(t) / 100 * e : parseFloat(t) : t + }, _O = function (t, e, i) { + var n = At(e); + i = +i, isNaN(i) && (i = 1), n && (t.color = gO(n[0], n[1], n[2]), t.opacity = i * n[3]) + }, wO = function (t) { + var e = At(t); + return [gO(e[0], e[1], e[2]), e[3]] + }, bO = function (t, e, i) { + var n = e.fill; + if (null != n) if (n instanceof CS) { + var o, a = 0, r = [0, 0], s = 0, l = 1, u = i.getBoundingRect(), h = u.width, c = u.height; + if ("linear" === n.type) { + o = "gradient"; + var d = i.transform, f = [n.x * h, n.y * c], p = [n.x2 * h, n.y2 * c]; + d && (Q(f, f, d), Q(p, p, d)); + var g = p[0] - f[0], m = p[1] - f[1]; + (a = 180 * Math.atan2(g, m) / Math.PI) < 0 && (a += 360), a < 1e-6 && (a = 0) + } else { + o = "gradientradial"; + var f = [n.x * h, n.y * c], d = i.transform, v = i.scale, y = h, x = c; + r = [(f[0] - u.x) / y, (f[1] - u.y) / x], d && Q(f, f, d), y /= v[0] * cO, x /= v[1] * cO; + var _ = hO(y, x); + s = 0 / _, l = 2 * n.r / _ - s + } + var w = n.colorStops.slice(); + w.sort(function (t, e) { + return t.offset - e.offset + }); + for (var b = w.length, S = [], M = [], I = 0; I < b; I++) { + var T = w[I], D = wO(T.color); + M.push(T.offset * l + s + " " + D[0]), 0 !== I && I !== b - 1 || S.push(D) + } + if (b >= 2) { + var A = S[0][0], C = S[1][0], L = S[0][1] * e.opacity, k = S[1][1] * e.opacity; + t.type = o, t.method = "none", t.focus = "100%", t.angle = a, t.color = A, t.color2 = C, t.colors = M.join(","), t.opacity = k, t.opacity2 = L + } + "radial" === o && (t.focusposition = r.join(",")) + } else _O(t, n, e.opacity) + }, SO = function (t, e) { + null != e.lineDash && (t.dashstyle = e.lineDash.join(" ")), null == e.stroke || e.stroke instanceof CS || _O(t, e.stroke, e.opacity) + }, MO = function (t, e, i, n) { + var o = "fill" == e, a = t.getElementsByTagName(e)[0]; + null != i[e] && "none" !== i[e] && (o || !o && i.lineWidth) ? (t[o ? "filled" : "stroked"] = "true", i[e] instanceof CS && vO(t, a), a || (a = Sx(e)), o ? bO(a, i, n) : SO(a, i), mO(t, a)) : (t[o ? "filled" : "stroked"] = "false", vO(t, a)) + }, IO = [[], [], []], TO = function (t, e) { + var i, n, o, a, r, s, l = oO.M, u = oO.C, h = oO.L, c = oO.A, d = oO.Q, f = [], p = t.data, g = t.len(); + for (a = 0; a < g;) { + switch (o = p[a++], n = "", i = 0, o) { + case l: + n = " m ", i = 1, r = p[a++], s = p[a++], IO[0][0] = r, IO[0][1] = s; + break; + case h: + n = " l ", i = 1, r = p[a++], s = p[a++], IO[0][0] = r, IO[0][1] = s; + break; + case d: + case u: + n = " c ", i = 3; + var m, v, y = p[a++], x = p[a++], _ = p[a++], w = p[a++]; + o === d ? (m = _, v = w, _ = (_ + 2 * y) / 3, w = (w + 2 * x) / 3, y = (r + 2 * y) / 3, x = (s + 2 * x) / 3) : (m = p[a++], v = p[a++]), IO[0][0] = y, IO[0][1] = x, IO[1][0] = _, IO[1][1] = w, IO[2][0] = m, IO[2][1] = v, r = m, s = v; + break; + case c: + var b = 0, S = 0, M = 1, I = 1, T = 0; + e && (b = e[4], S = e[5], M = rO(e[0] * e[0] + e[1] * e[1]), I = rO(e[2] * e[2] + e[3] * e[3]), T = Math.atan2(-e[1] / I, e[0] / M)); + var D = p[a++], A = p[a++], C = p[a++], L = p[a++], k = p[a++] + T, P = p[a++] + k + T; + a++; + var N = p[a++], O = D + lO(k) * C, E = A + uO(k) * L, y = D + lO(P) * C, x = A + uO(P) * L, + R = N ? " wa " : " at "; + Math.abs(O - y) < 1e-4 && (Math.abs(P - k) > .01 ? N && (O += .0125) : Math.abs(E - A) < 1e-4 ? N && O < D || !N && O > D ? x -= .0125 : x += .0125 : N && E < A || !N && E > A ? y += .0125 : y -= .0125), f.push(R, aO(((D - C) * M + b) * cO - dO), ",", aO(((A - L) * I + S) * cO - dO), ",", aO(((D + C) * M + b) * cO - dO), ",", aO(((A + L) * I + S) * cO - dO), ",", aO((O * M + b) * cO - dO), ",", aO((E * I + S) * cO - dO), ",", aO((y * M + b) * cO - dO), ",", aO((x * I + S) * cO - dO)), r = y, s = x; + break; + case oO.R: + var z = IO[0], B = IO[1]; + z[0] = p[a++], z[1] = p[a++], B[0] = z[0] + p[a++], B[1] = z[1] + p[a++], e && (Q(z, z, e), Q(B, B, e)), z[0] = aO(z[0] * cO - dO), B[0] = aO(B[0] * cO - dO), z[1] = aO(z[1] * cO - dO), B[1] = aO(B[1] * cO - dO), f.push(" m ", z[0], ",", z[1], " l ", B[0], ",", z[1], " l ", B[0], ",", B[1], " l ", z[0], ",", B[1]); + break; + case oO.Z: + f.push(" x ") + } + if (i > 0) { + f.push(n); + for (var V = 0; V < i; V++) { + var G = IO[V]; + e && Q(G, G, e), f.push(aO(G[0] * cO - dO), ",", aO(G[1] * cO - dO), V < i - 1 ? "," : "") + } + } + } + return f.join("") + }; + In.prototype.brushVML = function (t) { + var e = this.style, i = this._vmlEl; + i || (i = Sx("shape"), fO(i), this._vmlEl = i), MO(i, "fill", e, this), MO(i, "stroke", e, this); + var n = this.transform, o = null != n, a = i.getElementsByTagName("stroke")[0]; + if (a) { + var r = e.lineWidth; + if (o && !e.strokeNoScale) { + var s = n[0] * n[3] - n[1] * n[2]; + r *= rO(sO(s)) + } + a.weight = r + "px" + } + var l = this.path || (this.path = new Hb); + this.__dirtyPath && (l.beginPath(), this.buildPath(l, this.shape), l.toStatic(), this.__dirtyPath = !1), i.path = TO(l, this.transform), i.style.zIndex = yO(this.zlevel, this.z, this.z2), mO(t, i), null != e.text ? this.drawRectText(t, this.getBoundingRect()) : this.removeRectText(t) + }, In.prototype.onRemove = function (t) { + vO(t, this._vmlEl), this.removeRectText(t) + }, In.prototype.onAdd = function (t) { + mO(t, this._vmlEl), this.appendRectText(t) + }; + var DO = function (t) { + return "object" == typeof t && t.tagName && "IMG" === t.tagName.toUpperCase() + }; + Je.prototype.brushVML = function (t) { + var e, i, n = this.style, o = n.image; + if (DO(o)) { + var a = o.src; + if (a === this._imageSrc) e = this._imageWidth, i = this._imageHeight; else { + var r = o.runtimeStyle, s = r.width, l = r.height; + r.width = "auto", r.height = "auto", e = o.width, i = o.height, r.width = s, r.height = l, this._imageSrc = a, this._imageWidth = e, this._imageHeight = i + } + o = a + } else o === this._imageSrc && (e = this._imageWidth, i = this._imageHeight); + if (o) { + var u = n.x || 0, h = n.y || 0, c = n.width, d = n.height, f = n.sWidth, p = n.sHeight, g = n.sx || 0, + m = n.sy || 0, v = f && p, y = this._vmlEl; + y || (y = nO.createElement("div"), fO(y), this._vmlEl = y); + var x, _ = y.style, w = !1, b = 1, S = 1; + if (this.transform && (x = this.transform, b = rO(x[0] * x[0] + x[1] * x[1]), S = rO(x[2] * x[2] + x[3] * x[3]), w = x[1] || x[2]), w) { + var M = [u, h], I = [u + c, h], T = [u, h + d], D = [u + c, h + d]; + Q(M, M, x), Q(I, I, x), Q(T, T, x), Q(D, D, x); + var A = hO(M[0], I[0], T[0], D[0]), C = hO(M[1], I[1], T[1], D[1]), L = []; + L.push("M11=", x[0] / b, ",", "M12=", x[2] / S, ",", "M21=", x[1] / b, ",", "M22=", x[3] / S, ",", "Dx=", aO(u * b + x[4]), ",", "Dy=", aO(h * S + x[5])), _.padding = "0 " + aO(A) + "px " + aO(C) + "px 0", _.filter = "progid:DXImageTransform.Microsoft.Matrix(" + L.join("") + ", SizingMethod=clip)" + } else x && (u = u * b + x[4], h = h * S + x[5]), _.filter = "", _.left = aO(u) + "px", _.top = aO(h) + "px"; + var k = this._imageEl, P = this._cropEl; + k || (k = nO.createElement("div"), this._imageEl = k); + var N = k.style; + if (v) { + if (e && i) N.width = aO(b * e * c / f) + "px", N.height = aO(S * i * d / p) + "px"; else { + var O = new Image, E = this; + O.onload = function () { + O.onload = null, e = O.width, i = O.height, N.width = aO(b * e * c / f) + "px", N.height = aO(S * i * d / p) + "px", E._imageWidth = e, E._imageHeight = i, E._imageSrc = o + }, O.src = o + } + P || ((P = nO.createElement("div")).style.overflow = "hidden", this._cropEl = P); + var R = P.style; + R.width = aO((c + g * c / f) * b), R.height = aO((d + m * d / p) * S), R.filter = "progid:DXImageTransform.Microsoft.Matrix(Dx=" + -g * c / f * b + ",Dy=" + -m * d / p * S + ")", P.parentNode || y.appendChild(P), k.parentNode != P && P.appendChild(k) + } else N.width = aO(b * c) + "px", N.height = aO(S * d) + "px", y.appendChild(k), P && P.parentNode && (y.removeChild(P), this._cropEl = null); + var z = "", B = n.opacity; + B < 1 && (z += ".Alpha(opacity=" + aO(100 * B) + ") "), z += "progid:DXImageTransform.Microsoft.AlphaImageLoader(src=" + o + ", SizingMethod=scale)", N.filter = z, y.style.zIndex = yO(this.zlevel, this.z, this.z2), mO(t, y), null != n.text && this.drawRectText(t, this.getBoundingRect()) + } + }, Je.prototype.onRemove = function (t) { + vO(t, this._vmlEl), this._vmlEl = null, this._cropEl = null, this._imageEl = null, this.removeRectText(t) + }, Je.prototype.onAdd = function (t) { + mO(t, this._vmlEl), this.appendRectText(t) + }; + var AO, CO = {}, LO = 0, kO = document.createElement("div"), PO = function (t) { + var e = CO[t]; + if (!e) { + LO > 100 && (LO = 0, CO = {}); + var i, n = kO.style; + try { + n.font = t, i = n.fontFamily.split(",")[0] + } catch (t) { + } + e = { + style: n.fontStyle || "normal", + variant: n.fontVariant || "normal", + weight: n.fontWeight || "normal", + size: 0 | parseFloat(n.fontSize || 12), + family: i || "Microsoft YaHei" + }, CO[t] = e, LO++ + } + return e + }; + !function (t, e) { + Dw[t] = e + }("measureText", function (t, e) { + var i = nO; + AO || ((AO = i.createElement("div")).style.cssText = "position:absolute;top:-20000px;left:0;padding:0;margin:0;border:none;white-space:pre;", nO.body.appendChild(AO)); + try { + AO.style.font = e + } catch (t) { + } + return AO.innerHTML = "", AO.appendChild(i.createTextNode(t)), {width: AO.offsetWidth} + }); + for (var NO = new $t, OO = [kw, $e, Je, In, fS], EO = 0; EO < OO.length; EO++) { + var RO = OO[EO].prototype; + RO.drawRectText = function (t, e, i, n) { + var o = this.style; + this.__dirty && Ne(o); + var a = o.text; + if (null != a && (a += ""), a) { + if (o.rich) { + var r = Ce(a, o); + a = []; + for (var s = 0; s < r.lines.length; s++) { + for (var l = r.lines[s].tokens, u = [], h = 0; h < l.length; h++) u.push(l[h].text); + a.push(u.join("")) + } + a = a.join("\n") + } + var c, d, f = o.textAlign, p = o.textVerticalAlign, g = PO(o.font), + m = g.style + " " + g.variant + " " + g.weight + " " + g.size + 'px "' + g.family + '"'; + i = i || me(a, m, f, p); + var v = this.transform; + if (v && !n && (NO.copy(e), NO.applyTransform(v), e = NO), n) c = e.x, d = e.y; else { + var y = o.textPosition, x = o.textDistance; + if (y instanceof Array) c = e.x + xO(y[0], e.width), d = e.y + xO(y[1], e.height), f = f || "left"; else { + var _ = we(y, e, x); + c = _.x, d = _.y, f = f || _.textAlign, p = p || _.textVerticalAlign + } + } + c = xe(c, i.width, f), d = _e(d, i.height, p), d += i.height / 2; + var w, b, S, M = Sx, I = this._textVmlEl; + I ? b = (w = (S = I.firstChild).nextSibling).nextSibling : (I = M("line"), w = M("path"), b = M("textpath"), S = M("skew"), b.style["v-text-align"] = "left", fO(I), w.textpathok = !0, b.on = !0, I.from = "0 0", I.to = "1000 0.05", mO(I, S), mO(I, w), mO(I, b), this._textVmlEl = I); + var T = [c, d], D = I.style; + v && n ? (Q(T, T, v), S.on = !0, S.matrix = v[0].toFixed(3) + "," + v[2].toFixed(3) + "," + v[1].toFixed(3) + "," + v[3].toFixed(3) + ",0,0", S.offset = (aO(T[0]) || 0) + "," + (aO(T[1]) || 0), S.origin = "0 0", D.left = "0px", D.top = "0px") : (S.on = !1, D.left = aO(c) + "px", D.top = aO(d) + "px"), b.string = pO(a); + try { + b.style.font = m + } catch (t) { + } + MO(I, "fill", {fill: o.textFill, opacity: o.opacity}, this), MO(I, "stroke", { + stroke: o.textStroke, + opacity: o.opacity, + lineDash: o.lineDash + }, this), I.style.zIndex = yO(this.zlevel, this.z, this.z2), mO(t, I) + } + }, RO.removeRectText = function (t) { + vO(t, this._textVmlEl), this._textVmlEl = null + }, RO.appendRectText = function (t) { + mO(t, this._textVmlEl) + } + } + fS.prototype.brushVML = function (t) { + var e = this.style; + null != e.text ? this.drawRectText(t, { + x: e.x || 0, + y: e.y || 0, + width: 0, + height: 0 + }, this.getBoundingRect(), !0) : this.removeRectText(t) + }, fS.prototype.onRemove = function (t) { + this.removeRectText(t) + }, fS.prototype.onAdd = function (t) { + this.appendRectText(t) + } + } + Tx.prototype = { + constructor: Tx, getType: function () { + return "vml" + }, getViewportRoot: function () { + return this._vmlViewport + }, getViewportRootOffset: function () { + var t = this.getViewportRoot(); + if (t) return {offsetLeft: t.offsetLeft || 0, offsetTop: t.offsetTop || 0} + }, refresh: function () { + var t = this.storage.getDisplayList(!0, !0); + this._paintList(t) + }, _paintList: function (t) { + for (var e = this._vmlRoot, i = 0; i < t.length; i++) { + var n = t[i]; + n.invisible || n.ignore ? (n.__alreadyNotVisible || n.onRemove(e), n.__alreadyNotVisible = !0) : (n.__alreadyNotVisible && n.onAdd(e), n.__alreadyNotVisible = !1, n.__dirty && (n.beforeBrush && n.beforeBrush(), (n.brushVML || n.brush).call(n, e), n.afterBrush && n.afterBrush())), n.__dirty = !1 + } + this._firstPaint && (this._vmlViewport.appendChild(e), this._firstPaint = !1) + }, resize: function (t, e) { + var t = null == t ? this._getWidth() : t, e = null == e ? this._getHeight() : e; + if (this._width != t || this._height != e) { + this._width = t, this._height = e; + var i = this._vmlViewport.style; + i.width = t + "px", i.height = e + "px" + } + }, dispose: function () { + this.root.innerHTML = "", this._vmlRoot = this._vmlViewport = this.storage = null + }, getWidth: function () { + return this._width + }, getHeight: function () { + return this._height + }, clear: function () { + this._vmlViewport && this.root.removeChild(this._vmlViewport) + }, _getWidth: function () { + var t = this.root, e = t.currentStyle; + return (t.clientWidth || Ix(e.width)) - Ix(e.paddingLeft) - Ix(e.paddingRight) | 0 + }, _getHeight: function () { + var t = this.root, e = t.currentStyle; + return (t.clientHeight || Ix(e.height)) - Ix(e.paddingTop) - Ix(e.paddingBottom) | 0 + } + }, d(["getLayer", "insertLayer", "eachLayer", "eachBuiltinLayer", "eachOtherLayer", "getLayers", "modLayer", "delLayer", "clearLayer", "toDataURL", "pathToImage"], function (t) { + Tx.prototype[t] = Dx(t) + }), wi("vml", Tx); + var zO = "http://www.w3.org/2000/svg", BO = Hb.CMD, VO = Array.prototype.join, GO = "none", WO = Math.round, + FO = Math.sin, HO = Math.cos, ZO = Math.PI, UO = 2 * Math.PI, XO = 180 / ZO, jO = 1e-4, YO = {}; + YO.brush = function (t) { + var e = t.style, i = t.__svgEl; + i || (i = Ax("path"), t.__svgEl = i), t.path || t.createPathProxy(); + var n = t.path; + if (t.__dirtyPath) { + n.beginPath(), t.buildPath(n, t.shape), t.__dirtyPath = !1; + var o = zx(n); + o.indexOf("NaN") < 0 && Ox(i, "d", o) + } + Rx(i, e), Nx(i, t.transform), null != e.text && JO(t, t.getBoundingRect()) + }; + var qO = {}; + qO.brush = function (t) { + var e = t.style, i = e.image; + if (i instanceof HTMLImageElement && (i = i.src), i) { + var n = e.x || 0, o = e.y || 0, a = e.width, r = e.height, s = t.__svgEl; + s || (s = Ax("image"), t.__svgEl = s), i !== t.__imageSrc && (Ex(s, "href", i), t.__imageSrc = i), Ox(s, "width", a), Ox(s, "height", r), Ox(s, "x", n), Ox(s, "y", o), Nx(s, t.transform), null != e.text && JO(t, t.getBoundingRect()) + } + }; + var KO = {}, $O = new $t, JO = function (t, e, i) { + var n = t.style; + t.__dirty && Ne(n); + var o = n.text; + if (null != o) { + o += ""; + var a = t.__textSvgEl; + a || (a = Ax("text"), t.__textSvgEl = a); + var r, s, l = n.textPosition, u = n.textDistance, h = n.textAlign || "left"; + "number" == typeof n.fontSize && (n.fontSize += "px"); + var c = n.font || [n.fontStyle || "", n.fontWeight || "", n.fontSize || "", n.fontFamily || ""].join(" ") || Tw, + d = Bx(n.textVerticalAlign), f = (i = me(o, c, h, d)).lineHeight; + if (l instanceof Array) r = e.x + l[0], s = e.y + l[1]; else { + var p = we(l, e, u); + r = p.x, s = p.y, d = Bx(p.textVerticalAlign), h = p.textAlign + } + Ox(a, "alignment-baseline", d), c && (a.style.font = c); + var g = n.textPadding; + if (Ox(a, "x", r), Ox(a, "y", s), Rx(a, n, !0), t instanceof fS || t.style.transformText) Nx(a, t.transform); else { + if (t.transform) $O.copy(e), $O.applyTransform(t.transform), e = $O; else { + var m = t.transformCoordToGlobal(e.x, e.y); + e.x = m[0], e.y = m[1] + } + var v = n.textOrigin; + "center" === v ? (r = i.width / 2 + r, s = i.height / 2 + s) : v && (r = v[0] + r, s = v[1] + s); + var y = -n.textRotation || 0, x = st(); + dt(x, t.transform, y), Nx(a, x) + } + var _ = o.split("\n"), w = _.length, b = h; + "left" === b ? (b = "start", g && (r += g[3])) : "right" === b ? (b = "end", g && (r -= g[1])) : "center" === b && (b = "middle", g && (r += (g[3] - g[1]) / 2)); + var S = 0; + if ("baseline" === d ? (S = -i.height + f, g && (S -= g[2])) : "middle" === d ? (S = (-i.height + f) / 2, g && (s += (g[0] - g[2]) / 2)) : g && (S += g[0]), t.__text !== o || t.__textFont !== c) { + var M = t.__tspanList || []; + t.__tspanList = M; + for (T = 0; T < w; T++) (D = M[T]) ? D.innerHTML = "" : (D = M[T] = Ax("tspan"), a.appendChild(D), Ox(D, "alignment-baseline", d), Ox(D, "text-anchor", b)), Ox(D, "x", r), Ox(D, "y", s + T * f + S), D.appendChild(document.createTextNode(_[T])); + for (; T < M.length; T++) a.removeChild(M[T]); + M.length = w, t.__text = o, t.__textFont = c + } else if (t.__tspanList.length) for (var I = t.__tspanList.length, T = 0; T < I; ++T) { + var D = t.__tspanList[T]; + D && (Ox(D, "x", r), Ox(D, "y", s + T * f + S)) + } + } + }; + KO.drawRectText = JO, KO.brush = function (t) { + var e = t.style; + null != e.text && (e.textPosition = [0, 0], JO(t, { + x: e.x || 0, + y: e.y || 0, + width: 0, + height: 0 + }, t.getBoundingRect())) + }, Vx.prototype = { + diff: function (t, e, i) { + i || (i = function (t, e) { + return t === e + }), this.equals = i; + var n = this; + t = t.slice(); + var o = (e = e.slice()).length, a = t.length, r = 1, s = o + a, l = [{newPos: -1, components: []}], + u = this.extractCommon(l[0], e, t, 0); + if (l[0].newPos + 1 >= o && u + 1 >= a) { + for (var h = [], c = 0; c < e.length; c++) h.push(c); + return [{indices: h, count: e.length}] + } + for (; r <= s;) { + var d = function () { + for (var i = -1 * r; i <= r; i += 2) { + var s, u = l[i - 1], h = l[i + 1], c = (h ? h.newPos : 0) - i; + u && (l[i - 1] = void 0); + var d = u && u.newPos + 1 < o, f = h && 0 <= c && c < a; + if (d || f) { + if (!d || f && u.newPos < h.newPos ? (s = Wx(h), n.pushComponent(s.components, void 0, !0)) : ((s = u).newPos++, n.pushComponent(s.components, !0, void 0)), c = n.extractCommon(s, e, t, i), s.newPos + 1 >= o && c + 1 >= a) return Gx(0, s.components); + l[i] = s + } else l[i] = void 0 + } + r++ + }(); + if (d) return d + } + }, pushComponent: function (t, e, i) { + var n = t[t.length - 1]; + n && n.added === e && n.removed === i ? t[t.length - 1] = { + count: n.count + 1, + added: e, + removed: i + } : t.push({count: 1, added: e, removed: i}) + }, extractCommon: function (t, e, i, n) { + for (var o = e.length, a = i.length, r = t.newPos, s = r - n, l = 0; r + 1 < o && s + 1 < a && this.equals(e[r + 1], i[s + 1]);) r++, s++, l++; + return l && t.components.push({count: l}), t.newPos = r, s + }, tokenize: function (t) { + return t.slice() + }, join: function (t) { + return t.slice() + } + }; + var QO = new Vx, tE = function (t, e, i) { + return QO.diff(t, e, i) + }; + Fx.prototype.createElement = Ax, Fx.prototype.getDefs = function (t) { + var e = this._svgRoot, i = this._svgRoot.getElementsByTagName("defs"); + return 0 === i.length ? t ? ((i = e.insertBefore(this.createElement("defs"), e.firstChild)).contains || (i.contains = function (t) { + var e = i.children; + if (!e) return !1; + for (var n = e.length - 1; n >= 0; --n) if (e[n] === t) return !0; + return !1 + }), i) : null : i[0] + }, Fx.prototype.update = function (t, e) { + if (t) { + var i = this.getDefs(!1); + if (t[this._domName] && i.contains(t[this._domName])) "function" == typeof e && e(t); else { + var n = this.add(t); + n && (t[this._domName] = n) + } + } + }, Fx.prototype.addDom = function (t) { + this.getDefs(!0).appendChild(t) + }, Fx.prototype.removeDom = function (t) { + var e = this.getDefs(!1); + e && t[this._domName] && (e.removeChild(t[this._domName]), t[this._domName] = null) + }, Fx.prototype.getDoms = function () { + var t = this.getDefs(!1); + if (!t) return []; + var e = []; + return d(this._tagNames, function (i) { + var n = t.getElementsByTagName(i); + e = e.concat([].slice.call(n)) + }), e + }, Fx.prototype.markAllUnused = function () { + var t = this; + d(this.getDoms(), function (e) { + e[t._markLabel] = "0" + }) + }, Fx.prototype.markUsed = function (t) { + t && (t[this._markLabel] = "1") + }, Fx.prototype.removeUnused = function () { + var t = this.getDefs(!1); + if (t) { + var e = this; + d(this.getDoms(), function (i) { + "1" !== i[e._markLabel] && t.removeChild(i) + }) + } + }, Fx.prototype.getSvgProxy = function (t) { + return t instanceof In ? YO : t instanceof Je ? qO : t instanceof fS ? KO : YO + }, Fx.prototype.getTextSvgElement = function (t) { + return t.__textSvgEl + }, Fx.prototype.getSvgElement = function (t) { + return t.__svgEl + }, u(Hx, Fx), Hx.prototype.addWithoutUpdate = function (t, e) { + if (e && e.style) { + var i = this; + d(["fill", "stroke"], function (n) { + if (e.style[n] && ("linear" === e.style[n].type || "radial" === e.style[n].type)) { + var o, a = e.style[n], r = i.getDefs(!0); + a._dom ? (o = a._dom, r.contains(a._dom) || i.addDom(o)) : o = i.add(a), i.markUsed(e); + var s = o.getAttribute("id"); + t.setAttribute(n, "url(#" + s + ")") + } + }) + } + }, Hx.prototype.add = function (t) { + var e; + if ("linear" === t.type) e = this.createElement("linearGradient"); else { + if ("radial" !== t.type) return ew("Illegal gradient type."), null; + e = this.createElement("radialGradient") + } + return t.id = t.id || this.nextId++, e.setAttribute("id", "zr" + this._zrId + "-gradient-" + t.id), this.updateDom(t, e), this.addDom(e), e + }, Hx.prototype.update = function (t) { + var e = this; + Fx.prototype.update.call(this, t, function () { + var i = t.type, n = t._dom.tagName; + "linear" === i && "linearGradient" === n || "radial" === i && "radialGradient" === n ? e.updateDom(t, t._dom) : (e.removeDom(t), e.add(t)) + }) + }, Hx.prototype.updateDom = function (t, e) { + if ("linear" === t.type) e.setAttribute("x1", t.x), e.setAttribute("y1", t.y), e.setAttribute("x2", t.x2), e.setAttribute("y2", t.y2); else { + if ("radial" !== t.type) return void ew("Illegal gradient type."); + e.setAttribute("cx", t.x), e.setAttribute("cy", t.y), e.setAttribute("r", t.r) + } + t.global ? e.setAttribute("gradientUnits", "userSpaceOnUse") : e.setAttribute("gradientUnits", "objectBoundingBox"), e.innerHTML = ""; + for (var i = t.colorStops, n = 0, o = i.length; n < o; ++n) { + var a = this.createElement("stop"); + a.setAttribute("offset", 100 * i[n].offset + "%"), a.setAttribute("stop-color", i[n].color), e.appendChild(a) + } + t._dom = e + }, Hx.prototype.markUsed = function (t) { + if (t.style) { + var e = t.style.fill; + e && e._dom && Fx.prototype.markUsed.call(this, e._dom), (e = t.style.stroke) && e._dom && Fx.prototype.markUsed.call(this, e._dom) + } + }, u(Zx, Fx), Zx.prototype.update = function (t) { + var e = this.getSvgElement(t); + e && this.updateDom(e, t.__clipPaths, !1); + var i = this.getTextSvgElement(t); + i && this.updateDom(i, t.__clipPaths, !0), this.markUsed(t) + }, Zx.prototype.updateDom = function (t, e, i) { + if (e && e.length > 0) { + var n, o, a = this.getDefs(!0), r = e[0], s = i ? "_textDom" : "_dom"; + r[s] ? (o = r[s].getAttribute("id"), n = r[s], a.contains(n) || a.appendChild(n)) : (o = "zr" + this._zrId + "-clip-" + this.nextId, ++this.nextId, (n = this.createElement("clipPath")).setAttribute("id", o), a.appendChild(n), r[s] = n); + var l = this.getSvgProxy(r); + if (r.transform && r.parent.invTransform && !i) { + var u = Array.prototype.slice.call(r.transform); + ht(r.transform, r.parent.invTransform, r.transform), l.brush(r), r.transform = u + } else l.brush(r); + var h = this.getSvgElement(r); + n.innerHTML = "", n.appendChild(h.cloneNode()), t.setAttribute("clip-path", "url(#" + o + ")"), e.length > 1 && this.updateDom(n, e.slice(1), i) + } else t && t.setAttribute("clip-path", "none") + }, Zx.prototype.markUsed = function (t) { + var e = this; + t.__clipPaths && t.__clipPaths.length > 0 && d(t.__clipPaths, function (t) { + t._dom && Fx.prototype.markUsed.call(e, t._dom), t._textDom && Fx.prototype.markUsed.call(e, t._textDom) + }) + }, u(Ux, Fx), Ux.prototype.addWithoutUpdate = function (t, e) { + if (e && Xx(e.style)) { + var i, n = e.style; + n._shadowDom ? (i = n._shadowDom, this.getDefs(!0).contains(n._shadowDom) || this.addDom(i)) : i = this.add(e), this.markUsed(e); + var o = i.getAttribute("id"); + t.style.filter = "url(#" + o + ")" + } + }, Ux.prototype.add = function (t) { + var e = this.createElement("filter"), i = t.style; + return i._shadowDomId = i._shadowDomId || this.nextId++, e.setAttribute("id", "zr" + this._zrId + "-shadow-" + i._shadowDomId), this.updateDom(t, e), this.addDom(e), e + }, Ux.prototype.update = function (t, e) { + var i = e.style; + if (Xx(i)) { + var n = this; + Fx.prototype.update.call(this, e, function (t) { + n.updateDom(e, t._shadowDom) + }) + } else this.remove(t, i) + }, Ux.prototype.remove = function (t, e) { + null != e._shadowDomId && (this.removeDom(e), t.style.filter = "") + }, Ux.prototype.updateDom = function (t, e) { + var i = e.getElementsByTagName("feDropShadow"); + i = 0 === i.length ? this.createElement("feDropShadow") : i[0]; + var n, o, a, r, s = t.style, l = t.scale ? t.scale[0] || 1 : 1, u = t.scale ? t.scale[1] || 1 : 1; + if (s.shadowBlur || s.shadowOffsetX || s.shadowOffsetY) n = s.shadowOffsetX || 0, o = s.shadowOffsetY || 0, a = s.shadowBlur, r = s.shadowColor; else { + if (!s.textShadowBlur) return void this.removeDom(e, s); + n = s.textShadowOffsetX || 0, o = s.textShadowOffsetY || 0, a = s.textShadowBlur, r = s.textShadowColor + } + i.setAttribute("dx", n / l), i.setAttribute("dy", o / u), i.setAttribute("flood-color", r); + var h = a / 2 / l + " " + a / 2 / u; + i.setAttribute("stdDeviation", h), e.setAttribute("x", "-100%"), e.setAttribute("y", "-100%"), e.setAttribute("width", Math.ceil(a / 2 * 200) + "%"), e.setAttribute("height", Math.ceil(a / 2 * 200) + "%"), e.appendChild(i), s._shadowDom = e + }, Ux.prototype.markUsed = function (t) { + var e = t.style; + e && e._shadowDom && Fx.prototype.markUsed.call(this, e._shadowDom) + }; + var eE = function (t, e, i, n) { + this.root = t, this.storage = e, this._opts = i = a({}, i || {}); + var o = Ax("svg"); + o.setAttribute("xmlns", "http://www.w3.org/2000/svg"), o.setAttribute("version", "1.1"), o.setAttribute("baseProfile", "full"), o.style.cssText = "user-select:none;position:absolute;left:0;top:0;", this.gradientManager = new Hx(n, o), this.clipPathManager = new Zx(n, o), this.shadowManager = new Ux(n, o); + var r = document.createElement("div"); + r.style.cssText = "overflow:hidden;position:relative", this._svgRoot = o, this._viewport = r, t.appendChild(r), r.appendChild(o), this.resize(i.width, i.height), this._visibleList = [] + }; + eE.prototype = { + constructor: eE, getType: function () { + return "svg" + }, getViewportRoot: function () { + return this._viewport + }, getViewportRootOffset: function () { + var t = this.getViewportRoot(); + if (t) return {offsetLeft: t.offsetLeft || 0, offsetTop: t.offsetTop || 0} + }, refresh: function () { + var t = this.storage.getDisplayList(!0); + this._paintList(t) + }, setBackgroundColor: function (t) { + this._viewport.style.background = t + }, _paintList: function (t) { + this.gradientManager.markAllUnused(), this.clipPathManager.markAllUnused(), this.shadowManager.markAllUnused(); + var e, i = this._svgRoot, n = this._visibleList, o = t.length, a = []; + for (e = 0; e < o; e++) { + var r = Yx(f = t[e]), s = t_(f) || Qx(f); + f.invisible || (f.__dirty && (r && r.brush(f), this.clipPathManager.update(f), f.style && (this.gradientManager.update(f.style.fill), this.gradientManager.update(f.style.stroke), this.shadowManager.update(s, f)), f.__dirty = !1), a.push(f)) + } + var l, u = tE(n, a); + for (e = 0; e < u.length; e++) if ((c = u[e]).removed) for (d = 0; d < c.count; d++) { + var s = t_(f = n[c.indices[d]]), h = Qx(f); + Jx(i, s), Jx(i, h) + } + for (e = 0; e < u.length; e++) { + var c = u[e]; + if (c.added) for (d = 0; d < c.count; d++) { + var s = t_(f = a[c.indices[d]]), h = Qx(f); + l ? Kx(i, s, l) : $x(i, s), s ? Kx(i, h, s) : l ? Kx(i, h, l) : $x(i, h), Kx(i, h, s), l = h || s || l, this.gradientManager.addWithoutUpdate(s, f), this.shadowManager.addWithoutUpdate(l, f), this.clipPathManager.markUsed(f) + } else if (!c.removed) for (var d = 0; d < c.count; d++) { + var f = a[c.indices[d]]; + l = s = Qx(f) || t_(f) || l, this.gradientManager.markUsed(f), this.gradientManager.addWithoutUpdate(s, f), this.shadowManager.markUsed(f), this.shadowManager.addWithoutUpdate(s, f), this.clipPathManager.markUsed(f) + } + } + this.gradientManager.removeUnused(), this.clipPathManager.removeUnused(), this.shadowManager.removeUnused(), this._visibleList = a + }, _getDefs: function (t) { + var e = this._svgRoot, i = this._svgRoot.getElementsByTagName("defs"); + return 0 === i.length ? t ? ((i = e.insertBefore(Ax("defs"), e.firstChild)).contains || (i.contains = function (t) { + var e = i.children; + if (!e) return !1; + for (var n = e.length - 1; n >= 0; --n) if (e[n] === t) return !0; + return !1 + }), i) : null : i[0] + }, resize: function (t, e) { + var i = this._viewport; + i.style.display = "none"; + var n = this._opts; + if (null != t && (n.width = t), null != e && (n.height = e), t = this._getSize(0), e = this._getSize(1), i.style.display = "", this._width !== t || this._height !== e) { + this._width = t, this._height = e; + var o = i.style; + o.width = t + "px", o.height = e + "px"; + var a = this._svgRoot; + a.setAttribute("width", t), a.setAttribute("height", e) + } + }, getWidth: function () { + return this._width + }, getHeight: function () { + return this._height + }, _getSize: function (t) { + var e = this._opts, i = ["width", "height"][t], n = ["clientWidth", "clientHeight"][t], + o = ["paddingLeft", "paddingTop"][t], a = ["paddingRight", "paddingBottom"][t]; + if (null != e[i] && "auto" !== e[i]) return parseFloat(e[i]); + var r = this.root, s = document.defaultView.getComputedStyle(r); + return (r[n] || jx(s[i]) || jx(r.style[i])) - (jx(s[o]) || 0) - (jx(s[a]) || 0) | 0 + }, dispose: function () { + this.root.innerHTML = "", this._svgRoot = this._viewport = this.storage = null + }, clear: function () { + this._viewport && this.root.removeChild(this._viewport) + }, pathToDataUrl: function () { + return this.refresh(), "data:image/svg+xml;charset=UTF-8," + this._svgRoot.outerHTML + } + }, d(["getLayer", "insertLayer", "eachLayer", "eachBuiltinLayer", "eachOtherLayer", "getLayers", "modLayer", "delLayer", "clearLayer", "toDataURL", "pathToImage"], function (t) { + eE.prototype[t] = e_(t) + }), wi("svg", eE), t.version = "4.1.0", t.dependencies = TI, t.PRIORITY = LI, t.init = function (t, e, i) { + var n = ss(t); + if (n) return n; + var o = new Gr(t, e, i); + return o.id = "ec_" + jI++, UI[o.id] = o, Ri(t, qI, o.id), as(o), o + }, t.connect = function (t) { + if (y(t)) { + var e = t; + t = null, bI(e, function (e) { + null != e.group && (t = e.group) + }), t = t || "g_" + YI++, bI(e, function (e) { + e.group = t + }) + } + return XI[t] = !0, t + }, t.disConnect = rs, t.disconnect = $I, t.dispose = function (t) { + "string" == typeof t ? t = UI[t] : t instanceof Gr || (t = ss(t)), t instanceof Gr && !t.isDisposed() && t.dispose() + }, t.getInstanceByDom = ss, t.getInstanceById = function (t) { + return UI[t] + }, t.registerTheme = ls, t.registerPreprocessor = us, t.registerProcessor = hs, t.registerPostUpdate = function (t) { + WI.push(t) + }, t.registerAction = cs, t.registerCoordinateSystem = ds, t.getCoordinateSystemDimensions = function (t) { + var e = Ca.get(t); + if (e) return e.getDimensionsInfo ? e.getDimensionsInfo() : e.dimensions.slice() + }, t.registerLayout = fs, t.registerVisual = ps, t.registerLoading = ms, t.extendComponentModel = vs, t.extendComponentView = ys, t.extendSeriesModel = xs, t.extendChartView = _s, t.setCanvasCreator = function (t) { + e("createCanvas", t) + }, t.registerMap = function (t, e, i) { + e.geoJson && !e.features && (i = e.specialAreas, e = e.geoJson), "string" == typeof e && (e = "undefined" != typeof JSON && JSON.parse ? JSON.parse(e) : new Function("return (" + e + ");")()), KI[t] = { + geoJson: e, + specialAreas: i + } + }, t.getMap = ws, t.dataTool = JI, t.zrender = $w, t.graphic = BS, t.number = qS, t.format = nM, t.throttle = _r, t.helper = ZT, t.matrix = P_, t.vector = I_, t.color = q_, t.parseGeoJSON = XT, t.parseGeoJson = KT, t.util = $T, t.List = sT, t.Model = wo, t.Axis = qT, t.env = a_ +}); diff --git a/ruoyi-ui/src/plugins/index.js b/ruoyi-ui/src/plugins/index.js new file mode 100644 index 0000000..d000f2d --- /dev/null +++ b/ruoyi-ui/src/plugins/index.js @@ -0,0 +1,20 @@ +import tab from './tab' +import auth from './auth' +import cache from './cache' +import modal from './modal' +import download from './download' + +export default { + install(Vue) { + // 页签操作 + Vue.prototype.$tab = tab + // 认证对象 + Vue.prototype.$auth = auth + // 缓存对象 + Vue.prototype.$cache = cache + // 模态框对象 + Vue.prototype.$modal = modal + // 下载文件 + Vue.prototype.$download = download + } +} diff --git a/ruoyi-ui/src/plugins/modal.js b/ruoyi-ui/src/plugins/modal.js new file mode 100644 index 0000000..92bc1ef --- /dev/null +++ b/ruoyi-ui/src/plugins/modal.js @@ -0,0 +1,83 @@ +import { Message, MessageBox, Notification, Loading } from 'element-ui' + +let loadingInstance + +export default { + // 消息提示 + msg(content) { + Message.info(content) + }, + // 错误消息 + msgError(content) { + Message.error(content) + }, + // 成功消息 + msgSuccess(content) { + Message.success(content) + }, + // 警告消息 + msgWarning(content) { + Message.warning(content) + }, + // 弹出提示 + alert(content) { + MessageBox.alert(content, "系统提示") + }, + // 错误提示 + alertError(content) { + MessageBox.alert(content, "系统提示", { type: 'error' }) + }, + // 成功提示 + alertSuccess(content) { + MessageBox.alert(content, "系统提示", { type: 'success' }) + }, + // 警告提示 + alertWarning(content) { + MessageBox.alert(content, "系统提示", { type: 'warning' }) + }, + // 通知提示 + notify(content) { + Notification.info(content) + }, + // 错误通知 + notifyError(content) { + Notification.error(content) + }, + // 成功通知 + notifySuccess(content) { + Notification.success(content) + }, + // 警告通知 + notifyWarning(content) { + Notification.warning(content) + }, + // 确认窗体 + confirm(content) { + return MessageBox.confirm(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 提交内容 + prompt(content) { + return MessageBox.prompt(content, "系统提示", { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: "warning", + }) + }, + // 打开遮罩层 + loading(content) { + loadingInstance = Loading.service({ + lock: true, + text: content, + spinner: "el-icon-loading", + background: "rgba(0, 0, 0, 0.7)", + }) + }, + // 关闭遮罩层 + closeLoading() { + loadingInstance.close() + } +} diff --git a/ruoyi-ui/src/plugins/tab.js b/ruoyi-ui/src/plugins/tab.js new file mode 100644 index 0000000..f21467d --- /dev/null +++ b/ruoyi-ui/src/plugins/tab.js @@ -0,0 +1,71 @@ +import store from '@/store' +import router from '@/router' + +export default { + // 刷新当前tab页签 + refreshPage(obj) { + const { path, query, matched } = router.currentRoute + if (obj === undefined) { + matched.forEach((m) => { + if (m.components && m.components.default && m.components.default.name) { + if (!['Layout', 'ParentView'].includes(m.components.default.name)) { + obj = { name: m.components.default.name, path: path, query: query } + } + } + }) + } + return store.dispatch('tagsView/delCachedView', obj).then(() => { + const { path, query } = obj + router.replace({ + path: '/redirect' + path, + query: query + }) + }) + }, + // 关闭当前tab页签,打开新页签 + closeOpenPage(obj) { + store.dispatch("tagsView/delView", router.currentRoute) + if (obj !== undefined) { + return router.push(obj) + } + }, + // 关闭指定tab页签 + closePage(obj) { + if (obj === undefined) { + return store.dispatch('tagsView/delView', router.currentRoute).then(({ visitedViews }) => { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + return router.push(latestView.fullPath) + } + return router.push('/') + }) + } + return store.dispatch('tagsView/delView', obj) + }, + // 关闭所有tab页签 + closeAllPage() { + return store.dispatch('tagsView/delAllViews') + }, + // 关闭左侧tab页签 + closeLeftPage(obj) { + return store.dispatch('tagsView/delLeftTags', obj || router.currentRoute) + }, + // 关闭右侧tab页签 + closeRightPage(obj) { + return store.dispatch('tagsView/delRightTags', obj || router.currentRoute) + }, + // 关闭其他tab页签 + closeOtherPage(obj) { + return store.dispatch('tagsView/delOthersViews', obj || router.currentRoute) + }, + // 添加tab页签 + openPage(title, url, params) { + const obj = { path: url, meta: { title: title } } + store.dispatch('tagsView/addView', obj) + return router.push({ path: url, query: params }) + }, + // 修改tab页签 + updatePage(obj) { + return store.dispatch('tagsView/updateVisitedView', obj) + } +} diff --git a/ruoyi-ui/src/router/index.js b/ruoyi-ui/src/router/index.js new file mode 100644 index 0000000..fa02e4a --- /dev/null +++ b/ruoyi-ui/src/router/index.js @@ -0,0 +1,184 @@ +import Vue from 'vue' +import Router from 'vue-router' + +Vue.use(Router) + +/* Layout */ +import Layout from '@/layout' +import i18n from '@/lang' + +/** + * Note: 路由配置项 + * + * hidden: true // 当设置 true 的时候该路由不会再侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1 + * alwaysShow: true // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面 + * // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面 + * // 若你想不管路由下面的 children 声明的个数都显示你的根路由 + * // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由 + * redirect: noRedirect // 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击 + * name:'router-name' // 设定路由的名字,一定要填写不然使用时会出现各种问题 + * query: '{"id": 1, "name": "ry"}' // 访问路由的默认传递参数 + * roles: ['admin', 'common'] // 访问路由的角色权限 + * permissions: ['a:a:a', 'b:b:b'] // 访问路由的菜单权限 + * meta : { + noCache: true // 如果设置为true,则不会被 缓存(默认 false) + title: 'title' // 设置该路由在侧边栏和面包屑中展示的名字 + icon: 'svg-name' // 设置该路由的图标,对应路径src/assets/icons/svg + breadcrumb: false // 如果设置为false,则不会在breadcrumb面包屑中显示 + activeMenu: '/system/user' // 当路由设置了该属性,则会高亮相对应的侧边栏。 + } + */ + +// 公共路由 +export const constantRoutes = [ + { + path: '/redirect', + component: Layout, + hidden: true, + children: [ + { + path: '/redirect/:path(.*)', + component: () => import('@/views/redirect') + } + ] + }, + { + path: '/login', + component: () => import('@/views/login'), + hidden: true + }, + { + path: '/register', + component: () => import('@/views/register'), + hidden: true + }, + { + path: '/404', + component: () => import('@/views/error/404'), + hidden: true + }, + { + path: '/401', + component: () => import('@/views/error/401'), + hidden: true + }, + { + path: '', + component: Layout, + redirect: 'index', + children: [ + { + path: 'index', + component: () => import('@/views/index'), + name: 'Index', + meta: { title: i18n.t('menu.home'), icon: 'dashboard', affix: true } + } + ] + }, + { + path: '/user', + component: Layout, + hidden: true, + redirect: 'noredirect', + children: [ + { + path: 'profile', + component: () => import('@/views/system/user/profile/index'), + name: 'Profile', + meta: { title: '个人中心', icon: 'user' } + } + ] + } +] + +// 动态路由,基于用户权限动态去加载 +export const dynamicRoutes = [ + { + path: '/system/user-auth', + component: Layout, + hidden: true, + permissions: ['system:user:edit'], + children: [ + { + path: 'role/:userId(\\d+)', + component: () => import('@/views/system/user/authRole'), + name: 'AuthRole', + meta: { title: '分配角色', activeMenu: '/system/user' } + } + ] + }, + { + path: '/system/role-auth', + component: Layout, + hidden: true, + permissions: ['system:role:edit'], + children: [ + { + path: 'user/:roleId(\\d+)', + component: () => import('@/views/system/role/authUser'), + name: 'AuthUser', + meta: { title: '分配用户', activeMenu: '/system/role' } + } + ] + }, + { + path: '/system/dict-data', + component: Layout, + hidden: true, + permissions: ['system:dict:list'], + children: [ + { + path: 'index/:dictId(\\d+)', + component: () => import('@/views/system/dict/data'), + name: 'Data', + meta: { title: '字典数据', activeMenu: '/system/dict' } + } + ] + }, + { + path: '/monitor/job-log', + component: Layout, + hidden: true, + permissions: ['monitor:job:list'], + children: [ + { + path: 'index/:jobId(\\d+)', + component: () => import('@/views/monitor/job/log'), + name: 'JobLog', + meta: { title: '调度日志', activeMenu: '/monitor/job' } + } + ] + }, + { + path: '/tool/gen-edit', + component: Layout, + hidden: true, + permissions: ['tool:gen:edit'], + children: [ + { + path: 'index/:tableId(\\d+)', + component: () => import('@/views/tool/gen/editTable'), + name: 'GenEdit', + meta: { title: '修改生成配置', activeMenu: '/tool/gen' } + } + ] + } +] + +// 防止连续点击多次路由报错 +let routerPush = Router.prototype.push +let routerReplace = Router.prototype.replace +// push +Router.prototype.push = function push(location) { + return routerPush.call(this, location).catch(err => err) +} +// replace +Router.prototype.replace = function push(location) { + return routerReplace.call(this, location).catch(err => err) +} + +export default new Router({ + mode: 'history', // 去掉url中的# + scrollBehavior: () => ({ y: 0 }), + routes: constantRoutes +}) diff --git a/ruoyi-ui/src/settings.js b/ruoyi-ui/src/settings.js new file mode 100644 index 0000000..f7d1453 --- /dev/null +++ b/ruoyi-ui/src/settings.js @@ -0,0 +1,49 @@ +module.exports = { + /** + * 网页标题 + */ + title: process.env.VUE_APP_TITLE, + + /** + * 侧边栏主题 深色主题theme-dark,浅色主题theme-light,新主题theme-new + */ + sideTheme: 'theme-new', + + /** + * 是否系统布局配置 + */ + showSettings: true, + + /** + * 是否显示顶部导航 + */ + topNav: false, + + /** + * 是否显示 tagsView + */ + tagsView: true, + + /** + * 是否固定头部 + */ + fixedHeader: false, + + /** + * 是否显示logo + */ + sidebarLogo: true, + + /** + * 是否显示动态标题 + */ + dynamicTitle: false, + + /** + * @type {string | array} 'production' | ['production', 'development'] + * @description Need show err logs component. + * The default is only used in the production env + * If you want to also use it in dev, you can pass ['production', 'development'] + */ + errorLog: 'production' +} diff --git a/ruoyi-ui/src/store/getters.js b/ruoyi-ui/src/store/getters.js new file mode 100644 index 0000000..466abfe --- /dev/null +++ b/ruoyi-ui/src/store/getters.js @@ -0,0 +1,22 @@ +const getters = { + sidebar: state => state.app.sidebar, + size: state => state.app.size, + device: state => state.app.device, + dict: state => state.dict.dict, + visitedViews: state => state.tagsView.visitedViews, + cachedViews: state => state.tagsView.cachedViews, + token: state => state.user.token, + avatar: state => state.user.avatar, + id: state => state.user.id, + name: state => state.user.name, + nickName: state => state.user.nickName, + introduction: state => state.user.introduction, + roles: state => state.user.roles, + permissions: state => state.user.permissions, + permission_routes: state => state.permission.routes, + topbarRouters: state => state.permission.topbarRouters, + defaultRoutes: state => state.permission.defaultRoutes, + language: state => state.app.language, + sidebarRouters: state => state.permission.sidebarRouters +} +export default getters diff --git a/ruoyi-ui/src/store/index.js b/ruoyi-ui/src/store/index.js new file mode 100644 index 0000000..97aaef8 --- /dev/null +++ b/ruoyi-ui/src/store/index.js @@ -0,0 +1,25 @@ +import Vue from 'vue' +import Vuex from 'vuex' +import app from './modules/app' +import dict from './modules/dict' +import user from './modules/user' +import tagsView from './modules/tagsView' +import permission from './modules/permission' +import settings from './modules/settings' +import getters from './getters' + +Vue.use(Vuex) + +const store = new Vuex.Store({ + modules: { + app, + dict, + user, + tagsView, + permission, + settings + }, + getters +}) + +export default store diff --git a/ruoyi-ui/src/store/modules/app.js b/ruoyi-ui/src/store/modules/app.js new file mode 100644 index 0000000..60fde8c --- /dev/null +++ b/ruoyi-ui/src/store/modules/app.js @@ -0,0 +1,74 @@ +import Cookies from 'js-cookie' + +const state = { + sidebar: { + opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true, + withoutAnimation: false, + hide: false + }, + device: 'desktop', + size: Cookies.get('size') || 'medium', + language: Cookies.get('language') || 'en' +} + +const mutations = { + TOGGLE_SIDEBAR: state => { + if (state.sidebar.hide) { + return false + } + state.sidebar.opened = !state.sidebar.opened + state.sidebar.withoutAnimation = false + if (state.sidebar.opened) { + Cookies.set('sidebarStatus', 1) + } else { + Cookies.set('sidebarStatus', 0) + } + }, + CLOSE_SIDEBAR: (state, withoutAnimation) => { + Cookies.set('sidebarStatus', 0) + state.sidebar.opened = false + state.sidebar.withoutAnimation = withoutAnimation + }, + TOGGLE_DEVICE: (state, device) => { + state.device = device + }, + SET_SIZE: (state, size) => { + state.size = size + Cookies.set('size', size) + }, + SET_SIDEBAR_HIDE: (state, status) => { + state.sidebar.hide = status + }, + SET_LANGUAGE: (state, language) => { + state.language = language + Cookies.set('language', language) + } +} + +const actions = { + toggleSideBar({ commit }) { + commit('TOGGLE_SIDEBAR') + }, + closeSideBar({ commit }, { withoutAnimation }) { + commit('CLOSE_SIDEBAR', withoutAnimation) + }, + toggleDevice({ commit }, device) { + commit('TOGGLE_DEVICE', device) + }, + setSize({ commit }, size) { + commit('SET_SIZE', size) + }, + toggleSideBarHide({ commit }, status) { + commit('SET_SIDEBAR_HIDE', status) + }, + setLanguage({ commit }, language) { + commit('SET_LANGUAGE', language) + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/dict.js b/ruoyi-ui/src/store/modules/dict.js new file mode 100644 index 0000000..7a1b2f0 --- /dev/null +++ b/ruoyi-ui/src/store/modules/dict.js @@ -0,0 +1,50 @@ +const state = { + dict: new Array() +} +const mutations = { + SET_DICT: (state, { key, value }) => { + if (key !== null && key !== "") { + state.dict.push({ + key: key, + value: value + }) + } + }, + REMOVE_DICT: (state, key) => { + try { + for (let i = 0; i < state.dict.length; i++) { + if (state.dict[i].key == key) { + state.dict.splice(i, 1) + return true + } + } + } catch (e) { + } + }, + CLEAN_DICT: (state) => { + state.dict = new Array() + } +} + +const actions = { + // 设置字典 + setDict({ commit }, data) { + commit('SET_DICT', data) + }, + // 删除字典 + removeDict({ commit }, key) { + commit('REMOVE_DICT', key) + }, + // 清空字典 + cleanDict({ commit }) { + commit('CLEAN_DICT') + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ruoyi-ui/src/store/modules/permission.js b/ruoyi-ui/src/store/modules/permission.js new file mode 100644 index 0000000..ff029ef --- /dev/null +++ b/ruoyi-ui/src/store/modules/permission.js @@ -0,0 +1,127 @@ +import auth from '@/plugins/auth' +import router, { constantRoutes, dynamicRoutes } from '@/router' +import { getRouters } from '@/api/menu' +import Layout from '@/layout/index' +import ParentView from '@/components/ParentView' +import InnerLink from '@/layout/components/InnerLink' +import i18n from "../../lang"; + +const permission = { + state: { + routes: [], + addRoutes: [], + defaultRoutes: [], + topbarRouters: [], + sidebarRouters: [] + }, + mutations: { + SET_ROUTES: (state, routes) => { + state.addRoutes = routes + state.routes = constantRoutes.concat(routes) + }, + SET_DEFAULT_ROUTES: (state, routes) => { + state.defaultRoutes = constantRoutes.concat(routes) + }, + SET_TOPBAR_ROUTES: (state, routes) => { + state.topbarRouters = routes + }, + SET_SIDEBAR_ROUTERS: (state, routes) => { + state.sidebarRouters = routes + }, + }, + actions: { + // 生成路由 + GenerateRoutes({ commit }) { + return new Promise(resolve => { + // 向后端请求路由数据 + getRouters().then(res => { + const sdata = JSON.parse(JSON.stringify(res.data)) + const rdata = JSON.parse(JSON.stringify(res.data)) + const sidebarRoutes = filterAsyncRouter(sdata) + const rewriteRoutes = filterAsyncRouter(rdata, false, true) + const asyncRoutes = filterDynamicRoutes(dynamicRoutes) + rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) + router.addRoutes(asyncRoutes) + commit('SET_ROUTES', rewriteRoutes) + commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes)) + commit('SET_DEFAULT_ROUTES', sidebarRoutes) + commit('SET_TOPBAR_ROUTES', sidebarRoutes) + resolve(rewriteRoutes) + }) + }) + } + } +} + +// 遍历后台传来的路由字符串,转换为组件对象 +function filterAsyncRouter(asyncRouterMap, lastRouter = false, type = false) { + return asyncRouterMap.filter(route => { + if (type && route.children) { + route.children = filterChildren(route.children) + } + if (route.component) { + // Layout ParentView 组件特殊处理 + if (route.component === 'Layout') { + route.component = Layout + } else if (route.component === 'ParentView') { + route.component = ParentView + } else if (route.component === 'InnerLink') { + route.component = InnerLink + } else { + route.component = loadView(route.component) + } + } + // 在 route.component 处理后添加 + if (route.meta && route.meta.title) { + route.meta.title = i18n.t(route.meta.title) + } + if (route.children != null && route.children && route.children.length) { + route.children = filterAsyncRouter(route.children, route, type) + } else { + delete route['children'] + delete route['redirect'] + } + return true + }) +} + +function filterChildren(childrenMap, lastRouter = false) { + var children = [] + childrenMap.forEach(el => { + el.path = lastRouter ? lastRouter.path + '/' + el.path : el.path + if (el.children && el.children.length && el.component === 'ParentView') { + children = children.concat(filterChildren(el.children, el)) + } else { + children.push(el) + } + }) + return children +} + +// 动态路由遍历,验证是否具备权限 +export function filterDynamicRoutes(routes) { + const res = [] + routes.forEach(route => { + if (route.permissions) { + if (auth.hasPermiOr(route.permissions)) { + res.push(route) + } + } else if (route.roles) { + if (auth.hasRoleOr(route.roles)) { + res.push(route) + } + } + }) + return res +} + +export const loadView = (view) => { + if (process.env.NODE_ENV === 'development') { + return (resolve) => require([`@/views/${view}`], resolve) + } else { + // 使用 import 实现生产环境的路由懒加载 + return () => import(`@/views/${view}`) + } +} + +export default permission diff --git a/ruoyi-ui/src/store/modules/settings.js b/ruoyi-ui/src/store/modules/settings.js new file mode 100644 index 0000000..77121d9 --- /dev/null +++ b/ruoyi-ui/src/store/modules/settings.js @@ -0,0 +1,44 @@ +import defaultSettings from '@/settings' +import { useDynamicTitle } from '@/utils/dynamicTitle' + +const { sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle } = defaultSettings + +const storageSetting = JSON.parse(localStorage.getItem('layout-setting')) || '' +const state = { + title: '', + theme: storageSetting.theme || '#409EFF', + sideTheme: storageSetting.sideTheme || sideTheme, + showSettings: showSettings, + topNav: storageSetting.topNav === undefined ? topNav : storageSetting.topNav, + tagsView: storageSetting.tagsView === undefined ? tagsView : storageSetting.tagsView, + fixedHeader: storageSetting.fixedHeader === undefined ? fixedHeader : storageSetting.fixedHeader, + sidebarLogo: storageSetting.sidebarLogo === undefined ? sidebarLogo : storageSetting.sidebarLogo, + dynamicTitle: storageSetting.dynamicTitle === undefined ? dynamicTitle : storageSetting.dynamicTitle +} +const mutations = { + CHANGE_SETTING: (state, { key, value }) => { + if (state.hasOwnProperty(key)) { + state[key] = value + } + } +} + +const actions = { + // 修改布局设置 + changeSetting({ commit }, data) { + commit('CHANGE_SETTING', data) + }, + // 设置网页标题 + setTitle({ commit }, title) { + state.title = title + useDynamicTitle() + } +} + +export default { + namespaced: true, + state, + mutations, + actions +} + diff --git a/ruoyi-ui/src/store/modules/tagsView.js b/ruoyi-ui/src/store/modules/tagsView.js new file mode 100644 index 0000000..5fc011c --- /dev/null +++ b/ruoyi-ui/src/store/modules/tagsView.js @@ -0,0 +1,228 @@ +const state = { + visitedViews: [], + cachedViews: [], + iframeViews: [] +} + +const mutations = { + ADD_IFRAME_VIEW: (state, view) => { + if (state.iframeViews.some(v => v.path === view.path)) return + state.iframeViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_VISITED_VIEW: (state, view) => { + if (state.visitedViews.some(v => v.path === view.path)) return + state.visitedViews.push( + Object.assign({}, view, { + title: view.meta.title || 'no-name' + }) + ) + }, + ADD_CACHED_VIEW: (state, view) => { + if (state.cachedViews.includes(view.name)) return + if (view.meta && !view.meta.noCache) { + state.cachedViews.push(view.name) + } + }, + DEL_VISITED_VIEW: (state, view) => { + for (const [i, v] of state.visitedViews.entries()) { + if (v.path === view.path) { + state.visitedViews.splice(i, 1) + break + } + } + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_IFRAME_VIEW: (state, view) => { + state.iframeViews = state.iframeViews.filter(item => item.path !== view.path) + }, + DEL_CACHED_VIEW: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + index > -1 && state.cachedViews.splice(index, 1) + }, + + DEL_OTHERS_VISITED_VIEWS: (state, view) => { + state.visitedViews = state.visitedViews.filter(v => { + return v.meta.affix || v.path === view.path + }) + state.iframeViews = state.iframeViews.filter(item => item.path === view.path) + }, + DEL_OTHERS_CACHED_VIEWS: (state, view) => { + const index = state.cachedViews.indexOf(view.name) + if (index > -1) { + state.cachedViews = state.cachedViews.slice(index, index + 1) + } else { + state.cachedViews = [] + } + }, + DEL_ALL_VISITED_VIEWS: state => { + // keep affix tags + const affixTags = state.visitedViews.filter(tag => tag.meta.affix) + state.visitedViews = affixTags + state.iframeViews = [] + }, + DEL_ALL_CACHED_VIEWS: state => { + state.cachedViews = [] + }, + UPDATE_VISITED_VIEW: (state, view) => { + for (let v of state.visitedViews) { + if (v.path === view.path) { + v = Object.assign(v, view) + break + } + } + }, + DEL_RIGHT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx <= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + }, + DEL_LEFT_VIEWS: (state, view) => { + const index = state.visitedViews.findIndex(v => v.path === view.path) + if (index === -1) { + return + } + state.visitedViews = state.visitedViews.filter((item, idx) => { + if (idx >= index || (item.meta && item.meta.affix)) { + return true + } + const i = state.cachedViews.indexOf(item.name) + if (i > -1) { + state.cachedViews.splice(i, 1) + } + if(item.meta.link) { + const fi = state.iframeViews.findIndex(v => v.path === item.path) + state.iframeViews.splice(fi, 1) + } + return false + }) + } +} + +const actions = { + addView({ dispatch }, view) { + dispatch('addVisitedView', view) + dispatch('addCachedView', view) + }, + addIframeView({ commit }, view) { + commit('ADD_IFRAME_VIEW', view) + }, + addVisitedView({ commit }, view) { + commit('ADD_VISITED_VIEW', view) + }, + addCachedView({ commit }, view) { + commit('ADD_CACHED_VIEW', view) + }, + delView({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delVisitedView', view) + dispatch('delCachedView', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delVisitedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_VISITED_VIEW', view) + resolve([...state.visitedViews]) + }) + }, + delIframeView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_IFRAME_VIEW', view) + resolve([...state.iframeViews]) + }) + }, + delCachedView({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_CACHED_VIEW', view) + resolve([...state.cachedViews]) + }) + }, + delOthersViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delOthersVisitedViews', view) + dispatch('delOthersCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delOthersVisitedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_VISITED_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delOthersCachedViews({ commit, state }, view) { + return new Promise(resolve => { + commit('DEL_OTHERS_CACHED_VIEWS', view) + resolve([...state.cachedViews]) + }) + }, + delAllViews({ dispatch, state }, view) { + return new Promise(resolve => { + dispatch('delAllVisitedViews', view) + dispatch('delAllCachedViews', view) + resolve({ + visitedViews: [...state.visitedViews], + cachedViews: [...state.cachedViews] + }) + }) + }, + delAllVisitedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_VISITED_VIEWS') + resolve([...state.visitedViews]) + }) + }, + delAllCachedViews({ commit, state }) { + return new Promise(resolve => { + commit('DEL_ALL_CACHED_VIEWS') + resolve([...state.cachedViews]) + }) + }, + updateVisitedView({ commit }, view) { + commit('UPDATE_VISITED_VIEW', view) + }, + delRightTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_RIGHT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, + delLeftTags({ commit }, view) { + return new Promise(resolve => { + commit('DEL_LEFT_VIEWS', view) + resolve([...state.visitedViews]) + }) + }, +} + +export default { + namespaced: true, + state, + mutations, + actions +} diff --git a/ruoyi-ui/src/store/modules/user.js b/ruoyi-ui/src/store/modules/user.js new file mode 100644 index 0000000..f44862d --- /dev/null +++ b/ruoyi-ui/src/store/modules/user.js @@ -0,0 +1,111 @@ +import { login, logout, getInfo } from '@/api/login' +import { getToken, setToken, removeToken } from '@/utils/auth' +import { isHttp, isEmpty } from "@/utils/validate" +import defAva from '@/assets/images/profile.jpg' + +const user = { + state: { + token: getToken(), + id: '', + name: '', + nickName: '', + avatar: '', + roles: [], + permissions: [] + }, + + mutations: { + SET_TOKEN: (state, token) => { + state.token = token + }, + SET_ID: (state, id) => { + state.id = id + }, + SET_NAME: (state, name) => { + state.name = name + }, + SET_NICK_NAME: (state, nickName) =>{ + state.nickName = nickName + }, + SET_AVATAR: (state, avatar) => { + state.avatar = avatar + }, + SET_ROLES: (state, roles) => { + state.roles = roles + }, + SET_PERMISSIONS: (state, permissions) => { + state.permissions = permissions + } + }, + + actions: { + // 登录 + Login({ commit }, userInfo) { + const username = userInfo.username.trim() + const password = userInfo.password + const code = userInfo.code + const uuid = userInfo.uuid + return new Promise((resolve, reject) => { + login(username, password, code, uuid).then(res => { + setToken(res.token) + commit('SET_TOKEN', res.token) + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 获取用户信息 + GetInfo({ commit, state }) { + return new Promise((resolve, reject) => { + getInfo().then(res => { + const user = res.user + let avatar = user.avatar || "" + if (!isHttp(avatar)) { + avatar = (isEmpty(avatar)) ? defAva : process.env.VUE_APP_BASE_API + avatar + } + if (res.roles && res.roles.length > 0) { // 验证返回的roles是否是一个非空数组 + commit('SET_ROLES', res.roles) + commit('SET_PERMISSIONS', res.permissions) + } else { + commit('SET_ROLES', ['ROLE_DEFAULT']) + } + commit('SET_ID', user.userId) + commit('SET_NAME', user.userName) + commit('SET_NICK_NAME', user.nickName) + commit('SET_AVATAR', avatar) + resolve(res) + }).catch(error => { + reject(error) + }) + }) + }, + + // 退出系统 + LogOut({ commit, state }) { + return new Promise((resolve, reject) => { + logout(state.token).then(() => { + commit('SET_TOKEN', '') + commit('SET_ROLES', []) + commit('SET_PERMISSIONS', []) + removeToken() + resolve() + }).catch(error => { + reject(error) + }) + }) + }, + + // 前端 登出 + FedLogOut({ commit }) { + return new Promise(resolve => { + commit('SET_TOKEN', '') + removeToken() + resolve() + }) + } + } +} + +export default user diff --git a/ruoyi-ui/src/utils/auth.js b/ruoyi-ui/src/utils/auth.js new file mode 100644 index 0000000..08a43d6 --- /dev/null +++ b/ruoyi-ui/src/utils/auth.js @@ -0,0 +1,15 @@ +import Cookies from 'js-cookie' + +const TokenKey = 'Admin-Token' + +export function getToken() { + return Cookies.get(TokenKey) +} + +export function setToken(token) { + return Cookies.set(TokenKey, token) +} + +export function removeToken() { + return Cookies.remove(TokenKey) +} diff --git a/ruoyi-ui/src/utils/dict/Dict.js b/ruoyi-ui/src/utils/dict/Dict.js new file mode 100644 index 0000000..994f6f7 --- /dev/null +++ b/ruoyi-ui/src/utils/dict/Dict.js @@ -0,0 +1,82 @@ +import Vue from 'vue' +import { mergeRecursive } from "@/utils/ruoyi" +import DictMeta from './DictMeta' +import DictData from './DictData' + +const DEFAULT_DICT_OPTIONS = { + types: [], +} + +/** + * @classdesc 字典 + * @property {Object} label 标签对象,内部属性名为字典类型名称 + * @property {Object} dict 字段数组,内部属性名为字典类型名称 + * @property {Array.} _dictMetas 字典元数据数组 + */ +export default class Dict { + constructor() { + this.owner = null + this.label = {} + this.type = {} + } + + init(options) { + if (options instanceof Array) { + options = { types: options } + } + const opts = mergeRecursive(DEFAULT_DICT_OPTIONS, options) + if (opts.types === undefined) { + throw new Error('need dict types') + } + const ps = [] + this._dictMetas = opts.types.map(t => DictMeta.parse(t)) + this._dictMetas.forEach(dictMeta => { + const type = dictMeta.type + Vue.set(this.label, type, {}) + Vue.set(this.type, type, []) + if (dictMeta.lazy) { + return + } + ps.push(loadDict(this, dictMeta)) + }) + return Promise.all(ps) + } + + /** + * 重新加载字典 + * @param {String} type 字典类型 + */ + reloadDict(type) { + const dictMeta = this._dictMetas.find(e => e.type === type) + if (dictMeta === undefined) { + return Promise.reject(`the dict meta of ${type} was not found`) + } + return loadDict(this, dictMeta) + } +} + +/** + * 加载字典 + * @param {Dict} dict 字典 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {Promise} + */ +function loadDict(dict, dictMeta) { + return dictMeta.request(dictMeta) + .then(response => { + const type = dictMeta.type + let dicts = dictMeta.responseConverter(response, dictMeta) + if (!(dicts instanceof Array)) { + console.error('the return of responseConverter must be Array.') + dicts = [] + } else if (dicts.filter(d => d instanceof DictData).length !== dicts.length) { + console.error('the type of elements in dicts must be DictData') + dicts = [] + } + dict.type[type].splice(0, Number.MAX_SAFE_INTEGER, ...dicts) + dicts.forEach(d => { + Vue.set(dict.label[type], d.value, d.label) + }) + return dicts + }) +} diff --git a/ruoyi-ui/src/utils/dict/DictConverter.js b/ruoyi-ui/src/utils/dict/DictConverter.js new file mode 100644 index 0000000..0cf5df8 --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictConverter.js @@ -0,0 +1,17 @@ +import DictOptions from './DictOptions' +import DictData from './DictData' + +export default function(dict, dictMeta) { + const label = determineDictField(dict, dictMeta.labelField, ...DictOptions.DEFAULT_LABEL_FIELDS) + const value = determineDictField(dict, dictMeta.valueField, ...DictOptions.DEFAULT_VALUE_FIELDS) + return new DictData(dict[label], dict[value], dict) +} + +/** + * 确定字典字段 + * @param {DictData} dict + * @param {...String} fields + */ +function determineDictField(dict, ...fields) { + return fields.find(f => Object.prototype.hasOwnProperty.call(dict, f)) +} diff --git a/ruoyi-ui/src/utils/dict/DictData.js b/ruoyi-ui/src/utils/dict/DictData.js new file mode 100644 index 0000000..afc763e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictData.js @@ -0,0 +1,13 @@ +/** + * @classdesc 字典数据 + * @property {String} label 标签 + * @property {*} value 标签 + * @property {Object} raw 原始数据 + */ +export default class DictData { + constructor(label, value, raw) { + this.label = label + this.value = value + this.raw = raw + } +} diff --git a/ruoyi-ui/src/utils/dict/DictMeta.js b/ruoyi-ui/src/utils/dict/DictMeta.js new file mode 100644 index 0000000..617cc00 --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictMeta.js @@ -0,0 +1,38 @@ +import { mergeRecursive } from "@/utils/ruoyi" +import DictOptions from './DictOptions' + +/** + * @classdesc 字典元数据 + * @property {String} type 类型 + * @property {Function} request 请求 + * @property {String} label 标签字段 + * @property {String} value 值字段 + */ +export default class DictMeta { + constructor(options) { + this.type = options.type + this.request = options.request + this.responseConverter = options.responseConverter + this.labelField = options.labelField + this.valueField = options.valueField + this.lazy = options.lazy === true + } +} + + +/** + * 解析字典元数据 + * @param {Object} options + * @returns {DictMeta} + */ +DictMeta.parse= function(options) { + let opts = null + if (typeof options === 'string') { + opts = DictOptions.metas[options] || {} + opts.type = options + } else if (typeof options === 'object') { + opts = options + } + opts = mergeRecursive(DictOptions.metas['*'], opts) + return new DictMeta(opts) +} diff --git a/ruoyi-ui/src/utils/dict/DictOptions.js b/ruoyi-ui/src/utils/dict/DictOptions.js new file mode 100644 index 0000000..5fd1425 --- /dev/null +++ b/ruoyi-ui/src/utils/dict/DictOptions.js @@ -0,0 +1,51 @@ +import { mergeRecursive } from "@/utils/ruoyi" +import dictConverter from './DictConverter' + +export const options = { + metas: { + '*': { + /** + * 字典请求,方法签名为function(dictMeta: DictMeta): Promise + */ + request: (dictMeta) => { + console.log(`load dict ${dictMeta.type}`) + return Promise.resolve([]) + }, + /** + * 字典响应数据转换器,方法签名为function(response: Object, dictMeta: DictMeta): DictData + */ + responseConverter, + labelField: 'label', + valueField: 'value', + }, + }, + /** + * 默认标签字段 + */ + DEFAULT_LABEL_FIELDS: ['label', 'name', 'title'], + /** + * 默认值字段 + */ + DEFAULT_VALUE_FIELDS: ['value', 'id', 'uid', 'key'], +} + +/** + * 映射字典 + * @param {Object} response 字典数据 + * @param {DictMeta} dictMeta 字典元数据 + * @returns {DictData} + */ +function responseConverter(response, dictMeta) { + const dicts = response.content instanceof Array ? response.content : response + if (dicts === undefined) { + console.warn(`no dict data of "${dictMeta.type}" found in the response`) + return [] + } + return dicts.map(d => dictConverter(d, dictMeta)) +} + +export function mergeOptions(src) { + mergeRecursive(options, src) +} + +export default options diff --git a/ruoyi-ui/src/utils/dict/index.js b/ruoyi-ui/src/utils/dict/index.js new file mode 100644 index 0000000..215eb9e --- /dev/null +++ b/ruoyi-ui/src/utils/dict/index.js @@ -0,0 +1,33 @@ +import Dict from './Dict' +import { mergeOptions } from './DictOptions' + +export default function(Vue, options) { + mergeOptions(options) + Vue.mixin({ + data() { + if (this.$options === undefined || this.$options.dicts === undefined || this.$options.dicts === null) { + return {} + } + const dict = new Dict() + dict.owner = this + return { + dict + } + }, + created() { + if (!(this.dict instanceof Dict)) { + return + } + options.onCreated && options.onCreated(this.dict) + this.dict.init(this.$options.dicts).then(() => { + options.onReady && options.onReady(this.dict) + this.$nextTick(() => { + this.$emit('dictReady', this.dict) + if (this.$options.methods && this.$options.methods.onDictReady instanceof Function) { + this.$options.methods.onDictReady.call(this, this.dict) + } + }) + }) + }, + }) +} diff --git a/ruoyi-ui/src/utils/dynamicTitle.js b/ruoyi-ui/src/utils/dynamicTitle.js new file mode 100644 index 0000000..1b57efb --- /dev/null +++ b/ruoyi-ui/src/utils/dynamicTitle.js @@ -0,0 +1,13 @@ +import store from '@/store' +import defaultSettings from '@/settings' + +/** + * 动态修改标题 + */ +export function useDynamicTitle() { + if (store.state.settings.dynamicTitle) { + document.title = store.state.settings.title + ' - ' + defaultSettings.title + } else { + document.title = defaultSettings.title + } +} \ No newline at end of file diff --git a/ruoyi-ui/src/utils/errorCode.js b/ruoyi-ui/src/utils/errorCode.js new file mode 100644 index 0000000..d2111ee --- /dev/null +++ b/ruoyi-ui/src/utils/errorCode.js @@ -0,0 +1,6 @@ +export default { + '401': '认证失败,无法访问系统资源', + '403': '当前操作没有权限', + '404': '访问资源不存在', + 'default': '系统未知错误,请反馈给管理员' +} diff --git a/ruoyi-ui/src/utils/generator/config.js b/ruoyi-ui/src/utils/generator/config.js new file mode 100644 index 0000000..7abf227 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/config.js @@ -0,0 +1,438 @@ +export const formConf = { + formRef: 'elForm', + formModel: 'formData', + size: 'medium', + labelPosition: 'right', + labelWidth: 100, + formRules: 'rules', + gutter: 15, + disabled: false, + span: 24, + formBtns: true +} + +export const inputComponents = [ + { + label: '单行文本', + tag: 'el-input', + tagIcon: 'input', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '多行文本', + tag: 'el-input', + tagIcon: 'textarea', + type: 'textarea', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + labelWidth: null, + autosize: { + minRows: 4, + maxRows: 4 + }, + style: { width: '100%' }, + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '密码', + tag: 'el-input', + tagIcon: 'password', + placeholder: '请输入', + defaultValue: undefined, + span: 24, + 'show-password': true, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': '', + 'suffix-icon': '', + maxlength: null, + 'show-word-limit': false, + readonly: false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input' + }, + { + label: '计数器', + tag: 'el-input-number', + tagIcon: 'number', + placeholder: '', + defaultValue: undefined, + span: 24, + labelWidth: null, + min: undefined, + max: undefined, + step: undefined, + 'step-strictly': false, + precision: undefined, + 'controls-position': '', + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/input-number' + } +] + +export const selectComponents = [ + { + label: '下拉选择', + tag: 'el-select', + tagIcon: 'select', + placeholder: '请选择', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: { width: '100%' }, + clearable: true, + disabled: false, + required: true, + filterable: false, + multiple: false, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/select' + }, + { + label: '级联选择', + tag: 'el-cascader', + tagIcon: 'cascader', + placeholder: '请选择', + defaultValue: [], + span: 24, + labelWidth: null, + style: { width: '100%' }, + props: { + props: { + multiple: false + } + }, + 'show-all-levels': true, + disabled: false, + clearable: true, + filterable: false, + required: true, + options: [{ + id: 1, + value: 1, + label: '选项1', + children: [{ + id: 2, + value: 2, + label: '选项1-1' + }] + }], + dataType: 'dynamic', + labelKey: 'label', + valueKey: 'value', + childrenKey: 'children', + separator: '/', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/cascader' + }, + { + label: '单选框组', + tag: 'el-radio-group', + tagIcon: 'radio', + defaultValue: undefined, + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/radio' + }, + { + label: '多选框组', + tag: 'el-checkbox-group', + tagIcon: 'checkbox', + defaultValue: [], + span: 24, + labelWidth: null, + style: {}, + optionType: 'default', + border: false, + size: 'medium', + disabled: false, + required: true, + options: [{ + label: '选项一', + value: 1 + }, { + label: '选项二', + value: 2 + }], + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/checkbox' + }, + { + label: '开关', + tag: 'el-switch', + tagIcon: 'switch', + defaultValue: false, + span: 24, + labelWidth: null, + style: {}, + disabled: false, + required: true, + 'active-text': '', + 'inactive-text': '', + 'active-color': null, + 'inactive-color': null, + 'active-value': true, + 'inactive-value': false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/switch' + }, + { + label: '滑块', + tag: 'el-slider', + tagIcon: 'slider', + defaultValue: null, + span: 24, + labelWidth: null, + disabled: false, + required: true, + min: 0, + max: 100, + step: 1, + 'show-stops': false, + range: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/slider' + }, + { + label: '时间选择', + tag: 'el-time-picker', + tagIcon: 'time', + placeholder: '请选择', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'picker-options': { + selectableRange: '00:00:00-23:59:59' + }, + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '时间范围', + tag: 'el-time-picker', + tagIcon: 'time-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + 'is-range': true, + 'range-separator': '至', + 'start-placeholder': '开始时间', + 'end-placeholder': '结束时间', + format: 'HH:mm:ss', + 'value-format': 'HH:mm:ss', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/time-picker' + }, + { + label: '日期选择', + tag: 'el-date-picker', + tagIcon: 'date', + placeholder: '请选择', + defaultValue: null, + type: 'date', + span: 24, + labelWidth: null, + style: { width: '100%' }, + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '日期范围', + tag: 'el-date-picker', + tagIcon: 'date-range', + defaultValue: null, + span: 24, + labelWidth: null, + style: { width: '100%' }, + type: 'daterange', + 'range-separator': '至', + 'start-placeholder': '开始日期', + 'end-placeholder': '结束日期', + disabled: false, + clearable: true, + required: true, + format: 'yyyy-MM-dd', + 'value-format': 'yyyy-MM-dd', + readonly: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/date-picker' + }, + { + label: '评分', + tag: 'el-rate', + tagIcon: 'rate', + defaultValue: 0, + span: 24, + labelWidth: null, + style: {}, + max: 5, + 'allow-half': false, + 'show-text': false, + 'show-score': false, + disabled: false, + required: true, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/rate' + }, + { + label: '颜色选择', + tag: 'el-color-picker', + tagIcon: 'color', + defaultValue: null, + labelWidth: null, + 'show-alpha': false, + 'color-format': '', + disabled: false, + required: true, + size: 'medium', + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/color-picker' + }, + { + label: '上传', + tag: 'el-upload', + tagIcon: 'upload', + action: 'https://jsonplaceholder.typicode.com/posts/', + defaultValue: null, + labelWidth: null, + disabled: false, + required: true, + accept: '', + name: 'file', + 'auto-upload': true, + showTip: false, + buttonText: '点击上传', + fileSize: 2, + sizeUnit: 'MB', + 'list-type': 'text', + multiple: false, + regList: [], + changeTag: true, + document: 'https://element.eleme.cn/#/zh-CN/component/upload' + } +] + +export const layoutComponents = [ + { + layout: 'rowFormItem', + tagIcon: 'row', + type: 'default', + justify: 'start', + align: 'top', + label: '行容器', + layoutTree: true, + children: [], + document: 'https://element.eleme.cn/#/zh-CN/component/layout' + }, + { + layout: 'colFormItem', + label: '按钮', + changeTag: true, + labelWidth: null, + tag: 'el-button', + tagIcon: 'button', + span: 24, + default: '主要按钮', + type: 'primary', + icon: 'el-icon-search', + size: 'medium', + disabled: false, + document: 'https://element.eleme.cn/#/zh-CN/component/button' + } +] + +// 组件rule的触发方式,无触发方式的组件不生成rule +export const trigger = { + 'el-input': 'blur', + 'el-input-number': 'blur', + 'el-select': 'change', + 'el-radio-group': 'change', + 'el-checkbox-group': 'change', + 'el-cascader': 'change', + 'el-time-picker': 'change', + 'el-date-picker': 'change', + 'el-rate': 'change' +} diff --git a/ruoyi-ui/src/utils/generator/css.js b/ruoyi-ui/src/utils/generator/css.js new file mode 100644 index 0000000..c1c62e6 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/css.js @@ -0,0 +1,18 @@ +const styles = { + 'el-rate': '.el-rate{display: inline-block; vertical-align: text-top;}', + 'el-upload': '.el-upload__tip{line-height: 1.2;}' +} + +function addCss(cssList, el) { + const css = styles[el.tag] + css && cssList.indexOf(css) === -1 && cssList.push(css) + if (el.children) { + el.children.forEach(el2 => addCss(cssList, el2)) + } +} + +export function makeUpCss(conf) { + const cssList = [] + conf.fields.forEach(el => addCss(cssList, el)) + return cssList.join('\n') +} diff --git a/ruoyi-ui/src/utils/generator/drawingDefault.js b/ruoyi-ui/src/utils/generator/drawingDefault.js new file mode 100644 index 0000000..09f133c --- /dev/null +++ b/ruoyi-ui/src/utils/generator/drawingDefault.js @@ -0,0 +1,29 @@ +export default [ + { + layout: 'colFormItem', + tagIcon: 'input', + label: '手机号', + vModel: 'mobile', + formId: 6, + tag: 'el-input', + placeholder: '请输入手机号', + defaultValue: '', + span: 24, + style: { width: '100%' }, + clearable: true, + prepend: '', + append: '', + 'prefix-icon': 'el-icon-mobile', + 'suffix-icon': '', + maxlength: 11, + 'show-word-limit': true, + readonly: false, + disabled: false, + required: true, + changeTag: true, + regList: [{ + pattern: '/^1(3|4|5|7|8|9)\\d{9}$/', + message: '手机号格式错误' + }] + } +] diff --git a/ruoyi-ui/src/utils/generator/html.js b/ruoyi-ui/src/utils/generator/html.js new file mode 100644 index 0000000..3b3388d --- /dev/null +++ b/ruoyi-ui/src/utils/generator/html.js @@ -0,0 +1,358 @@ +import { trigger } from './config' + +let confGlobal +let someSpanIsNot24 + +export function dialogWrapper(str) { + return ` + ${str} +
+ 取消 + 确定 +
+
` +} + +export function vueTemplate(str) { + return `` +} + +export function vueScript(str) { + return `` +} + +export function cssStyle(cssStr) { + return `` +} + +function buildFormTemplate(conf, child, type) { + let labelPosition = '' + if (conf.labelPosition !== 'right') { + labelPosition = `label-position="${conf.labelPosition}"` + } + const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : '' + let str = ` + ${child} + ${buildFromBtns(conf, type)} + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + return str +} + +function buildFromBtns(conf, type) { + let str = '' + if (conf.formBtns && type === 'file') { + str = ` + 提交 + 重置 + ` + if (someSpanIsNot24) { + str = ` + ${str} + ` + } + } + return str +} + +// span不为24的用el-col包裹 +function colWrapper(element, str) { + if (someSpanIsNot24 || element.span !== 24) { + return ` + ${str} + ` + } + return str +} + +const layouts = { + colFormItem(element) { + let labelWidth = '' + if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) { + labelWidth = `label-width="${element.labelWidth}px"` + } + const required = !trigger[element.tag] && element.required ? 'required' : '' + const tagDom = tags[element.tag] ? tags[element.tag](element) : null + let str = ` + ${tagDom} + ` + str = colWrapper(element, str) + return str + }, + rowFormItem(element) { + const type = element.type === 'default' ? '' : `type="${element.type}"` + const justify = element.type === 'default' ? '' : `justify="${element.justify}"` + const align = element.type === 'default' ? '' : `align="${element.align}"` + const gutter = element.gutter ? `gutter="${element.gutter}"` : '' + const children = element.children.map(el => layouts[el.layout](el)) + let str = ` + ${children.join('\n')} + ` + str = colWrapper(element, str) + return str + } +} + +const tags = { + 'el-button': el => { + const { + tag, disabled + } = attrBuilder(el) + const type = el.type ? `type="${el.type}"` : '' + const icon = el.icon ? `icon="${el.icon}"` : '' + const size = el.size ? `size="${el.size}"` : '' + let child = buildElButtonChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${type} ${icon} ${size} ${disabled}>${child}` + }, + 'el-input': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : '' + const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : '' + const readonly = el.readonly ? 'readonly' : '' + const prefixIcon = el['prefix-icon'] ? `prefix-icon='${el['prefix-icon']}'` : '' + const suffixIcon = el['suffix-icon'] ? `suffix-icon='${el['suffix-icon']}'` : '' + const showPassword = el['show-password'] ? 'show-password' : '' + const type = el.type ? `type="${el.type}"` : '' + const autosize = el.autosize && el.autosize.minRows + ? `:autosize="{minRows: ${el.autosize.minRows}, maxRows: ${el.autosize.maxRows}}"` + : '' + let child = buildElInputChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}` + }, + 'el-input-number': el => { + const { disabled, vModel, placeholder } = attrBuilder(el) + const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : '' + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const stepStrictly = el['step-strictly'] ? 'step-strictly' : '' + const precision = el.precision ? `:precision='${el.precision}'` : '' + + return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}>` + }, + 'el-select': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const filterable = el.filterable ? 'filterable' : '' + const multiple = el.multiple ? 'multiple' : '' + let child = buildElSelectChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}` + }, + 'el-radio-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + let child = buildElRadioGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${size} ${disabled}>${child}` + }, + 'el-checkbox-group': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const min = el.min ? `:min="${el.min}"` : '' + const max = el.max ? `:max="${el.max}"` : '' + let child = buildElCheckboxGroupChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}` + }, + 'el-switch': el => { + const { disabled, vModel } = attrBuilder(el) + const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : '' + const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : '' + const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : '' + const inactiveColor = el['inactive-color'] ? `inactive-color="${el['inactive-color']}"` : '' + const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : '' + const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : '' + + return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}>` + }, + 'el-cascader': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const options = el.options ? `:options="${el.vModel}Options"` : '' + const props = el.props ? `:props="${el.vModel}Props"` : '' + const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"' + const filterable = el.filterable ? 'filterable' : '' + const separator = el.separator === '/' ? '' : `separator="${el.separator}"` + + return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}>` + }, + 'el-slider': el => { + const { disabled, vModel } = attrBuilder(el) + const min = el.min ? `:min='${el.min}'` : '' + const max = el.max ? `:max='${el.max}'` : '' + const step = el.step ? `:step='${el.step}'` : '' + const range = el.range ? 'range' : '' + const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : '' + + return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}>` + }, + 'el-time-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const isRange = el['is-range'] ? 'is-range' : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : '' + + return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}>` + }, + 'el-date-picker': el => { + const { + disabled, vModel, clearable, placeholder, width + } = attrBuilder(el) + const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : '' + const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : '' + const rangeSeparator = el['range-separator'] ? `range-separator="${el['range-separator']}"` : '' + const format = el.format ? `format="${el.format}"` : '' + const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : '' + const type = el.type === 'date' ? '' : `type="${el.type}"` + const readonly = el.readonly ? 'readonly' : '' + + return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}>` + }, + 'el-rate': el => { + const { disabled, vModel } = attrBuilder(el) + const max = el.max ? `:max='${el.max}'` : '' + const allowHalf = el['allow-half'] ? 'allow-half' : '' + const showText = el['show-text'] ? 'show-text' : '' + const showScore = el['show-score'] ? 'show-score' : '' + + return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}>` + }, + 'el-color-picker': el => { + const { disabled, vModel } = attrBuilder(el) + const size = `size="${el.size}"` + const showAlpha = el['show-alpha'] ? 'show-alpha' : '' + const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : '' + + return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}>` + }, + 'el-upload': el => { + const disabled = el.disabled ? ':disabled=\'true\'' : '' + const action = el.action ? `:action="${el.vModel}Action"` : '' + const multiple = el.multiple ? 'multiple' : '' + const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : '' + const accept = el.accept ? `accept="${el.accept}"` : '' + const name = el.name !== 'file' ? `name="${el.name}"` : '' + const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : '' + const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"` + const fileList = `:file-list="${el.vModel}fileList"` + const ref = `ref="${el.vModel}"` + let child = buildElUploadChild(el) + + if (child) child = `\n${child}\n` // 换行 + return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}` + } +} + +function attrBuilder(el) { + return { + vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`, + clearable: el.clearable ? 'clearable' : '', + placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '', + width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '', + disabled: el.disabled ? ':disabled=\'true\'' : '' + } +} + +// el-buttin 子级 +function buildElButtonChild(conf) { + const children = [] + if (conf.default) { + children.push(conf.default) + } + return children.join('\n') +} + +// el-input innerHTML +function buildElInputChild(conf) { + const children = [] + if (conf.prepend) { + children.push(``) + } + if (conf.append) { + children.push(``) + } + return children.join('\n') +} + +function buildElSelectChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + children.push(``) + } + return children.join('\n') +} + +function buildElRadioGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElCheckboxGroupChild(conf) { + const children = [] + if (conf.options && conf.options.length) { + const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox' + const border = conf.border ? 'border' : '' + children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}`) + } + return children.join('\n') +} + +function buildElUploadChild(conf) { + const list = [] + if (conf['list-type'] === 'picture-card') list.push('') + else list.push(`${conf.buttonText}`) + if (conf.showTip) list.push(`
只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件
`) + return list.join('\n') +} + +export function makeUpHtml(conf, type) { + const htmlList = [] + confGlobal = conf + someSpanIsNot24 = conf.fields.some(item => item.span !== 24) + conf.fields.forEach(el => { + htmlList.push(layouts[el.layout](el)) + }) + const htmlStr = htmlList.join('\n') + + let temp = buildFormTemplate(conf, htmlStr, type) + if (type === 'dialog') { + temp = dialogWrapper(temp) + } + confGlobal = null + return temp +} diff --git a/ruoyi-ui/src/utils/generator/icon.json b/ruoyi-ui/src/utils/generator/icon.json new file mode 100644 index 0000000..2d9999a --- /dev/null +++ b/ruoyi-ui/src/utils/generator/icon.json @@ -0,0 +1 @@ +["platform-eleme","eleme","delete-solid","delete","s-tools","setting","user-solid","user","phone","phone-outline","more","more-outline","star-on","star-off","s-goods","goods","warning","warning-outline","question","info","remove","circle-plus","success","error","zoom-in","zoom-out","remove-outline","circle-plus-outline","circle-check","circle-close","s-help","help","minus","plus","check","close","picture","picture-outline","picture-outline-round","upload","upload2","download","camera-solid","camera","video-camera-solid","video-camera","message-solid","bell","s-cooperation","s-order","s-platform","s-fold","s-unfold","s-operation","s-promotion","s-home","s-release","s-ticket","s-management","s-open","s-shop","s-marketing","s-flag","s-comment","s-finance","s-claim","s-custom","s-opportunity","s-data","s-check","s-grid","menu","share","d-caret","caret-left","caret-right","caret-bottom","caret-top","bottom-left","bottom-right","back","right","bottom","top","top-left","top-right","arrow-left","arrow-right","arrow-down","arrow-up","d-arrow-left","d-arrow-right","video-pause","video-play","refresh","refresh-right","refresh-left","finished","sort","sort-up","sort-down","rank","loading","view","c-scale-to-original","date","edit","edit-outline","folder","folder-opened","folder-add","folder-remove","folder-delete","folder-checked","tickets","document-remove","document-delete","document-copy","document-checked","document","document-add","printer","paperclip","takeaway-box","search","monitor","attract","mobile","scissors","umbrella","headset","brush","mouse","coordinate","magic-stick","reading","data-line","data-board","pie-chart","data-analysis","collection-tag","film","suitcase","suitcase-1","receiving","collection","files","notebook-1","notebook-2","toilet-paper","office-building","school","table-lamp","house","no-smoking","smoking","shopping-cart-full","shopping-cart-1","shopping-cart-2","shopping-bag-1","shopping-bag-2","sold-out","sell","present","box","bank-card","money","coin","wallet","discount","price-tag","news","guide","male","female","thumb","cpu","link","connection","open","turn-off","set-up","chat-round","chat-line-round","chat-square","chat-dot-round","chat-dot-square","chat-line-square","message","postcard","position","turn-off-microphone","microphone","close-notification","bangzhu","time","odometer","crop","aim","switch-button","full-screen","copy-document","mic","stopwatch","medal-1","medal","trophy","trophy-1","first-aid-kit","discover","place","location","location-outline","location-information","add-location","delete-location","map-location","alarm-clock","timer","watch-1","watch","lock","unlock","key","service","mobile-phone","bicycle","truck","ship","basketball","football","soccer","baseball","wind-power","light-rain","lightning","heavy-rain","sunrise","sunrise-1","sunset","sunny","cloudy","partly-cloudy","cloudy-and-sunny","moon","moon-night","dish","dish-1","food","chicken","fork-spoon","knife-fork","burger","tableware","sugar","dessert","ice-cream","hot-water","water-cup","coffee-cup","cold-drink","goblet","goblet-full","goblet-square","goblet-square-full","refrigerator","grape","watermelon","cherry","apple","pear","orange","coffee","ice-tea","ice-drink","milk-tea","potato-strips","lollipop","ice-cream-square","ice-cream-round"] \ No newline at end of file diff --git a/ruoyi-ui/src/utils/generator/js.js b/ruoyi-ui/src/utils/generator/js.js new file mode 100644 index 0000000..ee8668d --- /dev/null +++ b/ruoyi-ui/src/utils/generator/js.js @@ -0,0 +1,235 @@ +import { exportDefault, titleCase } from '@/utils/index' +import { trigger } from './config' + +const units = { + KB: '1024', + MB: '1024 / 1024', + GB: '1024 / 1024 / 1024' +} +let confGlobal +const inheritAttrs = { + file: '', + dialog: 'inheritAttrs: false,' +} + + +export function makeUpJs(conf, type) { + confGlobal = conf = JSON.parse(JSON.stringify(conf)) + const dataList = [] + const ruleList = [] + const optionsList = [] + const propsList = [] + const methodList = mixinMethod(type) + const uploadVarList = [] + + conf.fields.forEach(el => { + buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + + const script = buildexport( + conf, + type, + dataList.join('\n'), + ruleList.join('\n'), + optionsList.join('\n'), + uploadVarList.join('\n'), + propsList.join('\n'), + methodList.join('\n') + ) + confGlobal = null + return script +} + +function buildAttributes(el, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) { + buildData(el, dataList) + buildRules(el, ruleList) + + if (el.options && el.options.length) { + buildOptions(el, optionsList) + if (el.dataType === 'dynamic') { + const model = `${el.vModel}Options` + const options = titleCase(model) + buildOptionMethod(`get${options}`, model, methodList) + } + } + + if (el.props && el.props.props) { + buildProps(el, propsList) + } + + if (el.action && el.tag === 'el-upload') { + uploadVarList.push( + `${el.vModel}Action: '${el.action}', + ${el.vModel}fileList: [],` + ) + methodList.push(buildBeforeUpload(el)) + if (!el['auto-upload']) { + methodList.push(buildSubmitUpload(el)) + } + } + + if (el.children) { + el.children.forEach(el2 => { + buildAttributes(el2, dataList, ruleList, optionsList, methodList, propsList, uploadVarList) + }) + } +} + +function mixinMethod(type) { + const list = []; const + minxins = { + file: confGlobal.formBtns ? { + submitForm: `submitForm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + // TODO 提交表单 + }) + },`, + resetForm: `resetForm() { + this.$refs['${confGlobal.formRef}'].resetFields() + },` + } : null, + dialog: { + onOpen: 'onOpen() {},', + onClose: `onClose() { + this.$refs['${confGlobal.formRef}'].resetFields() + },`, + close: `close() { + this.$emit('update:visible', false) + },`, + handleConfirm: `handleConfirm() { + this.$refs['${confGlobal.formRef}'].validate(valid => { + if(!valid) return + this.close() + }) + },` + } + } + + const methods = minxins[type] + if (methods) { + Object.keys(methods).forEach(key => { + list.push(methods[key]) + }) + } + + return list +} + +function buildData(conf, dataList) { + if (conf.vModel === undefined) return + let defaultValue + if (typeof (conf.defaultValue) === 'string' && !conf.multiple) { + defaultValue = `'${conf.defaultValue}'` + } else { + defaultValue = `${JSON.stringify(conf.defaultValue)}` + } + dataList.push(`${conf.vModel}: ${defaultValue},`) +} + +function buildRules(conf, ruleList) { + if (conf.vModel === undefined) return + const rules = [] + if (trigger[conf.tag]) { + if (conf.required) { + const type = Array.isArray(conf.defaultValue) ? 'type: \'array\',' : '' + let message = Array.isArray(conf.defaultValue) ? `请至少选择一个${conf.vModel}` : conf.placeholder + if (message === undefined) message = `${conf.label}不能为空` + rules.push(`{ required: true, ${type} message: '${message}', trigger: '${trigger[conf.tag]}' }`) + } + if (conf.regList && Array.isArray(conf.regList)) { + conf.regList.forEach(item => { + if (item.pattern) { + rules.push(`{ pattern: ${eval(item.pattern)}, message: '${item.message}', trigger: '${trigger[conf.tag]}' }`) + } + }) + } + ruleList.push(`${conf.vModel}: [${rules.join(',')}],`) + } +} + +function buildOptions(conf, optionsList) { + if (conf.vModel === undefined) return + if (conf.dataType === 'dynamic') { conf.options = [] } + const str = `${conf.vModel}Options: ${JSON.stringify(conf.options)},` + optionsList.push(str) +} + +function buildProps(conf, propsList) { + if (conf.dataType === 'dynamic') { + conf.valueKey !== 'value' && (conf.props.props.value = conf.valueKey) + conf.labelKey !== 'label' && (conf.props.props.label = conf.labelKey) + conf.childrenKey !== 'children' && (conf.props.props.children = conf.childrenKey) + } + const str = `${conf.vModel}Props: ${JSON.stringify(conf.props.props)},` + propsList.push(str) +} + +function buildBeforeUpload(conf) { + const unitNum = units[conf.sizeUnit]; let rightSizeCode = ''; let acceptCode = ''; const + returnList = [] + if (conf.fileSize) { + rightSizeCode = `let isRightSize = file.size / ${unitNum} < ${conf.fileSize} + if(!isRightSize){ + this.$message.error('文件大小超过 ${conf.fileSize}${conf.sizeUnit}') + }` + returnList.push('isRightSize') + } + if (conf.accept) { + acceptCode = `let isAccept = new RegExp('${conf.accept}').test(file.type) + if(!isAccept){ + this.$message.error('应该选择${conf.accept}类型的文件') + }` + returnList.push('isAccept') + } + const str = `${conf.vModel}BeforeUpload(file) { + ${rightSizeCode} + ${acceptCode} + return ${returnList.join('&&')} + },` + return returnList.length ? str : '' +} + +function buildSubmitUpload(conf) { + const str = `submitUpload() { + this.$refs['${conf.vModel}'].submit() + },` + return str +} + +function buildOptionMethod(methodName, model, methodList) { + const str = `${methodName}() { + // TODO 发起请求获取数据 + this.${model} + },` + methodList.push(str) +} + +function buildexport(conf, type, data, rules, selectOptions, uploadVar, props, methods) { + const str = `${exportDefault}{ + ${inheritAttrs[type]} + components: {}, + props: [], + data () { + return { + ${conf.formModel}: { + ${data} + }, + ${conf.formRules}: { + ${rules} + }, + ${uploadVar} + ${selectOptions} + ${props} + } + }, + computed: {}, + watch: {}, + created () {}, + mounted () {}, + methods: { + ${methods} + } +}` + return str +} diff --git a/ruoyi-ui/src/utils/generator/render.js b/ruoyi-ui/src/utils/generator/render.js new file mode 100644 index 0000000..e8640f0 --- /dev/null +++ b/ruoyi-ui/src/utils/generator/render.js @@ -0,0 +1,126 @@ +import { makeMap } from '@/utils/index' + +// 参考https://github.com/vuejs/vue/blob/v2.6.10/src/platforms/web/server/util.js +const isAttr = makeMap( + 'accept,accept-charset,accesskey,action,align,alt,async,autocomplete,' + + 'autofocus,autoplay,autosave,bgcolor,border,buffered,challenge,charset,' + + 'checked,cite,class,code,codebase,color,cols,colspan,content,http-equiv,' + + 'name,contenteditable,contextmenu,controls,coords,data,datetime,default,' + + 'defer,dir,dirname,disabled,download,draggable,dropzone,enctype,method,for,' + + 'form,formaction,headers,height,hidden,high,href,hreflang,http-equiv,' + + 'icon,id,ismap,itemprop,keytype,kind,label,lang,language,list,loop,low,' + + 'manifest,max,maxlength,media,method,GET,POST,min,multiple,email,file,' + + 'muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,' + + 'preload,radiogroup,readonly,rel,required,reversed,rows,rowspan,sandbox,' + + 'scope,scoped,seamless,selected,shape,size,type,text,password,sizes,span,' + + 'spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,' + + 'target,title,type,usemap,value,width,wrap' +) + +function vModel(self, dataObject, defaultValue) { + dataObject.props.value = defaultValue + + dataObject.on.input = val => { + self.$emit('input', val) + } +} + +const componentChild = { + 'el-button': { + default(h, conf, key) { + return conf[key] + }, + }, + 'el-input': { + prepend(h, conf, key) { + return + }, + append(h, conf, key) { + return + } + }, + 'el-select': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + list.push() + }) + return list + } + }, + 'el-radio-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') list.push({item.label}) + else list.push({item.label}) + }) + return list + } + }, + 'el-checkbox-group': { + options(h, conf, key) { + const list = [] + conf.options.forEach(item => { + if (conf.optionType === 'button') { + list.push({item.label}) + } else { + list.push({item.label}) + } + }) + return list + } + }, + 'el-upload': { + 'list-type': (h, conf, key) => { + const list = [] + if (conf['list-type'] === 'picture-card') { + list.push() + } else { + list.push({conf.buttonText}) + } + if (conf.showTip) { + list.push(
只能上传不超过 {conf.fileSize}{conf.sizeUnit} 的{conf.accept}文件
) + } + return list + } + } +} + +export default { + render(h) { + const dataObject = { + attrs: {}, + props: {}, + on: {}, + style: {} + } + const confClone = JSON.parse(JSON.stringify(this.conf)) + const children = [] + + const childObjs = componentChild[confClone.tag] + if (childObjs) { + Object.keys(childObjs).forEach(key => { + const childFunc = childObjs[key] + if (confClone[key]) { + children.push(childFunc(h, confClone, key)) + } + }) + } + + Object.keys(confClone).forEach(key => { + const val = confClone[key] + if (key === 'vModel') { + vModel(this, dataObject, confClone.defaultValue) + } else if (dataObject[key]) { + dataObject[key] = val + } else if (!isAttr(key)) { + dataObject.props[key] = val + } else { + dataObject.attrs[key] = val + } + }) + return h(this.conf.tag, dataObject, children) + }, + props: ['conf'] +} diff --git a/ruoyi-ui/src/utils/index.js b/ruoyi-ui/src/utils/index.js new file mode 100644 index 0000000..9375db7 --- /dev/null +++ b/ruoyi-ui/src/utils/index.js @@ -0,0 +1,390 @@ +import { parseTime } from './ruoyi' + +/** + * 表格时间格式化 + */ +export function formatDate(cellValue) { + if (cellValue == null || cellValue == "") return "" + var date = new Date(cellValue) + var year = date.getFullYear() + var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1 + var day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate() + var hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours() + var minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes() + var seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() + return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds +} + +/** + * @param {number} time + * @param {string} option + * @returns {string} + */ +export function formatTime(time, option) { + if (('' + time).length === 10) { + time = parseInt(time) * 1000 + } else { + time = +time + } + const d = new Date(time) + const now = Date.now() + + const diff = (now - d) / 1000 + + if (diff < 30) { + return '刚刚' + } else if (diff < 3600) { + // less 1 hour + return Math.ceil(diff / 60) + '分钟前' + } else if (diff < 3600 * 24) { + return Math.ceil(diff / 3600) + '小时前' + } else if (diff < 3600 * 24 * 2) { + return '1天前' + } + if (option) { + return parseTime(time, option) + } else { + return ( + d.getMonth() + + 1 + + '月' + + d.getDate() + + '日' + + d.getHours() + + '时' + + d.getMinutes() + + '分' + ) + } +} + +/** + * @param {string} url + * @returns {Object} + */ +export function getQueryObject(url) { + url = url == null ? window.location.href : url + const search = url.substring(url.lastIndexOf('?') + 1) + const obj = {} + const reg = /([^?&=]+)=([^?&=]*)/g + search.replace(reg, (rs, $1, $2) => { + const name = decodeURIComponent($1) + let val = decodeURIComponent($2) + val = String(val) + obj[name] = val + return rs + }) + return obj +} + +/** + * @param {string} input value + * @returns {number} output value + */ +export function byteLength(str) { + // returns the byte length of an utf8 string + let s = str.length + for (var i = str.length - 1; i >= 0; i--) { + const code = str.charCodeAt(i) + if (code > 0x7f && code <= 0x7ff) s++ + else if (code > 0x7ff && code <= 0xffff) s += 2 + if (code >= 0xDC00 && code <= 0xDFFF) i-- + } + return s +} + +/** + * @param {Array} actual + * @returns {Array} + */ +export function cleanArray(actual) { + const newArray = [] + for (let i = 0; i < actual.length; i++) { + if (actual[i]) { + newArray.push(actual[i]) + } + } + return newArray +} + +/** + * @param {Object} json + * @returns {Array} + */ +export function param(json) { + if (!json) return '' + return cleanArray( + Object.keys(json).map(key => { + if (json[key] === undefined) return '' + return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) + }) + ).join('&') +} + +/** + * @param {string} url + * @returns {Object} + */ +export function param2Obj(url) { + const search = decodeURIComponent(url.split('?')[1]).replace(/\+/g, ' ') + if (!search) { + return {} + } + const obj = {} + const searchArr = search.split('&') + searchArr.forEach(v => { + const index = v.indexOf('=') + if (index !== -1) { + const name = v.substring(0, index) + const val = v.substring(index + 1, v.length) + obj[name] = val + } + }) + return obj +} + +/** + * @param {string} val + * @returns {string} + */ +export function html2Text(val) { + const div = document.createElement('div') + div.innerHTML = val + return div.textContent || div.innerText +} + +/** + * Merges two objects, giving the last one precedence + * @param {Object} target + * @param {(Object|Array)} source + * @returns {Object} + */ +export function objectMerge(target, source) { + if (typeof target !== 'object') { + target = {} + } + if (Array.isArray(source)) { + return source.slice() + } + Object.keys(source).forEach(property => { + const sourceProperty = source[property] + if (typeof sourceProperty === 'object') { + target[property] = objectMerge(target[property], sourceProperty) + } else { + target[property] = sourceProperty + } + }) + return target +} + +/** + * @param {HTMLElement} element + * @param {string} className + */ +export function toggleClass(element, className) { + if (!element || !className) { + return + } + let classString = element.className + const nameIndex = classString.indexOf(className) + if (nameIndex === -1) { + classString += '' + className + } else { + classString = + classString.substr(0, nameIndex) + + classString.substr(nameIndex + className.length) + } + element.className = classString +} + +/** + * @param {string} type + * @returns {Date} + */ +export function getTime(type) { + if (type === 'start') { + return new Date().getTime() - 3600 * 1000 * 24 * 90 + } else { + return new Date(new Date().toDateString()) + } +} + +/** + * @param {Function} func + * @param {number} wait + * @param {boolean} immediate + * @return {*} + */ +export function debounce(func, wait, immediate) { + let timeout, args, context, timestamp, result + + const later = function() { + // 据上一次触发时间间隔 + const last = +new Date() - timestamp + + // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait + if (last < wait && last > 0) { + timeout = setTimeout(later, wait - last) + } else { + timeout = null + // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 + if (!immediate) { + result = func.apply(context, args) + if (!timeout) context = args = null + } + } + } + + return function(...args) { + context = this + timestamp = +new Date() + const callNow = immediate && !timeout + // 如果延时不存在,重新设定延时 + if (!timeout) timeout = setTimeout(later, wait) + if (callNow) { + result = func.apply(context, args) + context = args = null + } + + return result + } +} + +/** + * This is just a simple version of deep copy + * Has a lot of edge cases bug + * If you want to use a perfect deep copy, use lodash's _.cloneDeep + * @param {Object} source + * @returns {Object} + */ +export function deepClone(source) { + if (!source && typeof source !== 'object') { + throw new Error('error arguments', 'deepClone') + } + const targetObj = source.constructor === Array ? [] : {} + Object.keys(source).forEach(keys => { + if (source[keys] && typeof source[keys] === 'object') { + targetObj[keys] = deepClone(source[keys]) + } else { + targetObj[keys] = source[keys] + } + }) + return targetObj +} + +/** + * @param {Array} arr + * @returns {Array} + */ +export function uniqueArr(arr) { + return Array.from(new Set(arr)) +} + +/** + * @returns {string} + */ +export function createUniqueString() { + const timestamp = +new Date() + '' + const randomNum = parseInt((1 + Math.random()) * 65536) + '' + return (+(randomNum + timestamp)).toString(32) +} + +/** + * Check if an element has a class + * @param {HTMLElement} elm + * @param {string} cls + * @returns {boolean} + */ +export function hasClass(ele, cls) { + return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) +} + +/** + * Add class to element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function addClass(ele, cls) { + if (!hasClass(ele, cls)) ele.className += ' ' + cls +} + +/** + * Remove class from element + * @param {HTMLElement} elm + * @param {string} cls + */ +export function removeClass(ele, cls) { + if (hasClass(ele, cls)) { + const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') + ele.className = ele.className.replace(reg, ' ') + } +} + +export function makeMap(str, expectsLowerCase) { + const map = Object.create(null) + const list = str.split(',') + for (let i = 0; i < list.length; i++) { + map[list[i]] = true + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] +} + +export const exportDefault = 'export default ' + +export const beautifierConf = { + html: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'separate', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: false, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + }, + js: { + indent_size: '2', + indent_char: ' ', + max_preserve_newlines: '-1', + preserve_newlines: false, + keep_array_indentation: false, + break_chained_methods: false, + indent_scripts: 'normal', + brace_style: 'end-expand', + space_before_conditional: true, + unescape_strings: false, + jslint_happy: true, + end_with_newline: true, + wrap_line_length: '110', + indent_inner_html: true, + comma_first: false, + e4x: true, + indent_empty_lines: true + } +} + +// 首字母大小 +export function titleCase(str) { + return str.replace(/( |^)[a-z]/g, L => L.toUpperCase()) +} + +// 下划转驼峰 +export function camelCase(str) { + return str.replace(/_[a-z]/g, str1 => str1.substr(-1).toUpperCase()) +} + +export function isNumberStr(str) { + return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str) +} + diff --git a/ruoyi-ui/src/utils/jsencrypt.js b/ruoyi-ui/src/utils/jsencrypt.js new file mode 100644 index 0000000..78d9523 --- /dev/null +++ b/ruoyi-ui/src/utils/jsencrypt.js @@ -0,0 +1,30 @@ +import JSEncrypt from 'jsencrypt/bin/jsencrypt.min' + +// 密钥对生成 http://web.chacuo.net/netrsakeypair + +const publicKey = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdH\n' + + 'nzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==' + +const privateKey = 'MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY\n' + + '7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKN\n' + + 'PuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gA\n' + + 'kM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWow\n' + + 'cSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99Ecv\n' + + 'DQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthh\n' + + 'YhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3\n' + + 'UP8iWi1Qw0Y=' + +// 加密 +export function encrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPublicKey(publicKey) // 设置公钥 + return encryptor.encrypt(txt) // 对数据进行加密 +} + +// 解密 +export function decrypt(txt) { + const encryptor = new JSEncrypt() + encryptor.setPrivateKey(privateKey) // 设置私钥 + return encryptor.decrypt(txt) // 对数据进行解密 +} + diff --git a/ruoyi-ui/src/utils/permission.js b/ruoyi-ui/src/utils/permission.js new file mode 100644 index 0000000..0263bbc --- /dev/null +++ b/ruoyi-ui/src/utils/permission.js @@ -0,0 +1,47 @@ +import store from '@/store' + +/** + * 字符权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkPermi(value) { + if (value && value instanceof Array && value.length > 0) { + const permissions = store.getters && store.getters.permissions + const permissionDatas = value + const all_permission = "*:*:*" + + const hasPermission = permissions.some(permission => { + return all_permission === permission || permissionDatas.includes(permission) + }) + + return hasPermission + + } else { + console.error(`need roles! Like checkPermi="['system:user:add','system:user:edit']"`) + return false + } +} + +/** + * 角色权限校验 + * @param {Array} value 校验值 + * @returns {Boolean} + */ +export function checkRole(value) { + if (value && value instanceof Array && value.length > 0) { + const roles = store.getters && store.getters.roles + const permissionRoles = value + const super_admin = "admin" + + const hasRole = roles.some(role => { + return super_admin === role || permissionRoles.includes(role) + }) + + return hasRole + + } else { + console.error(`need roles! Like checkRole="['admin','editor']"`) + return false + } +} \ No newline at end of file diff --git a/ruoyi-ui/src/utils/request.js b/ruoyi-ui/src/utils/request.js new file mode 100644 index 0000000..7150ecb --- /dev/null +++ b/ruoyi-ui/src/utils/request.js @@ -0,0 +1,152 @@ +import axios from 'axios' +import { Notification, MessageBox, Message, Loading } from 'element-ui' +import store from '@/store' +import { getToken } from '@/utils/auth' +import errorCode from '@/utils/errorCode' +import { tansParams, blobValidate } from "@/utils/ruoyi" +import cache from '@/plugins/cache' +import { saveAs } from 'file-saver' + +let downloadLoadingInstance +// 是否显示重新登录 +export let isRelogin = { show: false } + +axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8' +// 创建axios实例 +const service = axios.create({ + // axios中请求配置有baseURL选项,表示请求URL公共部分 + baseURL: process.env.VUE_APP_BASE_API, + // 超时 + timeout: 10000 +}) + +// request拦截器 +service.interceptors.request.use(config => { + // 是否需要设置 token + const isToken = (config.headers || {}).isToken === false + // 是否需要防止数据重复提交 + const isRepeatSubmit = (config.headers || {}).repeatSubmit === false + if (getToken() && !isToken) { + config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改 + } + // get请求映射params参数 + if (config.method === 'get' && config.params) { + let url = config.url + '?' + tansParams(config.params) + url = url.slice(0, -1) + config.params = {} + config.url = url + } + if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put')) { + const requestObj = { + url: config.url, + data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data, + time: new Date().getTime() + } + const requestSize = Object.keys(JSON.stringify(requestObj)).length // 请求数据大小 + const limitSize = 5 * 1024 * 1024 // 限制存放数据5M + if (requestSize >= limitSize) { + console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。') + return config + } + const sessionObj = cache.session.getJSON('sessionObj') + if (sessionObj === undefined || sessionObj === null || sessionObj === '') { + cache.session.setJSON('sessionObj', requestObj) + } else { + const s_url = sessionObj.url // 请求地址 + const s_data = sessionObj.data // 请求数据 + const s_time = sessionObj.time // 请求时间 + const interval = 1000 // 间隔时间(ms),小于此时间视为重复提交 + if (s_data === requestObj.data && requestObj.time - s_time < interval && s_url === requestObj.url) { + const message = '数据正在处理,请勿重复提交' + console.warn(`[${s_url}]: ` + message) + return Promise.reject(new Error(message)) + } else { + cache.session.setJSON('sessionObj', requestObj) + } + } + } + return config +}, error => { + console.log(error) + Promise.reject(error) +}) + +// 响应拦截器 +service.interceptors.response.use(res => { + // 未设置状态码则默认成功状态 + const code = res.data.code || 200 + // 获取错误信息 + const msg = errorCode[code] || res.data.msg || errorCode['default'] + // 二进制数据则直接返回 + if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') { + return res.data + } + if (code === 401) { + if (!isRelogin.show) { + isRelogin.show = true + MessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => { + isRelogin.show = false + store.dispatch('LogOut').then(() => { + location.href = '/index' + }) + }).catch(() => { + isRelogin.show = false + }) + } + return Promise.reject('无效的会话,或者会话已过期,请重新登录。') + } else if (code === 500) { + Message({ message: msg, type: 'error' }) + return Promise.reject(new Error(msg)) + } else if (code === 601) { + Message({ message: msg, type: 'warning' }) + return Promise.reject('error') + } else if (code !== 200) { + Notification.error({ title: msg }) + return Promise.reject('error') + } else { + return res.data + } + }, + error => { + console.log('err' + error) + let { message } = error + if (message == "Network Error") { + message = "后端接口连接异常" + } else if (message.includes("timeout")) { + message = "系统接口请求超时" + } else if (message.includes("Request failed with status code")) { + message = "系统接口" + message.substr(message.length - 3) + "异常" + } + Message({ message: message, type: 'error', duration: 5 * 1000 }) + return Promise.reject(error) + } +) + +// 通用下载方法 +export function download(url, params, filename, config) { + downloadLoadingInstance = Loading.service({ text: "正在下载数据,请稍候", spinner: "el-icon-loading", background: "rgba(0, 0, 0, 0.7)", }) + return service.post(url, params, { + transformRequest: [(params) => { return tansParams(params) }], + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + responseType: 'blob', + ...config + }).then(async (data) => { + const isBlob = blobValidate(data) + if (isBlob) { + const blob = new Blob([data]) + saveAs(blob, filename) + } else { + const resText = await data.text() + const rspObj = JSON.parse(resText) + const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default'] + Message.error(errMsg) + } + downloadLoadingInstance.close() + }).catch((r) => { + console.error(r) + Message.error('下载文件出现错误,请联系管理员!') + downloadLoadingInstance.close() + }) +} + +export default service diff --git a/ruoyi-ui/src/utils/ruoyi.js b/ruoyi-ui/src/utils/ruoyi.js new file mode 100644 index 0000000..3de2d98 --- /dev/null +++ b/ruoyi-ui/src/utils/ruoyi.js @@ -0,0 +1,228 @@ +/** + * 通用js方法封装处理 + * Copyright (c) 2019 ruoyi + */ + +// 日期格式化 +export function parseTime(time, pattern) { + if (arguments.length === 0 || !time) { + return null + } + const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}' + let date + if (typeof time === 'object') { + date = time + } else { + if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { + time = parseInt(time) + } else if (typeof time === 'string') { + time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '') + } + if ((typeof time === 'number') && (time.toString().length === 10)) { + time = time * 1000 + } + date = new Date(time) + } + const formatObj = { + y: date.getFullYear(), + m: date.getMonth() + 1, + d: date.getDate(), + h: date.getHours(), + i: date.getMinutes(), + s: date.getSeconds(), + a: date.getDay() + } + const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => { + let value = formatObj[key] + // Note: getDay() returns 0 on Sunday + if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } + if (result.length > 0 && value < 10) { + value = '0' + value + } + return value || 0 + }) + return time_str +} + +// 表单重置 +export function resetForm(refName) { + if (this.$refs[refName]) { + this.$refs[refName].resetFields() + } +} + +// 添加日期范围 +export function addDateRange(params, dateRange, propName) { + let search = params + search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {} + dateRange = Array.isArray(dateRange) ? dateRange : [] + if (typeof (propName) === 'undefined') { + search.params['beginTime'] = dateRange[0] + search.params['endTime'] = dateRange[1] + } else { + search.params['begin' + propName] = dateRange[0] + search.params['end' + propName] = dateRange[1] + } + return search +} + +// 回显数据字典 +export function selectDictLabel(datas, value) { + if (value === undefined) { + return "" + } + var actions = [] + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + value)) { + actions.push(datas[key].label) + return true + } + }) + if (actions.length === 0) { + actions.push(value) + } + return actions.join('') +} + +// 回显数据字典(字符串、数组) +export function selectDictLabels(datas, value, separator) { + if (value === undefined || value.length ===0) { + return "" + } + if (Array.isArray(value)) { + value = value.join(",") + } + var actions = [] + var currentSeparator = undefined === separator ? "," : separator + var temp = value.split(currentSeparator) + Object.keys(value.split(currentSeparator)).some((val) => { + var match = false + Object.keys(datas).some((key) => { + if (datas[key].value == ('' + temp[val])) { + actions.push(datas[key].label + currentSeparator) + match = true + } + }) + if (!match) { + actions.push(temp[val] + currentSeparator) + } + }) + return actions.join('').substring(0, actions.join('').length - 1) +} + +// 字符串格式化(%s ) +export function sprintf(str) { + var args = arguments, flag = true, i = 1 + str = str.replace(/%s/g, function () { + var arg = args[i++] + if (typeof arg === 'undefined') { + flag = false + return '' + } + return arg + }) + return flag ? str : '' +} + +// 转换字符串,undefined,null等转化为"" +export function parseStrEmpty(str) { + if (!str || str == "undefined" || str == "null") { + return "" + } + return str +} + +// 数据合并 +export function mergeRecursive(source, target) { + for (var p in target) { + try { + if (target[p].constructor == Object) { + source[p] = mergeRecursive(source[p], target[p]) + } else { + source[p] = target[p] + } + } catch (e) { + source[p] = target[p] + } + } + return source +} + +/** + * 构造树型结构数据 + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + */ +export function handleTree(data, id, parentId, children) { + let config = { + id: id || 'id', + parentId: parentId || 'parentId', + childrenList: children || 'children' + } + + var childrenListMap = {} + var tree = [] + for (let d of data) { + let id = d[config.id] + childrenListMap[id] = d + if (!d[config.childrenList]) { + d[config.childrenList] = [] + } + } + + for (let d of data) { + let parentId = d[config.parentId] + let parentObj = childrenListMap[parentId] + if (!parentObj) { + tree.push(d) + } else { + parentObj[config.childrenList].push(d) + } + } + return tree +} + +/** +* 参数处理 +* @param {*} params 参数 +*/ +export function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName] + var part = encodeURIComponent(propName) + "=" + if (value !== null && value !== "" && typeof (value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') { + let params = propName + '[' + key + ']' + var subPart = encodeURIComponent(params) + "=" + result += subPart + encodeURIComponent(value[key]) + "&" + } + } + } else { + result += part + encodeURIComponent(value) + "&" + } + } + } + return result +} + +// 返回项目路径 +export function getNormalPath(p) { + if (p.length === 0 || !p || p == 'undefined') { + return p + } + let res = p.replace('//', '/') + if (res[res.length - 1] === '/') { + return res.slice(0, res.length - 1) + } + return res +} + +// 验证是否为blob格式 +export function blobValidate(data) { + return data.type !== 'application/json' +} diff --git a/ruoyi-ui/src/utils/scroll-to.js b/ruoyi-ui/src/utils/scroll-to.js new file mode 100644 index 0000000..c5d8e04 --- /dev/null +++ b/ruoyi-ui/src/utils/scroll-to.js @@ -0,0 +1,58 @@ +Math.easeInOutQuad = function(t, b, c, d) { + t /= d / 2 + if (t < 1) { + return c / 2 * t * t + b + } + t-- + return -c / 2 * (t * (t - 2) - 1) + b +} + +// requestAnimationFrame for Smart Animating http://goo.gl/sx5sts +var requestAnimFrame = (function() { + return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) } +})() + +/** + * Because it's so fucking difficult to detect the scrolling element, just move them all + * @param {number} amount + */ +function move(amount) { + document.documentElement.scrollTop = amount + document.body.parentNode.scrollTop = amount + document.body.scrollTop = amount +} + +function position() { + return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop +} + +/** + * @param {number} to + * @param {number} duration + * @param {Function} callback + */ +export function scrollTo(to, duration, callback) { + const start = position() + const change = to - start + const increment = 20 + let currentTime = 0 + duration = (typeof (duration) === 'undefined') ? 500 : duration + var animateScroll = function() { + // increment the time + currentTime += increment + // find the value with the quadratic in-out easing function + var val = Math.easeInOutQuad(currentTime, start, change, duration) + // move the document.body + move(val) + // do the animation unless its over + if (currentTime < duration) { + requestAnimFrame(animateScroll) + } else { + if (callback && typeof (callback) === 'function') { + // the animation is done so lets callback + callback() + } + } + } + animateScroll() +} diff --git a/ruoyi-ui/src/utils/validate.js b/ruoyi-ui/src/utils/validate.js new file mode 100644 index 0000000..6a4c0c5 --- /dev/null +++ b/ruoyi-ui/src/utils/validate.js @@ -0,0 +1,114 @@ +/** + * 路径匹配器 + * @param {string} pattern + * @param {string} path + * @returns {Boolean} + */ +export function isPathMatch(pattern, path) { + const regexPattern = pattern.replace(/\//g, '\\/').replace(/\*\*/g, '.*').replace(/\*/g, '[^\\/]*') + const regex = new RegExp(`^${regexPattern}$`) + return regex.test(path) +} + +/** + * 判断value字符串是否为空 + * @param {string} value + * @returns {Boolean} + */ +export function isEmpty(value) { + if (value == null || value == "" || value == undefined || value == "undefined") { + return true + } + return false +} + +/** + * 判断url是否是http或https + * @param {string} url + * @returns {Boolean} + */ +export function isHttp(url) { + return url.indexOf('http://') !== -1 || url.indexOf('https://') !== -1 +} + +/** + * 判断path是否为外链 + * @param {string} path + * @returns {Boolean} + */ +export function isExternal(path) { + return /^(https?:|mailto:|tel:)/.test(path) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUsername(str) { + const valid_map = ['admin', 'editor'] + return valid_map.indexOf(str.trim()) >= 0 +} + +/** + * @param {string} url + * @returns {Boolean} + */ +export function validURL(url) { + const reg = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/ + return reg.test(url) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validLowerCase(str) { + const reg = /^[a-z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validUpperCase(str) { + const reg = /^[A-Z]+$/ + return reg.test(str) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function validAlphabets(str) { + const reg = /^[A-Za-z]+$/ + return reg.test(str) +} + +/** + * @param {string} email + * @returns {Boolean} + */ +export function validEmail(email) { + const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + return reg.test(email) +} + +/** + * @param {string} str + * @returns {Boolean} + */ +export function isString(str) { + return typeof str === 'string' || str instanceof String +} + +/** + * @param {Array} arg + * @returns {Boolean} + */ +export function isArray(arg) { + if (typeof Array.isArray === 'undefined') { + return Object.prototype.toString.call(arg) === '[object Array]' + } + return Array.isArray(arg) +} diff --git a/ruoyi-ui/src/views/agv/queue/index.vue b/ruoyi-ui/src/views/agv/queue/index.vue new file mode 100644 index 0000000..a3ced0a --- /dev/null +++ b/ruoyi-ui/src/views/agv/queue/index.vue @@ -0,0 +1,276 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/BarChart.vue b/ruoyi-ui/src/views/dashboard/BarChart.vue new file mode 100644 index 0000000..88e7ef6 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/BarChart.vue @@ -0,0 +1,102 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/LineChart.vue b/ruoyi-ui/src/views/dashboard/LineChart.vue new file mode 100644 index 0000000..702ff73 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/LineChart.vue @@ -0,0 +1,135 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/PanelGroup.vue b/ruoyi-ui/src/views/dashboard/PanelGroup.vue new file mode 100644 index 0000000..1a1081f --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PanelGroup.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/ruoyi-ui/src/views/dashboard/PieChart.vue b/ruoyi-ui/src/views/dashboard/PieChart.vue new file mode 100644 index 0000000..63f0d84 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/PieChart.vue @@ -0,0 +1,79 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/RaddarChart.vue b/ruoyi-ui/src/views/dashboard/RaddarChart.vue new file mode 100644 index 0000000..312e018 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/RaddarChart.vue @@ -0,0 +1,116 @@ + + + diff --git a/ruoyi-ui/src/views/dashboard/mixins/resize.js b/ruoyi-ui/src/views/dashboard/mixins/resize.js new file mode 100644 index 0000000..b1e76e9 --- /dev/null +++ b/ruoyi-ui/src/views/dashboard/mixins/resize.js @@ -0,0 +1,56 @@ +import { debounce } from '@/utils' + +export default { + data() { + return { + $_sidebarElm: null, + $_resizeHandler: null + } + }, + mounted() { + this.initListener() + }, + activated() { + if (!this.$_resizeHandler) { + // avoid duplication init + this.initListener() + } + + // when keep-alive chart activated, auto resize + this.resize() + }, + beforeDestroy() { + this.destroyListener() + }, + deactivated() { + this.destroyListener() + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_sidebarResizeHandler(e) { + if (e.propertyName === 'width') { + this.$_resizeHandler() + } + }, + initListener() { + this.$_resizeHandler = debounce(() => { + this.resize() + }, 100) + window.addEventListener('resize', this.$_resizeHandler) + + this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] + this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) + }, + destroyListener() { + window.removeEventListener('resize', this.$_resizeHandler) + this.$_resizeHandler = null + + this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) + }, + resize() { + const { chart } = this + chart && chart.resize() + } + } +} diff --git a/ruoyi-ui/src/views/device/DeviceCar/index.vue b/ruoyi-ui/src/views/device/DeviceCar/index.vue new file mode 100644 index 0000000..25d6ba5 --- /dev/null +++ b/ruoyi-ui/src/views/device/DeviceCar/index.vue @@ -0,0 +1,315 @@ + + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/device/api/index.vue b/ruoyi-ui/src/views/device/api/index.vue new file mode 100644 index 0000000..5952296 --- /dev/null +++ b/ruoyi-ui/src/views/device/api/index.vue @@ -0,0 +1,327 @@ + + + diff --git a/ruoyi-ui/src/views/device/ddj/index.vue b/ruoyi-ui/src/views/device/ddj/index.vue new file mode 100644 index 0000000..db58ef3 --- /dev/null +++ b/ruoyi-ui/src/views/device/ddj/index.vue @@ -0,0 +1,483 @@ + + + diff --git a/ruoyi-ui/src/views/device/ssj/index.vue b/ruoyi-ui/src/views/device/ssj/index.vue new file mode 100644 index 0000000..74846d3 --- /dev/null +++ b/ruoyi-ui/src/views/device/ssj/index.vue @@ -0,0 +1,405 @@ + + + + diff --git a/ruoyi-ui/src/views/dynamicMonitoring/index.vue b/ruoyi-ui/src/views/dynamicMonitoring/index.vue new file mode 100644 index 0000000..7a23699 --- /dev/null +++ b/ruoyi-ui/src/views/dynamicMonitoring/index.vue @@ -0,0 +1,25 @@ + + + + + + diff --git a/ruoyi-ui/src/views/error/401.vue b/ruoyi-ui/src/views/error/401.vue new file mode 100644 index 0000000..448b6ec --- /dev/null +++ b/ruoyi-ui/src/views/error/401.vue @@ -0,0 +1,88 @@ + + + + + diff --git a/ruoyi-ui/src/views/error/404.vue b/ruoyi-ui/src/views/error/404.vue new file mode 100644 index 0000000..96f075c --- /dev/null +++ b/ruoyi-ui/src/views/error/404.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/ruoyi-ui/src/views/index.vue b/ruoyi-ui/src/views/index.vue new file mode 100644 index 0000000..87af920 --- /dev/null +++ b/ruoyi-ui/src/views/index.vue @@ -0,0 +1,679 @@ + + + + + diff --git a/ruoyi-ui/src/views/index_v1.vue b/ruoyi-ui/src/views/index_v1.vue new file mode 100644 index 0000000..d2d2ec6 --- /dev/null +++ b/ruoyi-ui/src/views/index_v1.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/ruoyi-ui/src/views/job/mydoJob/index.vue b/ruoyi-ui/src/views/job/mydoJob/index.vue new file mode 100644 index 0000000..69b9c69 --- /dev/null +++ b/ruoyi-ui/src/views/job/mydoJob/index.vue @@ -0,0 +1,464 @@ + + + diff --git a/ruoyi-ui/src/views/login.vue b/ruoyi-ui/src/views/login.vue new file mode 100644 index 0000000..304815b --- /dev/null +++ b/ruoyi-ui/src/views/login.vue @@ -0,0 +1,245 @@ + + + + + diff --git a/ruoyi-ui/src/views/monitor/cache/index.vue b/ruoyi-ui/src/views/monitor/cache/index.vue new file mode 100644 index 0000000..1c9ca61 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/cache/index.vue @@ -0,0 +1,166 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/cache/list.vue b/ruoyi-ui/src/views/monitor/cache/list.vue new file mode 100644 index 0000000..c84d093 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/cache/list.vue @@ -0,0 +1,269 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/druid/index.vue b/ruoyi-ui/src/views/monitor/druid/index.vue new file mode 100644 index 0000000..8c3a257 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/druid/index.vue @@ -0,0 +1,15 @@ + + diff --git a/ruoyi-ui/src/views/monitor/job/index.vue b/ruoyi-ui/src/views/monitor/job/index.vue new file mode 100644 index 0000000..66526ee --- /dev/null +++ b/ruoyi-ui/src/views/monitor/job/index.vue @@ -0,0 +1,589 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/job/log.vue b/ruoyi-ui/src/views/monitor/job/log.vue new file mode 100644 index 0000000..22d4e31 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/job/log.vue @@ -0,0 +1,295 @@ + + + diff --git a/ruoyi-ui/src/views/monitor/logininfor/index.vue b/ruoyi-ui/src/views/monitor/logininfor/index.vue new file mode 100644 index 0000000..4ea6dd7 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/logininfor/index.vue @@ -0,0 +1,273 @@ + + + + diff --git a/ruoyi-ui/src/views/monitor/online/index.vue b/ruoyi-ui/src/views/monitor/online/index.vue new file mode 100644 index 0000000..70b630d --- /dev/null +++ b/ruoyi-ui/src/views/monitor/online/index.vue @@ -0,0 +1,141 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/monitor/operlog/index.vue b/ruoyi-ui/src/views/monitor/operlog/index.vue new file mode 100644 index 0000000..5dcf630 --- /dev/null +++ b/ruoyi-ui/src/views/monitor/operlog/index.vue @@ -0,0 +1,361 @@ + + + + diff --git a/ruoyi-ui/src/views/monitor/server/index.vue b/ruoyi-ui/src/views/monitor/server/index.vue new file mode 100644 index 0000000..fa7adeb --- /dev/null +++ b/ruoyi-ui/src/views/monitor/server/index.vue @@ -0,0 +1,248 @@ + + + diff --git a/ruoyi-ui/src/views/queue/ddj/index.vue b/ruoyi-ui/src/views/queue/ddj/index.vue new file mode 100644 index 0000000..fa87f2b --- /dev/null +++ b/ruoyi-ui/src/views/queue/ddj/index.vue @@ -0,0 +1,515 @@ + + + diff --git a/ruoyi-ui/src/views/redirect.vue b/ruoyi-ui/src/views/redirect.vue new file mode 100644 index 0000000..db4c1d6 --- /dev/null +++ b/ruoyi-ui/src/views/redirect.vue @@ -0,0 +1,12 @@ + diff --git a/ruoyi-ui/src/views/register.vue b/ruoyi-ui/src/views/register.vue new file mode 100644 index 0000000..d624fc6 --- /dev/null +++ b/ruoyi-ui/src/views/register.vue @@ -0,0 +1,211 @@ + + + + + diff --git a/ruoyi-ui/src/views/system/config/index.vue b/ruoyi-ui/src/views/system/config/index.vue new file mode 100644 index 0000000..cd09435 --- /dev/null +++ b/ruoyi-ui/src/views/system/config/index.vue @@ -0,0 +1,400 @@ + + + diff --git a/ruoyi-ui/src/views/system/dept/index.vue b/ruoyi-ui/src/views/system/dept/index.vue new file mode 100644 index 0000000..4330096 --- /dev/null +++ b/ruoyi-ui/src/views/system/dept/index.vue @@ -0,0 +1,374 @@ + + + diff --git a/ruoyi-ui/src/views/system/dict/data.vue b/ruoyi-ui/src/views/system/dict/data.vue new file mode 100644 index 0000000..1691bff --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/data.vue @@ -0,0 +1,407 @@ + + + diff --git a/ruoyi-ui/src/views/system/dict/index.vue b/ruoyi-ui/src/views/system/dict/index.vue new file mode 100644 index 0000000..2b106e5 --- /dev/null +++ b/ruoyi-ui/src/views/system/dict/index.vue @@ -0,0 +1,380 @@ + + + diff --git a/ruoyi-ui/src/views/system/menu/index.vue b/ruoyi-ui/src/views/system/menu/index.vue new file mode 100644 index 0000000..ab678e8 --- /dev/null +++ b/ruoyi-ui/src/views/system/menu/index.vue @@ -0,0 +1,558 @@ + + + diff --git a/ruoyi-ui/src/views/system/notice/index.vue b/ruoyi-ui/src/views/system/notice/index.vue new file mode 100644 index 0000000..dc11f2e --- /dev/null +++ b/ruoyi-ui/src/views/system/notice/index.vue @@ -0,0 +1,365 @@ + + + diff --git a/ruoyi-ui/src/views/system/post/index.vue b/ruoyi-ui/src/views/system/post/index.vue new file mode 100644 index 0000000..8ccd952 --- /dev/null +++ b/ruoyi-ui/src/views/system/post/index.vue @@ -0,0 +1,335 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/authUser.vue b/ruoyi-ui/src/views/system/role/authUser.vue new file mode 100644 index 0000000..4859d59 --- /dev/null +++ b/ruoyi-ui/src/views/system/role/authUser.vue @@ -0,0 +1,199 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/role/index.vue b/ruoyi-ui/src/views/system/role/index.vue new file mode 100644 index 0000000..24caaab --- /dev/null +++ b/ruoyi-ui/src/views/system/role/index.vue @@ -0,0 +1,692 @@ + + + diff --git a/ruoyi-ui/src/views/system/role/selectUser.vue b/ruoyi-ui/src/views/system/role/selectUser.vue new file mode 100644 index 0000000..d2b21bf --- /dev/null +++ b/ruoyi-ui/src/views/system/role/selectUser.vue @@ -0,0 +1,136 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/authRole.vue b/ruoyi-ui/src/views/system/user/authRole.vue new file mode 100644 index 0000000..1f862d7 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/authRole.vue @@ -0,0 +1,123 @@ + + + \ No newline at end of file diff --git a/ruoyi-ui/src/views/system/user/index.vue b/ruoyi-ui/src/views/system/user/index.vue new file mode 100644 index 0000000..6173be3 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/index.vue @@ -0,0 +1,644 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/profile/index.vue b/ruoyi-ui/src/views/system/user/profile/index.vue new file mode 100644 index 0000000..22502a7 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/index.vue @@ -0,0 +1,91 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/profile/resetPwd.vue b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue new file mode 100644 index 0000000..954d341 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/resetPwd.vue @@ -0,0 +1,69 @@ + + + diff --git a/ruoyi-ui/src/views/system/user/profile/userAvatar.vue b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue new file mode 100644 index 0000000..59afb35 --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userAvatar.vue @@ -0,0 +1,184 @@ + + + + diff --git a/ruoyi-ui/src/views/system/user/profile/userInfo.vue b/ruoyi-ui/src/views/system/user/profile/userInfo.vue new file mode 100644 index 0000000..486adfc --- /dev/null +++ b/ruoyi-ui/src/views/system/user/profile/userInfo.vue @@ -0,0 +1,88 @@ + + + diff --git a/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue new file mode 100644 index 0000000..b5c2e2e --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/CodeTypeDialog.vue @@ -0,0 +1,106 @@ + + diff --git a/ruoyi-ui/src/views/tool/build/DraggableItem.vue b/ruoyi-ui/src/views/tool/build/DraggableItem.vue new file mode 100644 index 0000000..e881778 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/DraggableItem.vue @@ -0,0 +1,100 @@ + diff --git a/ruoyi-ui/src/views/tool/build/IconsDialog.vue b/ruoyi-ui/src/views/tool/build/IconsDialog.vue new file mode 100644 index 0000000..958be50 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/IconsDialog.vue @@ -0,0 +1,123 @@ + + + diff --git a/ruoyi-ui/src/views/tool/build/RightPanel.vue b/ruoyi-ui/src/views/tool/build/RightPanel.vue new file mode 100644 index 0000000..c2760eb --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/RightPanel.vue @@ -0,0 +1,946 @@ + + + + + diff --git a/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue new file mode 100644 index 0000000..e913d0f --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/TreeNodeDialog.vue @@ -0,0 +1,148 @@ + + diff --git a/ruoyi-ui/src/views/tool/build/index.vue b/ruoyi-ui/src/views/tool/build/index.vue new file mode 100644 index 0000000..5849b34 --- /dev/null +++ b/ruoyi-ui/src/views/tool/build/index.vue @@ -0,0 +1,767 @@ + + + + + diff --git a/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue new file mode 100644 index 0000000..3a6e2ed --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/basicInfoForm.vue @@ -0,0 +1,60 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/createTable.vue b/ruoyi-ui/src/views/tool/gen/createTable.vue new file mode 100644 index 0000000..e71996c --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/createTable.vue @@ -0,0 +1,45 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/editTable.vue b/ruoyi-ui/src/views/tool/gen/editTable.vue new file mode 100644 index 0000000..512f66e --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/editTable.vue @@ -0,0 +1,229 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/genInfoForm.vue b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue new file mode 100644 index 0000000..60d9e87 --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/genInfoForm.vue @@ -0,0 +1,312 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/importTable.vue b/ruoyi-ui/src/views/tool/gen/importTable.vue new file mode 100644 index 0000000..cb080ce --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/importTable.vue @@ -0,0 +1,120 @@ + + + diff --git a/ruoyi-ui/src/views/tool/gen/index.vue b/ruoyi-ui/src/views/tool/gen/index.vue new file mode 100644 index 0000000..83c026f --- /dev/null +++ b/ruoyi-ui/src/views/tool/gen/index.vue @@ -0,0 +1,347 @@ + + + diff --git a/ruoyi-ui/src/views/tool/swagger/index.vue b/ruoyi-ui/src/views/tool/swagger/index.vue new file mode 100644 index 0000000..dd6b4a2 --- /dev/null +++ b/ruoyi-ui/src/views/tool/swagger/index.vue @@ -0,0 +1,15 @@ + + diff --git a/ruoyi-ui/vue.config.js b/ruoyi-ui/vue.config.js new file mode 100644 index 0000000..a1733b8 --- /dev/null +++ b/ruoyi-ui/vue.config.js @@ -0,0 +1,131 @@ +'use strict' +const path = require('path') + +function resolve(dir) { + return path.join(__dirname, dir) +} + +const CompressionPlugin = require('compression-webpack-plugin') + +const name = process.env.VUE_APP_TITLE || 'FORTUCKY WCS SYSTEM' // 网页标题 + +const baseUrl = 'http://localhost:8081' // 后端接口地址(你的后端端口是8081) + +const port = process.env.port || process.env.npm_config_port || 80 // 前端端口 + +module.exports = { + publicPath: process.env.NODE_ENV === "production" ? "/" : "/", + outputDir: 'dist', + assetsDir: 'static', + productionSourceMap: false, + transpileDependencies: ['quill'], + devServer: { + host: '0.0.0.0', + port: port, + open: true, + proxy: { + // 保留原有的基础API代理(如 /api) + [process.env.VUE_APP_BASE_API]: { + target: baseUrl, + changeOrigin: true, + pathRewrite: { + ['^' + process.env.VUE_APP_BASE_API]: '' + } + }, + // 新增:转发 /largeScreen 开头的接口(你的堆垛机接口路径) + '/largeScreen': { + target: baseUrl, // 转发到后端地址 http://localhost:8081 + changeOrigin: true, + // 不需要 pathRewrite,因为后端接口本身就是 /largeScreen/ddj/list + // 转发后实际请求地址为:http://localhost:8081/largeScreen/ddj/list + }, + // 保留原有的springdoc proxy + '^/v3/api-docs/(.*)': { + target: baseUrl, + changeOrigin: true + } + }, + disableHostCheck: true + }, + css: { + loaderOptions: { + sass: { + sassOptions: { outputStyle: "expanded" } + } + } + }, + configureWebpack: { + name: name, + resolve: { + alias: { + '@': resolve('src') + } + }, + plugins: [ + new CompressionPlugin({ + cache: false, + test: /\.(js|css|html|jpe?g|png|gif|svg)?$/i, + filename: '[path][base].gz[query]', + algorithm: 'gzip', + minRatio: 0.8, + deleteOriginalAssets: false + }) + ], + }, + chainWebpack(config) { + config.plugins.delete('preload') + config.plugins.delete('prefetch') + + config.module + .rule('svg') + .exclude.add(resolve('src/assets/icons')) + .end() + config.module + .rule('icons') + .test(/\.svg$/) + .include.add(resolve('src/assets/icons')) + .end() + .use('svg-sprite-loader') + .loader('svg-sprite-loader') + .options({ + symbolId: 'icon-[name]' + }) + .end() + + config.when(process.env.NODE_ENV !== 'development', config => { + config + .plugin('ScriptExtHtmlWebpackPlugin') + .after('html') + .use('script-ext-html-webpack-plugin', [{ + inline: /runtime\..*\.js$/ + }]) + .end() + + config.optimization.splitChunks({ + chunks: 'all', + cacheGroups: { + libs: { + name: 'chunk-libs', + test: /[\\/]node_modules[\\/]/, + priority: 10, + chunks: 'initial' + }, + elementUI: { + name: 'chunk-elementUI', + test: /[\\/]node_modules[\\/]_?element-ui(.*)/, + priority: 20 + }, + commons: { + name: 'chunk-commons', + test: resolve('src/components'), + minChunks: 3, + priority: 5, + reuseExistingChunk: true + } + } + }) + config.optimization.runtimeChunk('single') + }) + } +} + diff --git a/ry.bat b/ry.bat new file mode 100644 index 0000000..ac1e437 --- /dev/null +++ b/ry.bat @@ -0,0 +1,67 @@ +@echo off + +rem jarƽ¼¶Ŀ¼ +set AppName=ruoyi-admin.jar + +rem JVM²ÎÊý +set JVM_OPTS="-Dname=%AppName% -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" + + +ECHO. + ECHO. [1] Æô¶¯%AppName% + ECHO. [2] ¹رÕ%AppName% + ECHO. [3] ÖØÆô%AppName% + ECHO. [4] Æô¶¯״̬ %AppName% + ECHO. [5] ÍË ³ö +ECHO. + +ECHO.ÇëÊäÈëѡÔñÏîĿµÄÐòºÅ: +set /p ID= + IF "%id%"=="1" GOTO start + IF "%id%"=="2" GOTO stop + IF "%id%"=="3" GOTO restart + IF "%id%"=="4" GOTO status + IF "%id%"=="5" EXIT +PAUSE +:start + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if defined pid ( + echo %%is running + PAUSE + ) + +start javaw %JVM_OPTS% -jar %AppName% + +echo starting¡­¡­ +echo Start %AppName% success... +goto:eof + +rem º¯Êýstopͨ¹ýjpsÃüÁî²éÕÒpid²¢½áÊø½ø³Ì +:stop + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% does not exists) else ( + echo prepare to kill %image_name% + echo start kill %pid% ... + rem ¸ù¾ݽø³ÌID£¬kill½ø³Ì + taskkill /f /pid %pid% + ) +goto:eof +:restart + call :stop + call :start +goto:eof +:status + for /f "usebackq tokens=1-2" %%a in (`jps -l ^| findstr %AppName%`) do ( + set pid=%%a + set image_name=%%b + ) + if not defined pid (echo process %AppName% is dead ) else ( + echo %image_name% is running + ) +goto:eof diff --git a/ry.sh b/ry.sh new file mode 100644 index 0000000..d6a9cf3 --- /dev/null +++ b/ry.sh @@ -0,0 +1,86 @@ +#!/bin/sh +# ./ry.sh start 启动 stop 停止 restart 重启 status 状态 +AppName=ruoyi-admin.jar + +# JVM参数 +JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC" +APP_HOME=`pwd` +LOG_PATH=$APP_HOME/logs/$AppName.log + +if [ "$1" = "" ]; +then + echo -e "\033[0;31m 未输入操作名 \033[0m \033[0;34m {start|stop|restart|status} \033[0m" + exit 1 +fi + +if [ "$AppName" = "" ]; +then + echo -e "\033[0;31m 未输入应用名 \033[0m" + exit 1 +fi + +function start() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + + if [ x"$PID" != x"" ]; then + echo "$AppName is running..." + else + nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 & + echo "Start $AppName success..." + fi +} + +function stop() +{ + echo "Stop $AppName" + + PID="" + query(){ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'` + } + + query + if [ x"$PID" != x"" ]; then + kill -TERM $PID + echo "$AppName (pid:$PID) exiting..." + while [ x"$PID" != x"" ] + do + sleep 1 + query + done + echo "$AppName exited." + else + echo "$AppName already stopped." + fi +} + +function restart() +{ + stop + sleep 2 + start +} + +function status() +{ + PID=`ps -ef |grep java|grep $AppName|grep -v grep|wc -l` + if [ $PID != 0 ];then + echo "$AppName is running..." + else + echo "$AppName is not running..." + fi +} + +case $1 in + start) + start;; + stop) + stop;; + restart) + restart;; + status) + status;; + *) + +esac diff --git a/sql/quartz.sql b/sql/quartz.sql new file mode 100644 index 0000000..cee613b --- /dev/null +++ b/sql/quartz.sql @@ -0,0 +1,174 @@ +DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS; +DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE; +DROP TABLE IF EXISTS QRTZ_LOCKS; +DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_TRIGGERS; +DROP TABLE IF EXISTS QRTZ_JOB_DETAILS; +DROP TABLE IF EXISTS QRTZ_CALENDARS; + +-- ---------------------------- +-- 1、存储每一个已配置的 jobDetail 的详细信息 +-- ---------------------------- +create table QRTZ_JOB_DETAILS ( + sched_name varchar(120) not null comment '调度名称', + job_name varchar(200) not null comment '任务名称', + job_group varchar(200) not null comment '任务组名', + description varchar(250) null comment '相关介绍', + job_class_name varchar(250) not null comment '执行任务类名称', + is_durable varchar(1) not null comment '是否持久化', + is_nonconcurrent varchar(1) not null comment '是否并发', + is_update_data varchar(1) not null comment '是否更新数据', + requests_recovery varchar(1) not null comment '是否接受恢复执行', + job_data blob null comment '存放持久化job对象', + primary key (sched_name, job_name, job_group) +) engine=innodb comment = '任务详细信息表'; + +-- ---------------------------- +-- 2、 存储已配置的 Trigger 的信息 +-- ---------------------------- +create table QRTZ_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment '触发器的名字', + trigger_group varchar(200) not null comment '触发器所属组的名字', + job_name varchar(200) not null comment 'qrtz_job_details表job_name的外键', + job_group varchar(200) not null comment 'qrtz_job_details表job_group的外键', + description varchar(250) null comment '相关介绍', + next_fire_time bigint(13) null comment '上一次触发时间(毫秒)', + prev_fire_time bigint(13) null comment '下一次触发时间(默认为-1表示不触发)', + priority integer null comment '优先级', + trigger_state varchar(16) not null comment '触发器状态', + trigger_type varchar(8) not null comment '触发器的类型', + start_time bigint(13) not null comment '开始时间', + end_time bigint(13) null comment '结束时间', + calendar_name varchar(200) null comment '日程表名称', + misfire_instr smallint(2) null comment '补偿执行的策略', + job_data blob null comment '存放持久化job对象', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, job_name, job_group) references QRTZ_JOB_DETAILS(sched_name, job_name, job_group) +) engine=innodb comment = '触发器详细信息表'; + +-- ---------------------------- +-- 3、 存储简单的 Trigger,包括重复次数,间隔,以及已触发的次数 +-- ---------------------------- +create table QRTZ_SIMPLE_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + repeat_count bigint(7) not null comment '重复的次数统计', + repeat_interval bigint(12) not null comment '重复的间隔时间', + times_triggered bigint(10) not null comment '已经触发的次数', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = '简单触发器的信息表'; + +-- ---------------------------- +-- 4、 存储 Cron Trigger,包括 Cron 表达式和时区信息 +-- ---------------------------- +create table QRTZ_CRON_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + cron_expression varchar(200) not null comment 'cron表达式', + time_zone_id varchar(80) comment '时区', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = 'Cron类型的触发器表'; + +-- ---------------------------- +-- 5、 Trigger 作为 Blob 类型存储(用于 Quartz 用户用 JDBC 创建他们自己定制的 Trigger 类型,JobStore 并不知道如何存储实例的时候) +-- ---------------------------- +create table QRTZ_BLOB_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + blob_data blob null comment '存放持久化Trigger对象', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = 'Blob类型的触发器表'; + +-- ---------------------------- +-- 6、 以 Blob 类型存储存放日历信息, quartz可配置一个日历来指定一个时间范围 +-- ---------------------------- +create table QRTZ_CALENDARS ( + sched_name varchar(120) not null comment '调度名称', + calendar_name varchar(200) not null comment '日历名称', + calendar blob not null comment '存放持久化calendar对象', + primary key (sched_name, calendar_name) +) engine=innodb comment = '日历信息表'; + +-- ---------------------------- +-- 7、 存储已暂停的 Trigger 组的信息 +-- ---------------------------- +create table QRTZ_PAUSED_TRIGGER_GRPS ( + sched_name varchar(120) not null comment '调度名称', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + primary key (sched_name, trigger_group) +) engine=innodb comment = '暂停的触发器表'; + +-- ---------------------------- +-- 8、 存储与已触发的 Trigger 相关的状态信息,以及相联 Job 的执行信息 +-- ---------------------------- +create table QRTZ_FIRED_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + entry_id varchar(95) not null comment '调度器实例id', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + instance_name varchar(200) not null comment '调度器实例名', + fired_time bigint(13) not null comment '触发的时间', + sched_time bigint(13) not null comment '定时器制定的时间', + priority integer not null comment '优先级', + state varchar(16) not null comment '状态', + job_name varchar(200) null comment '任务名称', + job_group varchar(200) null comment '任务组名', + is_nonconcurrent varchar(1) null comment '是否并发', + requests_recovery varchar(1) null comment '是否接受恢复执行', + primary key (sched_name, entry_id) +) engine=innodb comment = '已触发的触发器表'; + +-- ---------------------------- +-- 9、 存储少量的有关 Scheduler 的状态信息,假如是用于集群中,可以看到其他的 Scheduler 实例 +-- ---------------------------- +create table QRTZ_SCHEDULER_STATE ( + sched_name varchar(120) not null comment '调度名称', + instance_name varchar(200) not null comment '实例名称', + last_checkin_time bigint(13) not null comment '上次检查时间', + checkin_interval bigint(13) not null comment '检查间隔时间', + primary key (sched_name, instance_name) +) engine=innodb comment = '调度器状态表'; + +-- ---------------------------- +-- 10、 存储程序的悲观锁的信息(假如使用了悲观锁) +-- ---------------------------- +create table QRTZ_LOCKS ( + sched_name varchar(120) not null comment '调度名称', + lock_name varchar(40) not null comment '悲观锁名称', + primary key (sched_name, lock_name) +) engine=innodb comment = '存储的悲观锁信息表'; + +-- ---------------------------- +-- 11、 Quartz集群实现同步机制的行锁表 +-- ---------------------------- +create table QRTZ_SIMPROP_TRIGGERS ( + sched_name varchar(120) not null comment '调度名称', + trigger_name varchar(200) not null comment 'qrtz_triggers表trigger_name的外键', + trigger_group varchar(200) not null comment 'qrtz_triggers表trigger_group的外键', + str_prop_1 varchar(512) null comment 'String类型的trigger的第一个参数', + str_prop_2 varchar(512) null comment 'String类型的trigger的第二个参数', + str_prop_3 varchar(512) null comment 'String类型的trigger的第三个参数', + int_prop_1 int null comment 'int类型的trigger的第一个参数', + int_prop_2 int null comment 'int类型的trigger的第二个参数', + long_prop_1 bigint null comment 'long类型的trigger的第一个参数', + long_prop_2 bigint null comment 'long类型的trigger的第二个参数', + dec_prop_1 numeric(13,4) null comment 'decimal类型的trigger的第一个参数', + dec_prop_2 numeric(13,4) null comment 'decimal类型的trigger的第二个参数', + bool_prop_1 varchar(1) null comment 'Boolean类型的trigger的第一个参数', + bool_prop_2 varchar(1) null comment 'Boolean类型的trigger的第二个参数', + primary key (sched_name, trigger_name, trigger_group), + foreign key (sched_name, trigger_name, trigger_group) references QRTZ_TRIGGERS(sched_name, trigger_name, trigger_group) +) engine=innodb comment = '同步机制的行锁表'; + +commit; \ No newline at end of file diff --git a/sql/ry_20240629.sql b/sql/ry_20240629.sql new file mode 100644 index 0000000..a8647d2 --- /dev/null +++ b/sql/ry_20240629.sql @@ -0,0 +1,701 @@ +-- ---------------------------- +-- 1、部门表 +-- ---------------------------- +drop table if exists sys_dept; +create table sys_dept ( + dept_id bigint(20) not null auto_increment comment '部门id', + parent_id bigint(20) default 0 comment '父部门id', + ancestors varchar(50) default '' comment '祖级列表', + dept_name varchar(30) default '' comment '部门名称', + order_num int(4) default 0 comment '显示顺序', + leader varchar(20) default null comment '负责人', + phone varchar(11) default null comment '联系电话', + email varchar(50) default null comment '邮箱', + status char(1) default '0' comment '部门状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (dept_id) +) engine=innodb auto_increment=200 comment = '部门表'; + +-- ---------------------------- +-- 初始化-部门表数据 +-- ---------------------------- +insert into sys_dept values(100, 0, '0', '若依科技', 0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(101, 100, '0,100', '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(102, 100, '0,100', '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(103, 101, '0,100,101', '研发部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(104, 101, '0,100,101', '市场部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(105, 101, '0,100,101', '测试部门', 3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(106, 101, '0,100,101', '财务部门', 4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(107, 101, '0,100,101', '运维部门', 5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(108, 102, '0,100,102', '市场部门', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); +insert into sys_dept values(109, 102, '0,100,102', '财务部门', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null); + + +-- ---------------------------- +-- 2、用户信息表 +-- ---------------------------- +drop table if exists sys_user; +create table sys_user ( + user_id bigint(20) not null auto_increment comment '用户ID', + dept_id bigint(20) default null comment '部门ID', + user_name varchar(30) not null comment '用户账号', + nick_name varchar(30) not null comment '用户昵称', + user_type varchar(2) default '00' comment '用户类型(00系统用户)', + email varchar(50) default '' comment '用户邮箱', + phonenumber varchar(11) default '' comment '手机号码', + sex char(1) default '0' comment '用户性别(0男 1女 2未知)', + avatar varchar(100) default '' comment '头像地址', + password varchar(100) default '' comment '密码', + status char(1) default '0' comment '帐号状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + login_ip varchar(128) default '' comment '最后登录IP', + login_date datetime comment '最后登录时间', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (user_id) +) engine=innodb auto_increment=100 comment = '用户信息表'; + +-- ---------------------------- +-- 初始化-用户信息表数据 +-- ---------------------------- +insert into sys_user values(1, 103, 'admin', '若依', '00', 'ry@163.com', '15888888888', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '管理员'); +insert into sys_user values(2, 105, 'ry', '若依', '00', 'ry@qq.com', '15666666666', '1', '', '$2a$10$7JB720yubVSZvUI0rEqK/.VqGOZTH.ulu33dHOiBE8ByOhJIrdAu2', '0', '0', '127.0.0.1', sysdate(), 'admin', sysdate(), '', null, '测试员'); + + +-- ---------------------------- +-- 3、岗位信息表 +-- ---------------------------- +drop table if exists sys_post; +create table sys_post +( + post_id bigint(20) not null auto_increment comment '岗位ID', + post_code varchar(64) not null comment '岗位编码', + post_name varchar(50) not null comment '岗位名称', + post_sort int(4) not null comment '显示顺序', + status char(1) not null comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (post_id) +) engine=innodb comment = '岗位信息表'; + +-- ---------------------------- +-- 初始化-岗位信息表数据 +-- ---------------------------- +insert into sys_post values(1, 'ceo', '董事长', 1, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(2, 'se', '项目经理', 2, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(3, 'hr', '人力资源', 3, '0', 'admin', sysdate(), '', null, ''); +insert into sys_post values(4, 'user', '普通员工', 4, '0', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 4、角色信息表 +-- ---------------------------- +drop table if exists sys_role; +create table sys_role ( + role_id bigint(20) not null auto_increment comment '角色ID', + role_name varchar(30) not null comment '角色名称', + role_key varchar(100) not null comment '角色权限字符串', + role_sort int(4) not null comment '显示顺序', + data_scope char(1) default '1' comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)', + menu_check_strictly tinyint(1) default 1 comment '菜单树选择项是否关联显示', + dept_check_strictly tinyint(1) default 1 comment '部门树选择项是否关联显示', + status char(1) not null comment '角色状态(0正常 1停用)', + del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (role_id) +) engine=innodb auto_increment=100 comment = '角色信息表'; + +-- ---------------------------- +-- 初始化-角色信息表数据 +-- ---------------------------- +insert into sys_role values('1', '超级管理员', 'admin', 1, 1, 1, 1, '0', '0', 'admin', sysdate(), '', null, '超级管理员'); +insert into sys_role values('2', '普通角色', 'common', 2, 2, 1, 1, '0', '0', 'admin', sysdate(), '', null, '普通角色'); + + +-- ---------------------------- +-- 5、菜单权限表 +-- ---------------------------- +drop table if exists sys_menu; +create table sys_menu ( + menu_id bigint(20) not null auto_increment comment '菜单ID', + menu_name varchar(50) not null comment '菜单名称', + parent_id bigint(20) default 0 comment '父菜单ID', + order_num int(4) default 0 comment '显示顺序', + path varchar(200) default '' comment '路由地址', + component varchar(255) default null comment '组件路径', + query varchar(255) default null comment '路由参数', + route_name varchar(50) default '' comment '路由名称', + is_frame int(1) default 1 comment '是否为外链(0是 1否)', + is_cache int(1) default 0 comment '是否缓存(0缓存 1不缓存)', + menu_type char(1) default '' comment '菜单类型(M目录 C菜单 F按钮)', + visible char(1) default 0 comment '菜单状态(0显示 1隐藏)', + status char(1) default 0 comment '菜单状态(0正常 1停用)', + perms varchar(100) default null comment '权限标识', + icon varchar(100) default '#' comment '菜单图标', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注', + primary key (menu_id) +) engine=innodb auto_increment=2000 comment = '菜单权限表'; + +-- ---------------------------- +-- 初始化-菜单信息表数据 +-- ---------------------------- +-- 一级菜单 +insert into sys_menu values('1', '系统管理', '0', '1', 'system', null, '', '', 1, 0, 'M', '0', '0', '', 'system', 'admin', sysdate(), '', null, '系统管理目录'); +insert into sys_menu values('2', '系统监控', '0', '2', 'monitor', null, '', '', 1, 0, 'M', '0', '0', '', 'monitor', 'admin', sysdate(), '', null, '系统监控目录'); +insert into sys_menu values('3', '系统工具', '0', '3', 'tool', null, '', '', 1, 0, 'M', '0', '0', '', 'tool', 'admin', sysdate(), '', null, '系统工具目录'); +insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', null, '', '', 0, 0, 'M', '0', '0', '', 'guide', 'admin', sysdate(), '', null, '若依官网地址'); +-- 二级菜单 +insert into sys_menu values('100', '用户管理', '1', '1', 'user', 'system/user/index', '', '', 1, 0, 'C', '0', '0', 'system:user:list', 'user', 'admin', sysdate(), '', null, '用户管理菜单'); +insert into sys_menu values('101', '角色管理', '1', '2', 'role', 'system/role/index', '', '', 1, 0, 'C', '0', '0', 'system:role:list', 'peoples', 'admin', sysdate(), '', null, '角色管理菜单'); +insert into sys_menu values('102', '菜单管理', '1', '3', 'menu', 'system/menu/index', '', '', 1, 0, 'C', '0', '0', 'system:menu:list', 'tree-table', 'admin', sysdate(), '', null, '菜单管理菜单'); +insert into sys_menu values('103', '部门管理', '1', '4', 'dept', 'system/dept/index', '', '', 1, 0, 'C', '0', '0', 'system:dept:list', 'tree', 'admin', sysdate(), '', null, '部门管理菜单'); +insert into sys_menu values('104', '岗位管理', '1', '5', 'post', 'system/post/index', '', '', 1, 0, 'C', '0', '0', 'system:post:list', 'post', 'admin', sysdate(), '', null, '岗位管理菜单'); +insert into sys_menu values('105', '字典管理', '1', '6', 'dict', 'system/dict/index', '', '', 1, 0, 'C', '0', '0', 'system:dict:list', 'dict', 'admin', sysdate(), '', null, '字典管理菜单'); +insert into sys_menu values('106', '参数设置', '1', '7', 'config', 'system/config/index', '', '', 1, 0, 'C', '0', '0', 'system:config:list', 'edit', 'admin', sysdate(), '', null, '参数设置菜单'); +insert into sys_menu values('107', '通知公告', '1', '8', 'notice', 'system/notice/index', '', '', 1, 0, 'C', '0', '0', 'system:notice:list', 'message', 'admin', sysdate(), '', null, '通知公告菜单'); +insert into sys_menu values('108', '日志管理', '1', '9', 'log', '', '', '', 1, 0, 'M', '0', '0', '', 'log', 'admin', sysdate(), '', null, '日志管理菜单'); +insert into sys_menu values('109', '在线用户', '2', '1', 'online', 'monitor/online/index', '', '', 1, 0, 'C', '0', '0', 'monitor:online:list', 'online', 'admin', sysdate(), '', null, '在线用户菜单'); +insert into sys_menu values('110', '定时任务', '2', '2', 'job', 'monitor/job/index', '', '', 1, 0, 'C', '0', '0', 'monitor:job:list', 'job', 'admin', sysdate(), '', null, '定时任务菜单'); +insert into sys_menu values('111', '数据监控', '2', '3', 'druid', 'monitor/druid/index', '', '', 1, 0, 'C', '0', '0', 'monitor:druid:list', 'druid', 'admin', sysdate(), '', null, '数据监控菜单'); +insert into sys_menu values('112', '服务监控', '2', '4', 'server', 'monitor/server/index', '', '', 1, 0, 'C', '0', '0', 'monitor:server:list', 'server', 'admin', sysdate(), '', null, '服务监控菜单'); +insert into sys_menu values('113', '缓存监控', '2', '5', 'cache', 'monitor/cache/index', '', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis', 'admin', sysdate(), '', null, '缓存监控菜单'); +insert into sys_menu values('114', '缓存列表', '2', '6', 'cacheList', 'monitor/cache/list', '', '', 1, 0, 'C', '0', '0', 'monitor:cache:list', 'redis-list', 'admin', sysdate(), '', null, '缓存列表菜单'); +insert into sys_menu values('115', '表单构建', '3', '1', 'build', 'tool/build/index', '', '', 1, 0, 'C', '0', '0', 'tool:build:list', 'build', 'admin', sysdate(), '', null, '表单构建菜单'); +insert into sys_menu values('116', '代码生成', '3', '2', 'gen', 'tool/gen/index', '', '', 1, 0, 'C', '0', '0', 'tool:gen:list', 'code', 'admin', sysdate(), '', null, '代码生成菜单'); +insert into sys_menu values('117', '系统接口', '3', '3', 'swagger', 'tool/swagger/index', '', '', 1, 0, 'C', '0', '0', 'tool:swagger:list', 'swagger', 'admin', sysdate(), '', null, '系统接口菜单'); +-- 三级菜单 +insert into sys_menu values('500', '操作日志', '108', '1', 'operlog', 'monitor/operlog/index', '', '', 1, 0, 'C', '0', '0', 'monitor:operlog:list', 'form', 'admin', sysdate(), '', null, '操作日志菜单'); +insert into sys_menu values('501', '登录日志', '108', '2', 'logininfor', 'monitor/logininfor/index', '', '', 1, 0, 'C', '0', '0', 'monitor:logininfor:list', 'logininfor', 'admin', sysdate(), '', null, '登录日志菜单'); +-- 用户管理按钮 +insert into sys_menu values('1000', '用户查询', '100', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1001', '用户新增', '100', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1002', '用户修改', '100', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1003', '用户删除', '100', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1004', '用户导出', '100', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1005', '用户导入', '100', '6', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1006', '重置密码', '100', '7', '', '', '', '', 1, 0, 'F', '0', '0', 'system:user:resetPwd', '#', 'admin', sysdate(), '', null, ''); +-- 角色管理按钮 +insert into sys_menu values('1007', '角色查询', '101', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1008', '角色新增', '101', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1009', '角色修改', '101', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1010', '角色删除', '101', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1011', '角色导出', '101', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:role:export', '#', 'admin', sysdate(), '', null, ''); +-- 菜单管理按钮 +insert into sys_menu values('1012', '菜单查询', '102', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1013', '菜单新增', '102', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1014', '菜单修改', '102', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1015', '菜单删除', '102', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove', '#', 'admin', sysdate(), '', null, ''); +-- 部门管理按钮 +insert into sys_menu values('1016', '部门查询', '103', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1017', '部门新增', '103', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1018', '部门修改', '103', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1019', '部门删除', '103', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove', '#', 'admin', sysdate(), '', null, ''); +-- 岗位管理按钮 +insert into sys_menu values('1020', '岗位查询', '104', '1', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1021', '岗位新增', '104', '2', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1022', '岗位修改', '104', '3', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1023', '岗位删除', '104', '4', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1024', '岗位导出', '104', '5', '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:export', '#', 'admin', sysdate(), '', null, ''); +-- 字典管理按钮 +insert into sys_menu values('1025', '字典查询', '105', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1026', '字典新增', '105', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1027', '字典修改', '105', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1028', '字典删除', '105', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1029', '字典导出', '105', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:dict:export', '#', 'admin', sysdate(), '', null, ''); +-- 参数设置按钮 +insert into sys_menu values('1030', '参数查询', '106', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1031', '参数新增', '106', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1032', '参数修改', '106', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1033', '参数删除', '106', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1034', '参数导出', '106', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:config:export', '#', 'admin', sysdate(), '', null, ''); +-- 通知公告按钮 +insert into sys_menu values('1035', '公告查询', '107', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1036', '公告新增', '107', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1037', '公告修改', '107', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1038', '公告删除', '107', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'system:notice:remove', '#', 'admin', sysdate(), '', null, ''); +-- 操作日志按钮 +insert into sys_menu values('1039', '操作查询', '500', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1040', '操作删除', '500', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1041', '日志导出', '500', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:operlog:export', '#', 'admin', sysdate(), '', null, ''); +-- 登录日志按钮 +insert into sys_menu values('1042', '登录查询', '501', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1043', '登录删除', '501', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1044', '日志导出', '501', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:export', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1045', '账户解锁', '501', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:logininfor:unlock', '#', 'admin', sysdate(), '', null, ''); +-- 在线用户按钮 +insert into sys_menu values('1046', '在线查询', '109', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:online:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1047', '批量强退', '109', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:online:batchLogout', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1048', '单条强退', '109', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:online:forceLogout', '#', 'admin', sysdate(), '', null, ''); +-- 定时任务按钮 +insert into sys_menu values('1049', '任务查询', '110', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1050', '任务新增', '110', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:add', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1051', '任务修改', '110', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1052', '任务删除', '110', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1053', '状态修改', '110', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:changeStatus', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1054', '任务导出', '110', '6', '#', '', '', '', 1, 0, 'F', '0', '0', 'monitor:job:export', '#', 'admin', sysdate(), '', null, ''); +-- 代码生成按钮 +insert into sys_menu values('1055', '生成查询', '116', '1', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:query', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1056', '生成修改', '116', '2', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:edit', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1057', '生成删除', '116', '3', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:remove', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1058', '导入代码', '116', '4', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:import', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1059', '预览代码', '116', '5', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:preview', '#', 'admin', sysdate(), '', null, ''); +insert into sys_menu values('1060', '生成代码', '116', '6', '#', '', '', '', 1, 0, 'F', '0', '0', 'tool:gen:code', '#', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 6、用户和角色关联表 用户N-1角色 +-- ---------------------------- +drop table if exists sys_user_role; +create table sys_user_role ( + user_id bigint(20) not null comment '用户ID', + role_id bigint(20) not null comment '角色ID', + primary key(user_id, role_id) +) engine=innodb comment = '用户和角色关联表'; + +-- ---------------------------- +-- 初始化-用户和角色关联表数据 +-- ---------------------------- +insert into sys_user_role values ('1', '1'); +insert into sys_user_role values ('2', '2'); + + +-- ---------------------------- +-- 7、角色和菜单关联表 角色1-N菜单 +-- ---------------------------- +drop table if exists sys_role_menu; +create table sys_role_menu ( + role_id bigint(20) not null comment '角色ID', + menu_id bigint(20) not null comment '菜单ID', + primary key(role_id, menu_id) +) engine=innodb comment = '角色和菜单关联表'; + +-- ---------------------------- +-- 初始化-角色和菜单关联表数据 +-- ---------------------------- +insert into sys_role_menu values ('2', '1'); +insert into sys_role_menu values ('2', '2'); +insert into sys_role_menu values ('2', '3'); +insert into sys_role_menu values ('2', '4'); +insert into sys_role_menu values ('2', '100'); +insert into sys_role_menu values ('2', '101'); +insert into sys_role_menu values ('2', '102'); +insert into sys_role_menu values ('2', '103'); +insert into sys_role_menu values ('2', '104'); +insert into sys_role_menu values ('2', '105'); +insert into sys_role_menu values ('2', '106'); +insert into sys_role_menu values ('2', '107'); +insert into sys_role_menu values ('2', '108'); +insert into sys_role_menu values ('2', '109'); +insert into sys_role_menu values ('2', '110'); +insert into sys_role_menu values ('2', '111'); +insert into sys_role_menu values ('2', '112'); +insert into sys_role_menu values ('2', '113'); +insert into sys_role_menu values ('2', '114'); +insert into sys_role_menu values ('2', '115'); +insert into sys_role_menu values ('2', '116'); +insert into sys_role_menu values ('2', '117'); +insert into sys_role_menu values ('2', '500'); +insert into sys_role_menu values ('2', '501'); +insert into sys_role_menu values ('2', '1000'); +insert into sys_role_menu values ('2', '1001'); +insert into sys_role_menu values ('2', '1002'); +insert into sys_role_menu values ('2', '1003'); +insert into sys_role_menu values ('2', '1004'); +insert into sys_role_menu values ('2', '1005'); +insert into sys_role_menu values ('2', '1006'); +insert into sys_role_menu values ('2', '1007'); +insert into sys_role_menu values ('2', '1008'); +insert into sys_role_menu values ('2', '1009'); +insert into sys_role_menu values ('2', '1010'); +insert into sys_role_menu values ('2', '1011'); +insert into sys_role_menu values ('2', '1012'); +insert into sys_role_menu values ('2', '1013'); +insert into sys_role_menu values ('2', '1014'); +insert into sys_role_menu values ('2', '1015'); +insert into sys_role_menu values ('2', '1016'); +insert into sys_role_menu values ('2', '1017'); +insert into sys_role_menu values ('2', '1018'); +insert into sys_role_menu values ('2', '1019'); +insert into sys_role_menu values ('2', '1020'); +insert into sys_role_menu values ('2', '1021'); +insert into sys_role_menu values ('2', '1022'); +insert into sys_role_menu values ('2', '1023'); +insert into sys_role_menu values ('2', '1024'); +insert into sys_role_menu values ('2', '1025'); +insert into sys_role_menu values ('2', '1026'); +insert into sys_role_menu values ('2', '1027'); +insert into sys_role_menu values ('2', '1028'); +insert into sys_role_menu values ('2', '1029'); +insert into sys_role_menu values ('2', '1030'); +insert into sys_role_menu values ('2', '1031'); +insert into sys_role_menu values ('2', '1032'); +insert into sys_role_menu values ('2', '1033'); +insert into sys_role_menu values ('2', '1034'); +insert into sys_role_menu values ('2', '1035'); +insert into sys_role_menu values ('2', '1036'); +insert into sys_role_menu values ('2', '1037'); +insert into sys_role_menu values ('2', '1038'); +insert into sys_role_menu values ('2', '1039'); +insert into sys_role_menu values ('2', '1040'); +insert into sys_role_menu values ('2', '1041'); +insert into sys_role_menu values ('2', '1042'); +insert into sys_role_menu values ('2', '1043'); +insert into sys_role_menu values ('2', '1044'); +insert into sys_role_menu values ('2', '1045'); +insert into sys_role_menu values ('2', '1046'); +insert into sys_role_menu values ('2', '1047'); +insert into sys_role_menu values ('2', '1048'); +insert into sys_role_menu values ('2', '1049'); +insert into sys_role_menu values ('2', '1050'); +insert into sys_role_menu values ('2', '1051'); +insert into sys_role_menu values ('2', '1052'); +insert into sys_role_menu values ('2', '1053'); +insert into sys_role_menu values ('2', '1054'); +insert into sys_role_menu values ('2', '1055'); +insert into sys_role_menu values ('2', '1056'); +insert into sys_role_menu values ('2', '1057'); +insert into sys_role_menu values ('2', '1058'); +insert into sys_role_menu values ('2', '1059'); +insert into sys_role_menu values ('2', '1060'); + +-- ---------------------------- +-- 8、角色和部门关联表 角色1-N部门 +-- ---------------------------- +drop table if exists sys_role_dept; +create table sys_role_dept ( + role_id bigint(20) not null comment '角色ID', + dept_id bigint(20) not null comment '部门ID', + primary key(role_id, dept_id) +) engine=innodb comment = '角色和部门关联表'; + +-- ---------------------------- +-- 初始化-角色和部门关联表数据 +-- ---------------------------- +insert into sys_role_dept values ('2', '100'); +insert into sys_role_dept values ('2', '101'); +insert into sys_role_dept values ('2', '105'); + + +-- ---------------------------- +-- 9、用户与岗位关联表 用户1-N岗位 +-- ---------------------------- +drop table if exists sys_user_post; +create table sys_user_post +( + user_id bigint(20) not null comment '用户ID', + post_id bigint(20) not null comment '岗位ID', + primary key (user_id, post_id) +) engine=innodb comment = '用户与岗位关联表'; + +-- ---------------------------- +-- 初始化-用户与岗位关联表数据 +-- ---------------------------- +insert into sys_user_post values ('1', '1'); +insert into sys_user_post values ('2', '2'); + + +-- ---------------------------- +-- 10、操作日志记录 +-- ---------------------------- +drop table if exists sys_oper_log; +create table sys_oper_log ( + oper_id bigint(20) not null auto_increment comment '日志主键', + title varchar(50) default '' comment '模块标题', + business_type int(2) default 0 comment '业务类型(0其它 1新增 2修改 3删除)', + method varchar(200) default '' comment '方法名称', + request_method varchar(10) default '' comment '请求方式', + operator_type int(1) default 0 comment '操作类别(0其它 1后台用户 2手机端用户)', + oper_name varchar(50) default '' comment '操作人员', + dept_name varchar(50) default '' comment '部门名称', + oper_url varchar(255) default '' comment '请求URL', + oper_ip varchar(128) default '' comment '主机地址', + oper_location varchar(255) default '' comment '操作地点', + oper_param varchar(2000) default '' comment '请求参数', + json_result varchar(2000) default '' comment '返回参数', + status int(1) default 0 comment '操作状态(0正常 1异常)', + error_msg varchar(2000) default '' comment '错误消息', + oper_time datetime comment '操作时间', + cost_time bigint(20) default 0 comment '消耗时间', + primary key (oper_id), + key idx_sys_oper_log_bt (business_type), + key idx_sys_oper_log_s (status), + key idx_sys_oper_log_ot (oper_time) +) engine=innodb auto_increment=100 comment = '操作日志记录'; + + +-- ---------------------------- +-- 11、字典类型表 +-- ---------------------------- +drop table if exists sys_dict_type; +create table sys_dict_type +( + dict_id bigint(20) not null auto_increment comment '字典主键', + dict_name varchar(100) default '' comment '字典名称', + dict_type varchar(100) default '' comment '字典类型', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_id), + unique (dict_type) +) engine=innodb auto_increment=100 comment = '字典类型表'; + +insert into sys_dict_type values(1, '用户性别', 'sys_user_sex', '0', 'admin', sysdate(), '', null, '用户性别列表'); +insert into sys_dict_type values(2, '菜单状态', 'sys_show_hide', '0', 'admin', sysdate(), '', null, '菜单状态列表'); +insert into sys_dict_type values(3, '系统开关', 'sys_normal_disable', '0', 'admin', sysdate(), '', null, '系统开关列表'); +insert into sys_dict_type values(4, '任务状态', 'sys_job_status', '0', 'admin', sysdate(), '', null, '任务状态列表'); +insert into sys_dict_type values(5, '任务分组', 'sys_job_group', '0', 'admin', sysdate(), '', null, '任务分组列表'); +insert into sys_dict_type values(6, '系统是否', 'sys_yes_no', '0', 'admin', sysdate(), '', null, '系统是否列表'); +insert into sys_dict_type values(7, '通知类型', 'sys_notice_type', '0', 'admin', sysdate(), '', null, '通知类型列表'); +insert into sys_dict_type values(8, '通知状态', 'sys_notice_status', '0', 'admin', sysdate(), '', null, '通知状态列表'); +insert into sys_dict_type values(9, '操作类型', 'sys_oper_type', '0', 'admin', sysdate(), '', null, '操作类型列表'); +insert into sys_dict_type values(10, '系统状态', 'sys_common_status', '0', 'admin', sysdate(), '', null, '登录状态列表'); + + +-- ---------------------------- +-- 12、字典数据表 +-- ---------------------------- +drop table if exists sys_dict_data; +create table sys_dict_data +( + dict_code bigint(20) not null auto_increment comment '字典编码', + dict_sort int(4) default 0 comment '字典排序', + dict_label varchar(100) default '' comment '字典标签', + dict_value varchar(100) default '' comment '字典键值', + dict_type varchar(100) default '' comment '字典类型', + css_class varchar(100) default null comment '样式属性(其他样式扩展)', + list_class varchar(100) default null comment '表格回显样式', + is_default char(1) default 'N' comment '是否默认(Y是 N否)', + status char(1) default '0' comment '状态(0正常 1停用)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (dict_code) +) engine=innodb auto_increment=100 comment = '字典数据表'; + +insert into sys_dict_data values(1, 1, '男', '0', 'sys_user_sex', '', '', 'Y', '0', 'admin', sysdate(), '', null, '性别男'); +insert into sys_dict_data values(2, 2, '女', '1', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别女'); +insert into sys_dict_data values(3, 3, '未知', '2', 'sys_user_sex', '', '', 'N', '0', 'admin', sysdate(), '', null, '性别未知'); +insert into sys_dict_data values(4, 1, '显示', '0', 'sys_show_hide', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '显示菜单'); +insert into sys_dict_data values(5, 2, '隐藏', '1', 'sys_show_hide', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '隐藏菜单'); +insert into sys_dict_data values(6, 1, '正常', '0', 'sys_normal_disable', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(7, 2, '停用', '1', 'sys_normal_disable', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(8, 1, '正常', '0', 'sys_job_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(9, 2, '暂停', '1', 'sys_job_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); +insert into sys_dict_data values(10, 1, '默认', 'DEFAULT', 'sys_job_group', '', '', 'Y', '0', 'admin', sysdate(), '', null, '默认分组'); +insert into sys_dict_data values(11, 2, '系统', 'SYSTEM', 'sys_job_group', '', '', 'N', '0', 'admin', sysdate(), '', null, '系统分组'); +insert into sys_dict_data values(12, 1, '是', 'Y', 'sys_yes_no', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '系统默认是'); +insert into sys_dict_data values(13, 2, '否', 'N', 'sys_yes_no', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '系统默认否'); +insert into sys_dict_data values(14, 1, '通知', '1', 'sys_notice_type', '', 'warning', 'Y', '0', 'admin', sysdate(), '', null, '通知'); +insert into sys_dict_data values(15, 2, '公告', '2', 'sys_notice_type', '', 'success', 'N', '0', 'admin', sysdate(), '', null, '公告'); +insert into sys_dict_data values(16, 1, '正常', '0', 'sys_notice_status', '', 'primary', 'Y', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(17, 2, '关闭', '1', 'sys_notice_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '关闭状态'); +insert into sys_dict_data values(18, 99, '其他', '0', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '其他操作'); +insert into sys_dict_data values(19, 1, '新增', '1', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '新增操作'); +insert into sys_dict_data values(20, 2, '修改', '2', 'sys_oper_type', '', 'info', 'N', '0', 'admin', sysdate(), '', null, '修改操作'); +insert into sys_dict_data values(21, 3, '删除', '3', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '删除操作'); +insert into sys_dict_data values(22, 4, '授权', '4', 'sys_oper_type', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '授权操作'); +insert into sys_dict_data values(23, 5, '导出', '5', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导出操作'); +insert into sys_dict_data values(24, 6, '导入', '6', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '导入操作'); +insert into sys_dict_data values(25, 7, '强退', '7', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '强退操作'); +insert into sys_dict_data values(26, 8, '生成代码', '8', 'sys_oper_type', '', 'warning', 'N', '0', 'admin', sysdate(), '', null, '生成操作'); +insert into sys_dict_data values(27, 9, '清空数据', '9', 'sys_oper_type', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '清空操作'); +insert into sys_dict_data values(28, 1, '成功', '0', 'sys_common_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', null, '正常状态'); +insert into sys_dict_data values(29, 2, '失败', '1', 'sys_common_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', null, '停用状态'); + + +-- ---------------------------- +-- 13、参数配置表 +-- ---------------------------- +drop table if exists sys_config; +create table sys_config ( + config_id int(5) not null auto_increment comment '参数主键', + config_name varchar(100) default '' comment '参数名称', + config_key varchar(100) default '' comment '参数键名', + config_value varchar(500) default '' comment '参数键值', + config_type char(1) default 'N' comment '系统内置(Y是 N否)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (config_id) +) engine=innodb auto_increment=100 comment = '参数配置表'; + +insert into sys_config values(1, '主框架页-默认皮肤样式名称', 'sys.index.skinName', 'skin-blue', 'Y', 'admin', sysdate(), '', null, '蓝色 skin-blue、绿色 skin-green、紫色 skin-purple、红色 skin-red、黄色 skin-yellow' ); +insert into sys_config values(2, '用户管理-账号初始密码', 'sys.user.initPassword', '123456', 'Y', 'admin', sysdate(), '', null, '初始化密码 123456' ); +insert into sys_config values(3, '主框架页-侧边栏主题', 'sys.index.sideTheme', 'theme-dark', 'Y', 'admin', sysdate(), '', null, '深色主题theme-dark,浅色主题theme-light' ); +insert into sys_config values(4, '账号自助-验证码开关', 'sys.account.captchaEnabled', 'true', 'Y', 'admin', sysdate(), '', null, '是否开启验证码功能(true开启,false关闭)'); +insert into sys_config values(5, '账号自助-是否开启用户注册功能', 'sys.account.registerUser', 'false', 'Y', 'admin', sysdate(), '', null, '是否开启注册用户功能(true开启,false关闭)'); +insert into sys_config values(6, '用户登录-黑名单列表', 'sys.login.blackIPList', '', 'Y', 'admin', sysdate(), '', null, '设置登录IP黑名单限制,多个匹配项以;分隔,支持匹配(*通配、网段)'); + + +-- ---------------------------- +-- 14、系统访问记录 +-- ---------------------------- +drop table if exists sys_logininfor; +create table sys_logininfor ( + info_id bigint(20) not null auto_increment comment '访问ID', + user_name varchar(50) default '' comment '用户账号', + ipaddr varchar(128) default '' comment '登录IP地址', + login_location varchar(255) default '' comment '登录地点', + browser varchar(50) default '' comment '浏览器类型', + os varchar(50) default '' comment '操作系统', + status char(1) default '0' comment '登录状态(0成功 1失败)', + msg varchar(255) default '' comment '提示消息', + login_time datetime comment '访问时间', + primary key (info_id), + key idx_sys_logininfor_s (status), + key idx_sys_logininfor_lt (login_time) +) engine=innodb auto_increment=100 comment = '系统访问记录'; + + +-- ---------------------------- +-- 15、定时任务调度表 +-- ---------------------------- +drop table if exists sys_job; +create table sys_job ( + job_id bigint(20) not null auto_increment comment '任务ID', + job_name varchar(64) default '' comment '任务名称', + job_group varchar(64) default 'DEFAULT' comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + cron_expression varchar(255) default '' comment 'cron执行表达式', + misfire_policy varchar(20) default '3' comment '计划执行错误策略(1立即执行 2执行一次 3放弃执行)', + concurrent char(1) default '1' comment '是否并发执行(0允许 1禁止)', + status char(1) default '0' comment '状态(0正常 1暂停)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default '' comment '备注信息', + primary key (job_id, job_name, job_group) +) engine=innodb auto_increment=100 comment = '定时任务调度表'; + +insert into sys_job values(1, '系统默认(无参)', 'DEFAULT', 'ryTask.ryNoParams', '0/10 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(2, '系统默认(有参)', 'DEFAULT', 'ryTask.ryParams(\'ry\')', '0/15 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); +insert into sys_job values(3, '系统默认(多参)', 'DEFAULT', 'ryTask.ryMultipleParams(\'ry\', true, 2000L, 316.50D, 100)', '0/20 * * * * ?', '3', '1', '1', 'admin', sysdate(), '', null, ''); + + +-- ---------------------------- +-- 16、定时任务调度日志表 +-- ---------------------------- +drop table if exists sys_job_log; +create table sys_job_log ( + job_log_id bigint(20) not null auto_increment comment '任务日志ID', + job_name varchar(64) not null comment '任务名称', + job_group varchar(64) not null comment '任务组名', + invoke_target varchar(500) not null comment '调用目标字符串', + job_message varchar(500) comment '日志信息', + status char(1) default '0' comment '执行状态(0正常 1失败)', + exception_info varchar(2000) default '' comment '异常信息', + create_time datetime comment '创建时间', + primary key (job_log_id) +) engine=innodb comment = '定时任务调度日志表'; + + +-- ---------------------------- +-- 17、通知公告表 +-- ---------------------------- +drop table if exists sys_notice; +create table sys_notice ( + notice_id int(4) not null auto_increment comment '公告ID', + notice_title varchar(50) not null comment '公告标题', + notice_type char(1) not null comment '公告类型(1通知 2公告)', + notice_content longblob default null comment '公告内容', + status char(1) default '0' comment '公告状态(0正常 1关闭)', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(255) default null comment '备注', + primary key (notice_id) +) engine=innodb auto_increment=10 comment = '通知公告表'; + +-- ---------------------------- +-- 初始化-公告信息表数据 +-- ---------------------------- +insert into sys_notice values('1', '温馨提醒:2018-07-01 若依新版本发布啦', '2', '新版本内容', '0', 'admin', sysdate(), '', null, '管理员'); +insert into sys_notice values('2', '维护通知:2018-07-01 若依系统凌晨维护', '1', '维护内容', '0', 'admin', sysdate(), '', null, '管理员'); + + +-- ---------------------------- +-- 18、代码生成业务表 +-- ---------------------------- +drop table if exists gen_table; +create table gen_table ( + table_id bigint(20) not null auto_increment comment '编号', + table_name varchar(200) default '' comment '表名称', + table_comment varchar(500) default '' comment '表描述', + sub_table_name varchar(64) default null comment '关联子表的表名', + sub_table_fk_name varchar(64) default null comment '子表关联的外键名', + class_name varchar(100) default '' comment '实体类名称', + tpl_category varchar(200) default 'crud' comment '使用的模板(crud单表操作 tree树表操作)', + tpl_web_type varchar(30) default '' comment '前端模板类型(element-ui模版 element-plus模版)', + package_name varchar(100) comment '生成包路径', + module_name varchar(30) comment '生成模块名', + business_name varchar(30) comment '生成业务名', + function_name varchar(50) comment '生成功能名', + function_author varchar(50) comment '生成功能作者', + gen_type char(1) default '0' comment '生成代码方式(0zip压缩包 1自定义路径)', + gen_path varchar(200) default '/' comment '生成路径(不填默认项目路径)', + options varchar(1000) comment '其它生成选项', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + remark varchar(500) default null comment '备注', + primary key (table_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表'; + + +-- ---------------------------- +-- 19、代码生成业务表字段 +-- ---------------------------- +drop table if exists gen_table_column; +create table gen_table_column ( + column_id bigint(20) not null auto_increment comment '编号', + table_id bigint(20) comment '归属表编号', + column_name varchar(200) comment '列名称', + column_comment varchar(500) comment '列描述', + column_type varchar(100) comment '列类型', + java_type varchar(500) comment 'JAVA类型', + java_field varchar(200) comment 'JAVA字段名', + is_pk char(1) comment '是否主键(1是)', + is_increment char(1) comment '是否自增(1是)', + is_required char(1) comment '是否必填(1是)', + is_insert char(1) comment '是否为插入字段(1是)', + is_edit char(1) comment '是否编辑字段(1是)', + is_list char(1) comment '是否列表字段(1是)', + is_query char(1) comment '是否查询字段(1是)', + query_type varchar(200) default 'EQ' comment '查询方式(等于、不等于、大于、小于、范围)', + html_type varchar(200) comment '显示类型(文本框、文本域、下拉框、复选框、单选框、日期控件)', + dict_type varchar(200) default '' comment '字典类型', + sort int comment '排序', + create_by varchar(64) default '' comment '创建者', + create_time datetime comment '创建时间', + update_by varchar(64) default '' comment '更新者', + update_time datetime comment '更新时间', + primary key (column_id) +) engine=innodb auto_increment=1 comment = '代码生成业务表字段'; \ No newline at end of file