diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..184be5f50 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +###################################################################### +# Build Tools + +.gradle +/build/ +!gradle/wrapper/gradle-wrapper.jar + +target/ +!.mvn/wrapper/maven-wrapper.jar + +###################################################################### +# IDE + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +nbproject/private/ +build/* +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +###################################################################### +# Others +*.log +*.xml.versionsBackup + +!*/build/*.java +!*/build/*.html +!*/build/*.xml + +application-druid.yml diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..8564f294c --- /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 000000000..6ae503cd1 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +## 平台简介 + +一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适的。于是利用空闲休息时间开始自己写了一套后台系统。如此有了若依。她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。 + +性别男,若依是给女儿取的名字(寓意:你若不离不弃,我必生死相依) + +若依是一套全部开源的快速开发平台,毫无保留给个人及企业免费使用。 + +* 前后端分离版本,请移步[RuoYi-Vue](https://gitee.com/y_project/RuoYi-Vue),微服务版本,请移步[RuoYi-Cloud](https://gitee.com/y_project/RuoYi-Cloud) +* 感谢 [hplus](https://gitee.com/hplus_admin/hplus) 后台主题 UI 框架。 +* 阿里云折扣场:[点我进入](http://aly.ruoyi.vip),腾讯云秒杀场:[点我进入](http://txy.ruoyi.vip)   +* 阿里云优惠券:[点我领取](https://www.aliyun.com/minisite/goods?userCode=brki8iof&share_source=copy_link),腾讯云优惠券:[点我领取](https://cloud.tencent.com/redirect.php?redirect=1025&cps_key=198c8df2ed259157187173bc7f4f32fd&from=console)   + +## 内置功能 + +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://ruoyi.vip +文档地址:http://doc.ruoyi.vip + +## 演示图 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +## 若依交流群 + +QQ群: [![加入QQ群](https://img.shields.io/badge/已满-1389287-blue.svg)](https://jq.qq.com/?_wv=1027&k=5HBAaYN) [![加入QQ群](https://img.shields.io/badge/已满-1679294-blue.svg)](https://jq.qq.com/?_wv=1027&k=5cHeRVW) [![加入QQ群](https://img.shields.io/badge/已满-1529866-blue.svg)](https://jq.qq.com/?_wv=1027&k=53R0L5Z) [![加入QQ群](https://img.shields.io/badge/已满-1772718-blue.svg)](https://jq.qq.com/?_wv=1027&k=5g75dCU) [![加入QQ群](https://img.shields.io/badge/已满-1366522-blue.svg)](https://jq.qq.com/?_wv=1027&k=58cPoHA) [![加入QQ群](https://img.shields.io/badge/已满-1382251-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Ofd4Pb) [![加入QQ群](https://img.shields.io/badge/已满-1145125-blue.svg)](https://jq.qq.com/?_wv=1027&k=5yugASz) [![加入QQ群](https://img.shields.io/badge/已满-86752435-blue.svg)](https://jq.qq.com/?_wv=1027&k=5Rf3d2P) [![加入QQ群](https://img.shields.io/badge/已满-134072510-blue.svg)](https://jq.qq.com/?_wv=1027&k=5ZIjaeP) [![加入QQ群](https://img.shields.io/badge/已满-210336300-blue.svg)](https://jq.qq.com/?_wv=1027&k=5CJw1jY) [![加入QQ群](https://img.shields.io/badge/已满-339522636-blue.svg)](https://jq.qq.com/?_wv=1027&k=5omzbKc) [![加入QQ群](https://img.shields.io/badge/已满-130035985-blue.svg)](https://jq.qq.com/?_wv=1027&k=qPIKBb7s) [![加入QQ群](https://img.shields.io/badge/已满-143151071-blue.svg)](https://jq.qq.com/?_wv=1027&k=4NsjKbtU) [![加入QQ群](https://img.shields.io/badge/已满-158781320-blue.svg)](https://jq.qq.com/?_wv=1027&k=VD2pkz2G) [![加入QQ群](https://img.shields.io/badge/201531282-blue.svg)](https://jq.qq.com/?_wv=1027&k=HlshFwkJ) \ No newline at end of file diff --git a/bin/clean.bat b/bin/clean.bat new file mode 100644 index 000000000..24c09741e --- /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 000000000..c693ec067 --- /dev/null +++ b/bin/package.bat @@ -0,0 +1,12 @@ +@echo off +echo. +echo [Ϣ] Weḅ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 000000000..41efbd0f3 --- /dev/null +++ b/bin/run.bat @@ -0,0 +1,14 @@ +@echo off +echo. +echo [Ϣ] ʹJarWeb̡ +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/box-bps/pom.xml b/box-bps/pom.xml new file mode 100644 index 000000000..5cc6d8bad --- /dev/null +++ b/box-bps/pom.xml @@ -0,0 +1,47 @@ + + + + ruoyi + com.ruoyi + 4.6.2 + + 4.0.0 + + box-bps + + + bps系统模块 + + + + + + + com.ruoyi + ruoyi-common + + + + org.json + json + 20160810 + + + + io.swagger + swagger-annotations + 1.5.21 + compile + + + + com.github.kuaidi100-api + sdk + 1.0.2 + + + + + \ No newline at end of file diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpImportQueryController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpImportQueryController.java new file mode 100644 index 000000000..07ad24b84 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpImportQueryController.java @@ -0,0 +1,186 @@ +package com.ruoyi.bps.controller; + +import com.ruoyi.bps.domain.ExpImportQuery; +import com.ruoyi.bps.domain.ExpressInfo; +import com.ruoyi.bps.mapper.ExpressInfoMapper; +import com.ruoyi.bps.service.IExpImportQueryService; +import com.ruoyi.bps.service.IExpressInfoService; +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 org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * Excel批量快递查询Controller + * + * @author Bo + * @date 2021-07-21 + */ +@Controller +@RequestMapping("/bps/expImportQuery") +public class ExpImportQueryController extends BaseController +{ + private String prefix = "bps/expImportQuery"; + + @Autowired + private IExpImportQueryService expImportQueryService; + + @Autowired + private IExpressInfoService expressInfoService; + + @Autowired + private ExpressInfoMapper expressInfoMapper; + + @RequiresPermissions("bps:expImportQuery:view") + @GetMapping() + public String expImportQuery() + { + return prefix + "/expImportQuery"; + } + + /** + * 查询Excel批量快递查询列表 + */ + @RequiresPermissions("bps:expImportQuery:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(ExpImportQuery expImportQuery) + { + startPage(); + List list = expImportQueryService.selectExpImportQueryList(expImportQuery); + return getDataTable(list); + } + + /** + * 导出Excel批量快递查询列表 + */ + @RequiresPermissions("bps:expImportQuery:export") + @Log(title = "Excel批量快递查询", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(ExpImportQuery expImportQuery) + { + List list = expImportQueryService.selectExpImportQueryList(expImportQuery); + ExcelUtil util = new ExcelUtil(ExpImportQuery.class); + return util.exportExcel(list, "Excel批量快递查询数据"); + } + + + /** + * 导出Excel批量快递查询列表 + */ + @RequiresPermissions("bps:expressInfo:export") + @Log(title = "详细快递信息导出", businessType = BusinessType.EXPORT) + @PostMapping("/exportDetail") + @ResponseBody + public AjaxResult exportDetail(ExpressInfo expressInfo) + { + List list = expressInfoService.selectLocalExpressInfoList(expressInfo); + ExcelUtil util = new ExcelUtil(ExpressInfo.class); + return util.exportExcel(list, "Excel批量快递查询数据"); + } + + /** + * 新增Excel批量快递查询 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存Excel批量快递查询 + */ + @RequiresPermissions("bps:expImportQuery:add") + @Log(title = "Excel批量快递查询", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(ExpImportQuery expImportQuery) + { + return toAjax(expImportQueryService.insertExpImportQuery(expImportQuery)); + } + + /** + * 修改Excel批量快递查询 + */ + @GetMapping("/edit/{sid}") + public String edit(@PathVariable("sid") Long sid, ModelMap mmap) + { + ExpImportQuery expImportQuery = expImportQueryService.selectExpImportQueryById(sid); + mmap.put("expImportQuery", expImportQuery); + return prefix + "/edit"; + } + + /** + * 修改保存Excel批量快递查询 + */ + @RequiresPermissions("bps:expImportQuery:edit") + @Log(title = "Excel批量快递查询", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(ExpImportQuery expImportQuery) + { + return toAjax(expImportQueryService.updateExpImportQuery(expImportQuery)); + } + + /** + * 删除Excel批量快递查询 + */ + @RequiresPermissions("bps:expImportQuery:remove") + @Log(title = "Excel批量快递查询", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(expImportQueryService.deleteExpImportQueryByIds(ids)); + } + + + /** + * 快递查询明细信息 + */ + @RequiresPermissions("bps:expImportQuery:detail") + @GetMapping("/detail/{sid}") + public String detail(@PathVariable("sid") Long sid, ModelMap mmap) + { + String queryId = expImportQueryService.selectExpImportQueryById(sid).getQueryId(); + ExpressInfo expressInfo= new ExpressInfo(); + expressInfo.setQueryId(queryId); + mmap.put("expressInfo",expressInfo); + return prefix + "/detail"; + } + + /** + * Excel导入查模板下载 + */ + @GetMapping ( "/importTemplate" ) + @ResponseBody + public AjaxResult importTemplate ( ) { + ExcelUtil util = new ExcelUtil<>(ExpressInfo.class); + return util.importTemplateExcel ( "快递查询导入模板" ); + } + + /** + * Excel导入查询 + */ + @PostMapping("/importData") + @Log(title = "Excel批量导入快递查询", businessType = BusinessType.IMPORT) + @ResponseBody + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util= new ExcelUtil(ExpressInfo.class); + List expressInfoList=util.importExcel(file.getInputStream()); + return expImportQueryService.importData(expressInfoList); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushApiController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushApiController.java new file mode 100644 index 000000000..8bb2ae3d1 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushApiController.java @@ -0,0 +1,88 @@ +package com.ruoyi.bps.controller; + +import com.kuaidi100.sdk.response.SubscribeResp; +import com.ruoyi.bps.domain.ExpSubscribe; +import com.ruoyi.bps.service.IExpSubsPushApiService; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.DateUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +/** + * 接受快递推送信息的API接口Controller + * + * @author box + * @date 2021-05-13 + */ +@Api(value = "快递信息订阅推送",tags = "快递订阅接口") +@RestController +/*@RequestMapping("/anon")*/ +public class ExpSubsPushApiController extends BaseController { + @Autowired + IExpSubsPushApiService expSubsPushApiService; + + //快递100推送 + @CrossOrigin + @PostMapping("anon/subscribeCallBackUrl/{salt}") + @ApiOperation("快递信息订阅推送接受") + public SubscribeResp SubscribeCallBackUrl(@PathVariable("salt") String salt, HttpServletRequest request) { + return expSubsPushApiService.ExpressSubscribeCallBackUrl(request,salt); + } + + + //订阅 + @CrossOrigin + @PostMapping("anon/subscribe") + public SubscribeResp Subscribe(ExpSubscribe expSubscribe){ + return expSubsPushApiService.ExpressSubscribe(expSubscribe); + } + + //接受topgp订阅, + @Log(title = "快递订阅", businessType = BusinessType.OTHER) + @CrossOrigin + @ApiOperation(value="topgp订阅快递",notes = "request body格式: {\"requestId\":\"1628584040740\",\"deliveryNum\":\"S301-2108020001\",\"expressNum\":\"300444235610\",\"company\":\"annengwuliu\",\"phone\":\"13800138000\"}") + @ApiImplicitParams({ + @ApiImplicitParam(name = "token", value = "token", required = true, paramType = "header", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "requestJson", value = "请求json",required = true, paramType = "body", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("api/express/topgpSubscribe") + public String topgpSubscribe(HttpServletRequest request, HttpServletResponse response) throws IOException { + return expSubsPushApiService.ExpressSubscribeFromTopgp(request); + } + + //接受topgp转签收单完成后的消息推送 + @Log(title = "TOPGP出货已转签收", businessType = BusinessType.OTHER) + @CrossOrigin + @ApiOperation(value="接受TOPGP已转签收消息推送",notes = "request body格式: {\"requestId\":\"topgpSign1628584040740\"," + + "\"signedInfoList\":[{\"deliveryNum\":\"S301-2108020001\",\"signNo\":\"S501-2108020001\"},{\"deliveryNum\":\"S301-2108020002\",\"signNo\":\"S501-2108020002\"}]," + + "\"expressNum\":\"300444235610\",\"company\":\"annengwuliu\",\"phone\":\"13800138000\",\"status\":\"0\"}" + ) + @ApiImplicitParams({ + @ApiImplicitParam(name = "token", value = "token", required = true, paramType = "header", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "requestJson", value = "请求json",required = true, paramType = "body", dataType = "String", dataTypeClass = String.class) + }) + @PostMapping("api/express/topgpSigned") + public String topgpSigned(HttpServletRequest request, HttpServletResponse response) throws IOException { + // return expSubsPushApiService.ExpressSubscribeFromTopgp(request); + return expSubsPushApiService.TopgpDeliverySigned(request); + } + + + +} + diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushRespController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushRespController.java new file mode 100644 index 000000000..561d0a174 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubsPushRespController.java @@ -0,0 +1,135 @@ +package com.ruoyi.bps.controller; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.service.IExpSubsPushRespService; +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 org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 快递订阅推送信息增删改查Controller + * + * @author box + * @date 2021-05-13 + */ +@Controller +@RequestMapping("/bps/expsubspushresp") +public class ExpSubsPushRespController extends BaseController +{ + private String prefix = "bps/expsubspushresp"; + + @Autowired + private IExpSubsPushRespService expSubsPushRespService; + + @RequiresPermissions("bps:expsubspushresp:view") + @GetMapping() + public String expsubspushresp() + { + return prefix + "/expsubspushresp"; + } + + /** + * 查询快递订阅推送信息列表 + */ + @RequiresPermissions("bps:expsubspushresp:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(ExpSubsPushResp expSubsPushResp) + { + startPage(); + List list = expSubsPushRespService.selectExpSubsPushRespList(expSubsPushResp); + return getDataTable(list); + } + + /** + * 导出快递订阅推送信息列表 + */ + @RequiresPermissions("bps:expsubspushresp:export") + @Log(title = "快递订阅推送信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(ExpSubsPushResp expSubsPushResp) + { + List list = expSubsPushRespService.selectExpSubsPushRespList(expSubsPushResp); + ExcelUtil util = new ExcelUtil(ExpSubsPushResp.class); + return util.exportExcel(list, "快递订阅推送信息数据"); + } + + /** + * 新增快递订阅推送信息 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存快递订阅推送信息 + */ + @RequiresPermissions("bps:expsubspushresp:add") + @Log(title = "快递订阅推送信息", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(ExpSubsPushResp expSubsPushResp) + { + return toAjax(expSubsPushRespService.insertExpSubsPushResp(expSubsPushResp)); + } + + /** + * 修改快递订阅推送信息 + */ + @GetMapping("/edit/{sid}") + public String edit(@PathVariable("sid") Long sid, ModelMap mmap) + { + ExpSubsPushResp expSubsPushResp = expSubsPushRespService.selectExpSubsPushRespById(sid); + mmap.put("expSubsPushResp", expSubsPushResp); + return prefix + "/edit"; + } + + /** + * 修改保存快递订阅推送信息 + */ + @RequiresPermissions("bps:expsubspushresp:edit") + @Log(title = "快递订阅推送信息", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(ExpSubsPushResp expSubsPushResp) + { + return toAjax(expSubsPushRespService.updateExpSubsPushResp(expSubsPushResp)); + } + + /** + * 删除快递订阅推送信息 + */ + @RequiresPermissions("bps:expsubspushresp:remove") + @Log(title = "快递订阅推送信息", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(expSubsPushRespService.deleteExpSubsPushRespByIds(ids)); + } + + /** + * 快递订阅推送详细信息 + */ + @RequiresPermissions("bps:expsubspushresp:detail") + @GetMapping("/detail/{sid}") + public String detail(@PathVariable("sid") Long sid, ModelMap mmap) + { + ExpSubsPushResp expSubsPushResp = expSubsPushRespService.selectExpSubsPushRespById(sid); + mmap.put("expSubsPushResp", expSubsPushResp); + return prefix + "/detail"; + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubscribeController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubscribeController.java new file mode 100644 index 000000000..ee8753a04 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpSubscribeController.java @@ -0,0 +1,148 @@ +package com.ruoyi.bps.controller; + +import com.ruoyi.bps.domain.ExpSubscribe; +import com.ruoyi.bps.service.IExpSubsPushApiService; +import com.ruoyi.bps.service.IExpSubscribeService; +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.kuaidi100.sdk.response.SubscribeResp; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 快递订阅Controller + * + * @author box + * @date 2021-05-20 + */ +@Controller +@RequestMapping("/bps/subscribe") +public class ExpSubscribeController extends BaseController +{ + private String prefix = "bps/subscribe"; + + @Autowired + private IExpSubscribeService expSubscribeService; + + @Autowired + private IExpSubsPushApiService iExpSubsPushApiService; + + @RequiresPermissions("bps:subscribe:view") + @GetMapping() + public String subscribe() + { + return prefix + "/subscribe"; + } + + /** + * 订阅快递 + */ + @CrossOrigin + @RequestMapping("/subscribe") + @ResponseBody + public SubscribeResp ExpressSubscribe(@RequestBody ExpSubscribe expSubscribe) { + return iExpSubsPushApiService.ExpressSubscribe(expSubscribe); + } + + + /** + * 查询快递订阅列表 + */ + @RequiresPermissions("bps:subscribe:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(ExpSubscribe expSubscribe) + { + startPage(); + List list; + if(expSubscribe.getNumber().contains(",")){ + List number= Arrays.asList(expSubscribe.getNumber().split(",")); + list=expSubscribeService.selectExpSubsPushRespByNumber(number); + } + else { + list = expSubscribeService.selectExpSubscribeList(expSubscribe); + } + return getDataTable(list); + } + + /** + * 导出快递订阅列表 + */ + @RequiresPermissions("bps:subscribe:export") + @Log(title = "快递订阅", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(ExpSubscribe expSubscribe) + { + List list = expSubscribeService.selectExpSubscribeList(expSubscribe); + ExcelUtil util = new ExcelUtil(ExpSubscribe.class); + return util.exportExcel(list, "快递订阅数据"); + } + + /** + * 新增快递订阅 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存快递订阅 + */ + @RequiresPermissions("bps:subscribe:add") + @Log(title = "快递订阅", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(ExpSubscribe expSubscribe) + { + return toAjax(expSubscribeService.insertExpSubscribe(expSubscribe)); + } + + /** + * 修改快递订阅 + */ + @GetMapping("/edit/{sid}") + public String edit(@PathVariable("sid") Long sid, ModelMap mmap) + { + ExpSubscribe expSubscribe = expSubscribeService.selectExpSubscribeById(sid); + mmap.put("expSubscribe", expSubscribe); + return prefix + "/edit"; + } + + /** + * 修改保存快递订阅 + */ + @RequiresPermissions("bps:subscribe:edit") + @Log(title = "快递订阅", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(ExpSubscribe expSubscribe) + { + return toAjax(expSubscribeService.updateExpSubscribe(expSubscribe)); + } + + /** + * 删除快递订阅 + */ + @RequiresPermissions("bps:subscribe:remove") + @Log(title = "快递订阅", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(expSubscribeService.deleteExpSubscribeByIds(ids)); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpTopgpLogController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpTopgpLogController.java new file mode 100644 index 000000000..6fd8090c4 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpTopgpLogController.java @@ -0,0 +1,126 @@ +package com.ruoyi.bps.controller; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.bps.domain.ExpTopgpLog; +import com.ruoyi.bps.service.IExpTopgpLogService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * ERP订阅推送日志Controller + * + * @author Bo + * @date 2021-08-11 + */ +@Controller +@RequestMapping("/bps/expTopgpLog") +public class ExpTopgpLogController extends BaseController +{ + private String prefix = "bps/expTopgpLog"; + + @Autowired + private IExpTopgpLogService expTopgpLogService; + + @RequiresPermissions("bps:expTopgpLog:view") + @GetMapping() + public String expTopgpLog() + { + return prefix + "/expTopgpLog"; + } + + /** + * 查询ERP订阅推送日志列表 + */ + @RequiresPermissions("bps:expTopgpLog:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(ExpTopgpLog expTopgpLog) + { + startPage(); + List list = expTopgpLogService.selectExpTopgpLogList(expTopgpLog); + return getDataTable(list); + } + + /** + * 导出ERP订阅推送日志列表 + */ + @RequiresPermissions("bps:expTopgpLog:export") + @Log(title = "ERP订阅推送日志", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(ExpTopgpLog expTopgpLog) + { + List list = expTopgpLogService.selectExpTopgpLogList(expTopgpLog); + ExcelUtil util = new ExcelUtil(ExpTopgpLog.class); + return util.exportExcel(list, "ERP订阅推送日志数据"); + } + + /** + * 新增ERP订阅推送日志 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存ERP订阅推送日志 + */ + @RequiresPermissions("bps:expTopgpLog:add") + @Log(title = "ERP订阅推送日志", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(ExpTopgpLog expTopgpLog) + { + return toAjax(expTopgpLogService.insertExpTopgpLog(expTopgpLog)); + } + + /** + * 修改ERP订阅推送日志 + */ + @GetMapping("/edit/{sid}") + public String edit(@PathVariable("sid") Long sid, ModelMap mmap) + { + ExpTopgpLog expTopgpLog = expTopgpLogService.selectExpTopgpLogBySid(sid); + mmap.put("expTopgpLog", expTopgpLog); + return prefix + "/edit"; + } + + /** + * 修改保存ERP订阅推送日志 + */ + @RequiresPermissions("bps:expTopgpLog:edit") + @Log(title = "ERP订阅推送日志", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(ExpTopgpLog expTopgpLog) + { + return toAjax(expTopgpLogService.updateExpTopgpLog(expTopgpLog)); + } + + /** + * 删除ERP订阅推送日志 + */ + @RequiresPermissions("bps:expTopgpLog:remove") + @Log(title = "ERP订阅推送日志", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(expTopgpLogService.deleteExpTopgpLogBySids(ids)); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressInfoController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressInfoController.java new file mode 100644 index 000000000..93f7e921f --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressInfoController.java @@ -0,0 +1,174 @@ +package com.ruoyi.bps.controller; + +import com.ruoyi.bps.domain.ExpressInfo; +import com.ruoyi.bps.service.IExpressInfoService; +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.ShiroUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.controller.BaseController; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +/** + * 快递信息Controller + * + * @author box + * @date 2021-05-06 + */ +@Controller +@RequestMapping("/bps/expressInfo") +public class ExpressInfoController extends BaseController +{ + private String prefix = "bps/expressInfo"; + + @Autowired + private IExpressInfoService expressInfoService; + + @RequiresPermissions("bps:expressInfo:view") + @GetMapping() + public String expressInfo() + { + return prefix + "/expressInfo"; + } + + /** + * 查询快递信息列表 + */ + @RequiresPermissions("bps:expressInfo:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(ExpressInfo expressInfo) + { + startPage(); + List list = expressInfoService.selectExpressInfoList(expressInfo); + return getDataTable(list); + } + + /** + * 查询快递信息列表 + */ + @RequiresPermissions("bps:expressInfo:list") + @PostMapping("/localList") + @ResponseBody + public TableDataInfo localList(ExpressInfo expressInfo) + { + startPage(); + List list = expressInfoService.selectLocalExpressInfoList(expressInfo); + + return getDataTable(list); + } + + /** + * 导出快递信息列表 + */ + @RequiresPermissions("bps:expressInfo:export") + @Log(title = "快递信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(ExpressInfo expressInfo) + { + List list = expressInfoService.selectExpressInfoList(expressInfo); + ExcelUtil util = new ExcelUtil(ExpressInfo.class); + return util.exportExcel(list, "快递信息数据"); + } + + /** + * 新增快递信息 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存快递信息 + */ + @RequiresPermissions("bps:expressInfo:add") + @Log(title = "快递信息", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(ExpressInfo expressInfo) + { + return toAjax(expressInfoService.insertExpressInfo(expressInfo)); + } + + /** + * 修改快递信息 + */ + @GetMapping("/edit/{message}") + public String edit(@PathVariable("message") String message, ModelMap mmap) + { + ExpressInfo expressInfo = expressInfoService.selectExpressInfoById(message); + mmap.put("expressInfo", expressInfo); + return prefix + "/edit"; + } + + /** + * 修改保存快递信息 + */ + @RequiresPermissions("bps:expressInfo:edit") + @Log(title = "快递信息", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(ExpressInfo expressInfo) + { + return toAjax(expressInfoService.updateExpressInfo(expressInfo)); + } + + /** + * 删除快递信息 + */ + @RequiresPermissions("bps:expressInfo:remove") + @Log(title = "快递信息", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(expressInfoService.deleteExpressInfoByIds(ids)); + } + + + + @GetMapping ( "/importTemplate" ) + @ResponseBody + public AjaxResult importTemplate ( ) { + ExcelUtil < ExpressInfo > util = new ExcelUtil<>(ExpressInfo.class); + return util.importTemplateExcel ( "快递查询导入模板" ); + } + + @PostMapping("/importData") + @ResponseBody + public TableDataInfo importData(MultipartFile file, boolean updateSupport) throws Exception + { + /*ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String operName = ShiroUtils.getSysUser().getLoginName(); + String message = userService.importUser(userList, updateSupport, operName); + return AjaxResult.success(message);*/ + + ExcelUtil util= new ExcelUtil(ExpressInfo.class); + List expressInfoList=util.importExcel(file.getInputStream()); + List list = new ArrayList<>(); + for( ExpressInfo expressInfo:expressInfoList) + { + list.add(expressInfo); + + } + + //String message = expressInfoList.importUser(userList, updateSupport, operName); + + return getDataTable(list); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressTestController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressTestController.java new file mode 100644 index 000000000..c248bc8e6 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/ExpressTestController.java @@ -0,0 +1,50 @@ +package com.ruoyi.bps.controller; + + +//import com.ruoyi.bps.express.contant.CompanyConstant; +//import com.ruoyi.bps.express.request.QueryTrackParam; + +import com.ruoyi.bps.service.IExpressService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.TableDataInfo; +import com.kuaidi100.sdk.request.QueryTrackParam; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ExpressTestController extends BaseController { + @Autowired + private IExpressService expressService; + + @RequestMapping("/anon/bps/express/test/queryTrackMultiList") + public TableDataInfo QueryTrackMultiList(){ + //return expressService.QueryTrackExpressMultiList(expressService.GetTestQueryTrackParam()).toString(); + return getDataTable(expressService.QueryTrackExpressMultiList(expressService.GetTestQueryTrackParam())); + } + + @RequestMapping("/anon/bps/express/test/queryTrackMulti") + public String QueryTrackMulti(){ + return expressService.QueryTrackExpressMulti(expressService.GetTestQueryTrackParam()); + } + + @RequestMapping("/anon/bps/express/test/queryTrack") + public String QueryTrack(){ + QueryTrackParam queryTrackParam = new QueryTrackParam(); + queryTrackParam.setCom("annengwuliu"); + queryTrackParam.setNum("3004459671351"); + queryTrackParam.setPhone("17725390266"); + + return expressService.QueryTrackExpress(queryTrackParam); + + + } + @RequestMapping("/anon/bps/express/test/subscribe") + public String Subscribe(){ + return expressService.SubscribeExpress(); + } + + + + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/FrForCrTopgpController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/FrForCrTopgpController.java new file mode 100644 index 000000000..ea61ca9ac --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/FrForCrTopgpController.java @@ -0,0 +1,107 @@ +package com.ruoyi.bps.controller; + +import com.ruoyi.bps.service.TopgpDdlService; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.common.utils.StringUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Api(tags = "TOPGP使用帆软报表接口") +@RestController +public class FrForCrTopgpController { + @Autowired + private TopgpDdlService topgpDdlService; + + //访问 ../anon/bps/frforcr/topprod时,使用topprod + @ApiOperation("TOPPROD正式区访问") + @ApiImplicitParam(name = "jsonString", value = "Json字符串", paramType = "body", dataType = "String", dataTypeClass = String.class) + @CrossOrigin + @Log(title = "CSFR412_CR报表_TOPPROD", businessType = BusinessType.DROP) + @PostMapping("/anon/bps/frforcr/topprod") + @DataSource(value = DataSourceType.TOPPRODDSREPORT) + public AjaxResult frforcrtopprod(@RequestBody Map map){ + return frforcrtoppgp(map); + } + + //访问../anon/bps/frforcr/topprod时,使用toptest实例 + @ApiOperation("TOPTEST正式区访问") + @ApiImplicitParam(name = "jsonString", value = "Json字符串", paramType = "body", dataType = "String", dataTypeClass = String.class) + @CrossOrigin + @Log(title = "CSFR412_CR报表_TOPTEST", businessType = BusinessType.DROP) + @PostMapping("/anon/bps/frforcr/toptest") + @DataSource(value = DataSourceType.TOPTESTDSREPORT) + public AjaxResult frforcrtoptest(@RequestBody Map map){ + return frforcrtoppgp(map); + } + + private AjaxResult frforcrtoppgp(Map map){ + /* 多此一举,在@RequestBody时取不到值是,已经会报500错误了,并不会执行到这一段。 + if(map.isEmpty()){ //如果传过来的值为空,返回未取到表名 + // System.out.println(LocalTime.now()+"未获取到表名!"); + return AjaxResult.error("未取到表名传参!"); + } + */ + StringBuilder droppedTable = new StringBuilder(); + StringBuilder errorDroppedTable = new StringBuilder(); + StringBuilder notExistsTable = new StringBuilder(); + + for (String tableName : getTableName(map)) { + if(topgpDdlService.isTableInDb("ds_report",tableName) <=0){ + //表名在数据库中不存在 + //System.out.println(LocalTime.now() + "【" + tableName + "】不存在!"); + notExistsTable.append(tableName+","); + continue; + } + + try { + topgpDdlService.dropTable("ds_report." + tableName); + //System.out.println(LocalTime.now() + "【" + tableName + "】已被删除!"); + droppedTable.append(tableName+","); + } catch (Exception e) { + //System.out.println(LocalTime.now() + "【" + tableName + "】删除异常!"); + errorDroppedTable.append(tableName+","); + } + } + + //删除异常写入日志 + if (StringUtils.isNotEmpty(errorDroppedTable)){ + return AjaxResult.error(errorDroppedTable+"删除异常!"); + } + //表不存在写入日志 + if(StringUtils.isNotEmpty(notExistsTable)){ + return AjaxResult.warn(notExistsTable+"表不存在!"); + } + //被删除的表写入日志 + if(StringUtils.isNotEmpty(droppedTable)){ + return AjaxResult.success(droppedTable + "删除成功!"); + } + //以上三种情况都不存在,说明发生了不明异常,写入日志 + return AjaxResult.warn("发生不明异常!"); + } + + //防止意手抖把SID也当成表传过来 + //排除sid的key值 + private List getTableName(Map map){ + List list=new ArrayList(); + for(String key:map.keySet()){ + if(key !="sid") + { + list.add(map.get(key).toString()); + } + } + return list; + } +} + + diff --git a/box-bps/src/main/java/com/ruoyi/bps/controller/QueryExpressController.java b/box-bps/src/main/java/com/ruoyi/bps/controller/QueryExpressController.java new file mode 100644 index 000000000..a0884a030 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/controller/QueryExpressController.java @@ -0,0 +1,103 @@ +package com.ruoyi.bps.controller; + + +//import com.ruoyi.bps.express.contant.CompanyConstant; +//import com.ruoyi.bps.express.request.QueryTrackParam; +//import com.ruoyi.bps.express.response.QueryTrackResp; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.bps.service.IExpressService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.page.TableDataInfo; +import com.kuaidi100.sdk.request.QueryTrackParam; +import com.kuaidi100.sdk.response.QueryTrackResp; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.HashMap; +import java.util.Map; + +@Controller +public class QueryExpressController extends BaseController { + @Autowired + private IExpressService expressService; + @RequiresPermissions("bps:express:view") + @RequestMapping("/bps/express/queryExpress") + public String queryExpress() + { + //return "express/queryExpress"; + //说明:此处不能用绝对路径,否则,当application.yml中,设定context-path: /it_war后,打Jar包时,会找不到thymeleaf对应的文件。 + return "express/queryExpress"; + } + + @CrossOrigin + @RequestMapping("/bps/express/queryExpress/list") + @ResponseBody + public Map queryExpressList(@RequestBody QueryTrackParam queryTrackParam){ + Map result = null; + if (queryTrackParam != null) { + System.out.println("运单号码:" + queryTrackParam.getNum()); + System.out.println("快递公司:" + queryTrackParam.getCom()); + System.out.println("电话:" + queryTrackParam.getPhone()); + result = new HashMap<>(); + result.put("code", "1"); + result.put("msg", "ok"); + result.put("info",expressService.QueryTrackExpress(queryTrackParam)); + } + return result; + } + + @CrossOrigin + @RequestMapping("/bps/express/queryExpress/list1") + @ResponseBody + public Map queryExpressList1(@RequestBody QueryTrackParam queryTrackParam){ + Map result = null; + if (queryTrackParam != null) { + System.out.println("运单号码:" + queryTrackParam.getNum()); + System.out.println("快递公司:" + queryTrackParam.getCom()); + System.out.println("电话:" + queryTrackParam.getPhone()); + String info=expressService.QueryTrackExpress(queryTrackParam); + QueryTrackResp queryTrackResp= JSONObject.parseObject(info,QueryTrackResp.class); + result = new HashMap<>(); + result.put("code", "1"); + result.put("msg", "ok"); + result.put("nu",queryTrackResp.getNu()); + result.put("com",queryTrackResp.getCom()); + result.put("state",queryTrackResp.getState()); + result.put("info",queryTrackResp.getData()); + } + return result; + } + + @RequestMapping("/anon/bps/express/queryTrackMultiList") + public TableDataInfo QueryTrackMultiList(@RequestBody QueryTrackParam queryTrackParam){ + + return getDataTable(expressService.QueryTrackExpressMultiList(expressService.GetTestQueryTrackParam())); + } + + @RequestMapping("/anon/bps/express/queryTrackMulti") + public String QueryTrackMulti(){ + return expressService.QueryTrackExpressMulti(expressService.GetTestQueryTrackParam()); + } + + @RequestMapping("/anon/bps/express/queryTrack") + public String QueryTrack(){ + QueryTrackParam queryTrackParam = new QueryTrackParam(); + queryTrackParam.setCom("annengwuliu"); + queryTrackParam.setNum("3004459671351"); + queryTrackParam.setPhone("17725390266"); + + return expressService.QueryTrackExpress(queryTrackParam); + + + } + @RequestMapping("/anon/bps/express/subscribe") + public String Subscribe(){ + return expressService.SubscribeExpress(); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/domain/ExpImportQuery.java b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpImportQuery.java new file mode 100644 index 000000000..b18dde143 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpImportQuery.java @@ -0,0 +1,143 @@ +package com.ruoyi.bps.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; + +/** + * Excel批量快递查询对象 exp_import_query + * + * @author Bo + * @date 2021-07-21 + */ +public class ExpImportQuery extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** sid */ + private Long sid; + + /** 查询ID */ + @Excel(name = "查询ID") + private String queryId; + + /** 查询时间 */ + @Excel(name = "查询时间") + private String queryTime; + + /** 用户ID */ + @Excel(name = "用户ID") + private String queryLoginName; + + /** 用户名 */ + @Excel(name = "用户名") + private String queryUserName; + + /** 查询IP */ + @Excel(name = "查询IP") + private String queryIp; + + /** 完成时间 */ + @Excel(name = "完成时间") + private String finishTime; + + /** 完成状态 */ + @Excel(name = "完成状态") + private String status; + + /** 运单总量 */ + @Excel(name = "运单总量") + private String queryQty; + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public Long getSid() { + return sid; + } + + public void setSid(Long sid) { + this.sid = sid; + } + + public String getQueryId() { + return queryId; + } + + public void setQueryId(String queryId) { + this.queryId = queryId; + } + + public String getQueryTime() { + return queryTime; + } + + public void setQueryTime(String queryTime) { + this.queryTime = queryTime; + } + + public String getQueryLoginName() { + return queryLoginName; + } + + public void setQueryLoginName(String queryLoginName) { + this.queryLoginName = queryLoginName; + } + + public String getQueryUserName() { + return queryUserName; + } + + public void setQueryUserName(String queryUserName) { + this.queryUserName = queryUserName; + } + + public String getQueryIp() { + return queryIp; + } + + public void setQueryIp(String queryIp) { + this.queryIp = queryIp; + } + + public String getFinishTime() { + return finishTime; + } + + public void setFinishTime(String finishTime) { + this.finishTime = finishTime; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getQueryQty() { + return queryQty; + } + + public void setQueryQty(String queryQty) { + this.queryQty = queryQty; + } + + @Override + public String toString() { + return "ExpImportQuery{" + + "sid=" + sid + + ", queryId='" + queryId + '\'' + + ", queryTime='" + queryTime + '\'' + + ", queryLoginName='" + queryLoginName + '\'' + + ", queryUserName='" + queryUserName + '\'' + + ", queryIp='" + queryIp + '\'' + + ", finishTime='" + finishTime + '\'' + + ", status='" + status + '\'' + + ", queryQty='" + queryQty + '\'' + + '}'; + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubsPushResp.java b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubsPushResp.java new file mode 100644 index 000000000..c462fd961 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubsPushResp.java @@ -0,0 +1,334 @@ +package com.ruoyi.bps.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 快递订阅推送信息对象 exp_subs_push_resp + * + * @author box + * @date 2021-05-13 + */ +public class ExpSubsPushResp extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** SID */ + private Long sid; + + /** 监控状态 */ + @Excel(name = "监控状态") + private String status; + + /** 状态 */ + @Excel(name = "状态") + private String billStatus; + + /** 监控状态消息 */ + @Excel(name = "监控状态消息") + private String message; + + /** 快递公司编码是否出错 */ + @Excel(name = "快递公司编码是否出错") + private String autoCheck; + + /** 原始快递公司编码 */ + @Excel(name = "原始快递公司编码") + private String comOld; + + /** 修正快递公司编码 */ + @Excel(name = "修正快递公司编码") + private String comNew; + + /** 当前快递消息 */ + @Excel(name = "当前快递消息") + private String lastResultMessage; + + /** 当前快递单状态 */ + @Excel(name = "当前快递单状态") + private String lastResultState; + + /** 通讯状态 */ + @Excel(name = "通讯状态") + private String lastResulStatus; + + /** 快递单明细状态 */ + @Excel(name = "快递单明细状态") + private String lastResultCondition; + + /** 是否签收 */ + @Excel(name = "是否签收") + private String lastResultIsCheck; + + /** 快递公司编码 */ + @Excel(name = "快递公司编码") + private String lastResultCom; + + /** 快递单号 */ + @Excel(name = "快递单号") + private String lastResultNu; + + /** 快递流转信息 */ + @Excel(name = "快递流转信息") + private String lastResultData; + + /** 目的国快递消息 */ + @Excel(name = "目的国快递消息") + private String destResultMessage; + + /** 目的国快递单状态 */ + @Excel(name = "目的国快递单状态") + private String destResultState; + + /** 目的国通讯状态 */ + @Excel(name = "目的国通讯状态") + private String destResultStatus; + + /** 目的国快递单明细状态 */ + @Excel(name = "目的国快递单明细状态") + private String destResultCondition; + + /** 目的国是否签收 */ + @Excel(name = "目的国是否签收") + private String destResultIsCheck; + + /** 目的国快递公司编码 */ + @Excel(name = "目的国快递公司编码") + private String destResultCom; + + /** 目的国快递单号 */ + @Excel(name = "目的国快递单号") + private String destResultNu; + + /** 目的国快递流转信息 */ + @Excel(name = "目的国快递流转信息") + private String destResultData; + + /** 最后更新时间 */ + @Excel(name = "最后更新时间") + private String lastResponseTime; + + public Long getSid() { + return sid; + } + + public void setSid(Long sid) { + this.sid = sid; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getBillStatus() { + return billStatus; + } + + public void setBillStatus(String billStatus) { + this.billStatus = billStatus; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getAutoCheck() { + return autoCheck; + } + + public void setAutoCheck(String autoCheck) { + this.autoCheck = autoCheck; + } + + public String getComOld() { + return comOld; + } + + public void setComOld(String comOld) { + this.comOld = comOld; + } + + public String getComNew() { + return comNew; + } + + public void setComNew(String comNew) { + this.comNew = comNew; + } + + public String getLastResultMessage() { + return lastResultMessage; + } + + public void setLastResultMessage(String lastResultMessage) { + this.lastResultMessage = lastResultMessage; + } + + public String getLastResultState() { + return lastResultState; + } + + public void setLastResultState(String lastResultState) { + this.lastResultState = lastResultState; + } + + public String getLastResulStatus() { + return lastResulStatus; + } + + public void setLastResulStatus(String lastResulStatus) { + this.lastResulStatus = lastResulStatus; + } + + public String getLastResultCondition() { + return lastResultCondition; + } + + public void setLastResultCondition(String lastResultCondition) { + this.lastResultCondition = lastResultCondition; + } + + public String getLastResultIsCheck() { + return lastResultIsCheck; + } + + public void setLastResultIsCheck(String lastResultIsCheck) { + this.lastResultIsCheck = lastResultIsCheck; + } + + public String getLastResultCom() { + return lastResultCom; + } + + public void setLastResultCom(String lastResultCom) { + this.lastResultCom = lastResultCom; + } + + public String getLastResultNu() { + return lastResultNu; + } + + public void setLastResultNu(String lastResultNu) { + this.lastResultNu = lastResultNu; + } + + public String getLastResultData() { + return lastResultData; + } + + public void setLastResultData(String lastResultData) { + this.lastResultData = lastResultData; + } + + public String getDestResultMessage() { + return destResultMessage; + } + + public void setDestResultMessage(String destResultMessage) { + this.destResultMessage = destResultMessage; + } + + public String getDestResultState() { + return destResultState; + } + + public void setDestResultState(String destResultState) { + this.destResultState = destResultState; + } + + public String getDestResultStatus() { + return destResultStatus; + } + + public void setDestResultStatus(String destResultStatus) { + this.destResultStatus = destResultStatus; + } + + public String getDestResultCondition() { + return destResultCondition; + } + + public void setDestResultCondition(String destResultCondition) { + this.destResultCondition = destResultCondition; + } + + public String getDestResultIsCheck() { + return destResultIsCheck; + } + + public void setDestResultIsCheck(String destResultIsCheck) { + this.destResultIsCheck = destResultIsCheck; + } + + public String getDestResultCom() { + return destResultCom; + } + + public void setDestResultCom(String destResultCom) { + this.destResultCom = destResultCom; + } + + public String getDestResultNu() { + return destResultNu; + } + + public void setDestResultNu(String destResultNu) { + this.destResultNu = destResultNu; + } + + public String getDestResultData() { + return destResultData; + } + + public void setDestResultData(String destResultData) { + this.destResultData = destResultData; + } + + public String getLastResponseTime() { + return lastResponseTime; + } + + public void setLastResponseTime(String lastResponseTime) { + this.lastResponseTime = lastResponseTime; + } + + @Override + public String toString() { + return "ExpSubsPushResp{" + + "sid=" + sid + + ", status='" + status + '\'' + + ", billStatus='" + billStatus + '\'' + + ", message='" + message + '\'' + + ", autoCheck='" + autoCheck + '\'' + + ", comOld='" + comOld + '\'' + + ", comNew='" + comNew + '\'' + + ", lastResultMessage='" + lastResultMessage + '\'' + + ", lastResultState='" + lastResultState + '\'' + + ", lastResulStatus='" + lastResulStatus + '\'' + + ", lastResultCondition='" + lastResultCondition + '\'' + + ", lastResultIsCheck='" + lastResultIsCheck + '\'' + + ", lastResultCom='" + lastResultCom + '\'' + + ", lastResultNu='" + lastResultNu + '\'' + + ", lastResultData='" + lastResultData + '\'' + + ", destResultMessage='" + destResultMessage + '\'' + + ", destResultState='" + destResultState + '\'' + + ", destResultStatus='" + destResultStatus + '\'' + + ", destResultCondition='" + destResultCondition + '\'' + + ", destResultIsCheck='" + destResultIsCheck + '\'' + + ", destResultCom='" + destResultCom + '\'' + + ", destResultNu='" + destResultNu + '\'' + + ", destResultData='" + destResultData + '\'' + + ", lastResponseTime='" + lastResponseTime + '\'' + + '}'; + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubscribe.java b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubscribe.java new file mode 100644 index 000000000..8a2ab7602 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpSubscribe.java @@ -0,0 +1,179 @@ +package com.ruoyi.bps.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 快递订阅对象 exp_subscribe + * + * @author box + * @date 2021-05-20 + */ +public class ExpSubscribe extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** SID */ + private Long sid; + + /** 快递公司编码 */ + @Excel(name = "快递公司编码") + private String company; + + /** 快递单号 */ + @Excel(name = "快递单号") + private String number; + + /** 收/寄件人电话 */ + @Excel(name = "收/寄件人电话") + private String phone; + + /** 盐 */ + @Excel(name = "盐") + private String salt; + + /** 订阅时间 */ + @Excel(name = "订阅时间") + private String subscribeTime; + + /** 订阅结果 */ + @Excel(name = "订阅结果") + private String result; + + /** 返回码 */ + @Excel(name = "返回码") + private String returnCode; + + /** 返回消息 */ + @Excel(name = "返回消息") + private String message; + + /** 返回消息 */ + @Excel(name = "请求方") + private String requestFrom; + + /** 返回消息 */ + @Excel(name = "请求ID") + private String requestId; + + public void setSid(Long sid) + { + this.sid = sid; + } + + public Long getSid() + { + return sid; + } + public void setCompany(String company) + { + this.company = company; + } + + public String getCompany() + { + return company; + } + public void setNumber(String number) + { + this.number = number; + } + + public String getNumber() + { + return number; + } + public void setPhone(String phone) + { + this.phone = phone; + } + + public String getPhone() + { + return phone; + } + public void setSalt(String salt) + { + this.salt = salt; + } + + public String getSalt() + { + return salt; + } + public void setSubscribeTime(String subscribeTime) + { + this.subscribeTime = subscribeTime; + } + + public String getSubscribeTime() + { + return subscribeTime; + } + public void setResult(String result) + { + this.result = result; + } + + public String getResult() + { + return result; + } + public void setReturnCode(String returnCode) + { + this.returnCode = returnCode; + } + + public String getReturnCode() + { + return returnCode; + } + public void setMessage(String message) + { + this.message = message; + } + + public String getMessage() + { + return message; + } + + public void setRequestFrom(String requestFrom) + { + this.requestFrom = requestFrom; + } + + public String getRequestFrom() + { + return requestFrom; + } + + public void setRequestId(String requestId) + { + this.requestId = requestId; + } + + public String getRequestId() + { + return requestId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("sid", getSid()) + .append("company", getCompany()) + .append("number", getNumber()) + .append("phone", getPhone()) + .append("salt", getSalt()) + .append("subscribeTime", getSubscribeTime()) + .append("result", getResult()) + .append("returnCode", getReturnCode()) + .append("message", getMessage()) + .append("message", getMessage()) + .append("message", getMessage()) + .toString(); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/domain/ExpTopgpLog.java b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpTopgpLog.java new file mode 100644 index 000000000..371ddd225 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpTopgpLog.java @@ -0,0 +1,149 @@ +package com.ruoyi.bps.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; + +/** + * ERP订阅推送日志对象 exp_topgp_log + * + * @author Bo + * @date 2021-08-11 + */ +public class ExpTopgpLog extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** SID */ + private Long sid; + + /** 请求ID */ + @Excel(name = "请求ID") + private String requestId; + + /** 请求类型(FromTopgp:ERP请求订阅、ToTopgp:Java推送签收指令) */ + @Excel(name = "请求类型", readConverterExp = "请求类型(FromTopgp:ERP请求订阅、ToTopgp:Java推送签收指令)") + private String requestType; + + /** 快递单 */ + @Excel(name = "快递单") + private String expressNum; + + /** 出货单号 */ + @Excel(name = "出货单号") + private String deliveryNum; + + /** 请求报文 */ + @Excel(name = "请求报文") + private String requestStr; + + /** 请求时间 */ + @Excel(name = "请求时间") + private String requestTime; + + /** 返回code */ + @Excel(name = "返回code") + private String responseCode; + + /** 返回报文 */ + @Excel(name = "返回报文") + private String responseStr; + + public void setSid(Long sid) + { + this.sid = sid; + } + + public Long getSid() + { + return sid; + } + public void setRequestId(String requestId) + { + this.requestId = requestId; + } + + public String getRequestId() + { + return requestId; + } + public void setRequestType(String requestType) + { + this.requestType = requestType; + } + + public String getRequestType() + { + return requestType; + } + public void setExpressNum(String expressNum) + { + this.expressNum = expressNum; + } + + public String getExpressNum() + { + return expressNum; + } + public void setDeliveryNum(String deliveryNum) + { + this.deliveryNum = deliveryNum; + } + + public String getDeliveryNum() + { + return deliveryNum; + } + public void setRequestStr(String requestStr) + { + this.requestStr = requestStr; + } + + public String getRequestStr() + { + return requestStr; + } + public void setRequestTime(String requestTime) + { + this.requestTime = requestTime; + } + + public String getRequestTime() + { + return requestTime; + } + public void setResponseCode(String responseCode) + { + this.responseCode = responseCode; + } + + public String getResponseCode() + { + return responseCode; + } + public void setResponseStr(String responseStr) + { + this.responseStr = responseStr; + } + + public String getResponseStr() + { + return responseStr; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("sid", getSid()) + .append("requestId", getRequestId()) + .append("requestType", getRequestType()) + .append("expressNum", getExpressNum()) + .append("deliveryNum", getDeliveryNum()) + .append("requestStr", getRequestStr()) + .append("requestTime", getRequestTime()) + .append("responseCode", getResponseCode()) + .append("responseStr", getResponseStr()) + .toString(); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/domain/ExpressInfo.java b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpressInfo.java new file mode 100644 index 000000000..bfaeb80a4 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/domain/ExpressInfo.java @@ -0,0 +1,324 @@ +package com.ruoyi.bps.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 快递信息对象 expressInfo + * + * @author box + * @date 2021-05-06 + */ +public class ExpressInfo extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 消息 */ + private String sid; + + /** 消息 */ + @Excel(name = "消息",type= Excel.Type.EXPORT) + private String message; + + /** 出货单号 */ + @Excel(name = "出货单号") + private String deliveryNum; + + /** 送货客户*/ + @Excel(name = "送货客户") + private String customer; + + /** 客服人员*/ + @Excel(name = "客服人员") + private String csName; + + /** 快递单号 */ + @Excel(name = "快递单号") + private String nu; + + /** 签收状态 */ + @Excel(name = "签收状态",type= Excel.Type.EXPORT,readConverterExp = "0=未签收,1=已签收") + private String ischeck; + + /** 快递公司 */ + @Excel(name = "快递公司",dictType= "express_company",dictTypeExceptImport = "true") + private String com; + + /** 通信状态 */ + //@Excel(name = "通信状态",type= Excel.Type.EXPORT) + private String status; + + /** 运单详情 */ + @Excel(name = "运单详情",type= Excel.Type.EXPORT,align = Excel.Align.LEFT) + private String data; + + /** 当前状态 */ + @Excel(name = "当前状态",type= Excel.Type.EXPORT,dictType = "express_stats") + private String state; + + /** 状态标志 */ + //@Excel(name = "状态标志",type= Excel.Type.EXPORT) + private String condition; + + /** 路由信息 */ + //@Excel(name = "路由信息",type= Excel.Type.EXPORT) + private String routeInfo; + + /** 返回码 */ + //@Excel(name = "返回码",type= Excel.Type.EXPORT) + private String returnCode; + + /** 返回结果 */ + //@Excel(name = "返回结果",type= Excel.Type.EXPORT) + private String result; + + /** 电话号码 */ + //@Excel(name = "电话号码",type= Excel.Type.EXPORT) + private String phone; + + /** 揽收时间*/ + @Excel(name = "揽收时间",type= Excel.Type.EXPORT) + private String collectTime; + + /** 签收时间*/ + @Excel(name = "签收时间",type= Excel.Type.EXPORT) + private String singedTime; + + /** 最后更新时间*/ + @Excel(name = "最后更新时间",type= Excel.Type.EXPORT) + private String lastUpdateTime; + + /** 查询时间*/ + @Excel(name = "查询时间",type= Excel.Type.EXPORT) + private String queryTime; + + /** 查询人*/ + @Excel(name = "查询人",type= Excel.Type.EXPORT) + private String queryUserName; + + /** 查询ID*/ + private String queryId; + + /** 查询类型*/ + private String queryType; + + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getSid() { + return sid; + } + + public void setSid(String sid) { + this.sid = sid; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getDeliveryNum() { + return deliveryNum; + } + + public void setDeliveryNum(String deliveryNum) { + this.deliveryNum = deliveryNum; + } + + public String getNu() { + return nu; + } + + public void setNu(String nu) { + this.nu = nu; + } + + public String getIscheck() { + return ischeck; + } + + public void setIscheck(String ischeck) { + this.ischeck = ischeck; + } + + public String getCom() { + return com; + } + + public void setCom(String com) { + this.com = com; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public String getRouteInfo() { + return routeInfo; + } + + public void setRouteInfo(String routeInfo) { + this.routeInfo = routeInfo; + } + + public String getReturnCode() { + return returnCode; + } + + public void setReturnCode(String returnCode) { + this.returnCode = returnCode; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getCollectTime() { + return collectTime; + } + + public void setCollectTime(String collectTime) { + this.collectTime = collectTime; + } + + public String getSingedTime() { + return singedTime; + } + + public void setSingedTime(String singedTime) { + this.singedTime = singedTime; + } + + public String getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(String lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public String getQueryTime() { + return queryTime; + } + + public void setQueryTime(String queryTime) { + this.queryTime = queryTime; + } + + public String getQueryUserName() { + return queryUserName; + } + + public void setQueryUserName(String queryUserName) { + this.queryUserName = queryUserName; + } + + public String getQueryId() { + return queryId; + } + + public void setQueryId(String queryId) { + this.queryId = queryId; + } + + public String getQueryType() { + return queryType; + } + + public void setQueryType(String queryType) { + this.queryType = queryType; + } + + public String getCsName() { + return csName; + } + + public void setCsName(String csName) { + this.csName = csName; + } + + public String getCustomer() { + return customer; + } + + public void setCustomer(String customer) { + this.customer = customer; + } + + @Override + public String toString() { + return "ExpressInfo{" + + "sid='" + sid + '\'' + + ", message='" + message + '\'' + + ", deliveryNum='" + deliveryNum + '\'' + + ", nu='" + nu + '\'' + + ", ischeck='" + ischeck + '\'' + + ", com='" + com + '\'' + + ", status='" + status + '\'' + + ", data='" + data + '\'' + + ", state='" + state + '\'' + + ", condition='" + condition + '\'' + + ", routeInfo='" + routeInfo + '\'' + + ", returnCode='" + returnCode + '\'' + + ", result='" + result + '\'' + + ", phone='" + phone + '\'' + + ", collectTime='" + collectTime + '\'' + + ", singedTime='" + singedTime + '\'' + + ", lastUpdateTime='" + lastUpdateTime + '\'' + + ", queryTime='" + queryTime + '\'' + + ", queryUserName='" + queryUserName + '\'' + + ", queryId='" + queryId + '\'' + + ", queryType='" + queryType + '\'' + + ", csName='" + csName + '\'' + + ", customer='" + customer + '\'' + + '}'; + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpImportQueryMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpImportQueryMapper.java new file mode 100644 index 000000000..a5f968462 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpImportQueryMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.bps.mapper; + +import java.util.List; +import com.ruoyi.bps.domain.ExpImportQuery; + +/** + * Excel批量快递查询Mapper接口 + * + * @author Bo + * @date 2021-07-21 + */ +public interface ExpImportQueryMapper +{ + /** + * 查询Excel批量快递查询 + * + * @param sid Excel批量快递查询ID + * @return Excel批量快递查询 + */ + public ExpImportQuery selectExpImportQueryById(Long sid); + + /** + * 查询Excel批量快递查询列表 + * + * @param expImportQuery Excel批量快递查询 + * @return Excel批量快递查询集合 + */ + public List selectExpImportQueryList(ExpImportQuery expImportQuery); + + /** + * 新增Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + public int insertExpImportQuery(ExpImportQuery expImportQuery); + + /** + * 修改Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + public int updateExpImportQuery(ExpImportQuery expImportQuery); + + /** + * 删除Excel批量快递查询 + * + * @param sid Excel批量快递查询ID + * @return 结果 + */ + public int deleteExpImportQueryById(Long sid); + + /** + * 批量删除Excel批量快递查询 + * + * @param sids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpImportQueryByIds(String[] sids); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubsPushRespMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubsPushRespMapper.java new file mode 100644 index 000000000..f1a0bac8b --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubsPushRespMapper.java @@ -0,0 +1,64 @@ +package com.ruoyi.bps.mapper; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +/** + * 快递订阅推送信息Mapper接口 + * + * @author box + * @date 2021-05-13 + */ +public interface ExpSubsPushRespMapper +{ + /** + * 查询快递订阅推送信息 + * + * @param sid 快递订阅推送信息ID + * @return 快递订阅推送信息 + */ + public ExpSubsPushResp selectExpSubsPushRespById(Long sid); + + /** + * 查询快递订阅推送信息列表 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 快递订阅推送信息集合 + */ + public List selectExpSubsPushRespList(ExpSubsPushResp expSubsPushResp); + + /** + * 新增快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + public int insertExpSubsPushResp(ExpSubsPushResp expSubsPushResp); + + /** + * 修改快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + public int updateExpSubsPushResp(ExpSubsPushResp expSubsPushResp); + + /** + * 删除快递订阅推送信息 + * + * @param sid 快递订阅推送信息ID + * @return 结果 + */ + public int deleteExpSubsPushRespById(Long sid); + + /** + * 批量删除快递订阅推送信息 + * + * @param sids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpSubsPushRespByIds(String[] sids); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubscribeMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubscribeMapper.java new file mode 100644 index 000000000..e13b0bbb0 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpSubscribeMapper.java @@ -0,0 +1,73 @@ +package com.ruoyi.bps.mapper; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.domain.ExpSubscribe; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +/** + * 快递订阅Mapper接口 + * + * @author box + * @date 2021-05-20 + */ +public interface ExpSubscribeMapper +{ + /** + * 查询快递订阅 + * + * @param sid 快递订阅ID + * @return 快递订阅 + */ + public ExpSubscribe selectExpSubscribeById(Long sid); + + /** + * 查询快递订阅列表 + * + * @param expSubscribe 快递订阅 + * @return 快递订阅集合 + */ + public List selectExpSubscribeList(ExpSubscribe expSubscribe); + + /** + * 新增快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + public int insertExpSubscribe(ExpSubscribe expSubscribe); + + /** + * 修改快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + public int updateExpSubscribe(ExpSubscribe expSubscribe); + + /** + * 删除快递订阅 + * + * @param sid 快递订阅ID + * @return 结果 + */ + public int deleteExpSubscribeById(Long sid); + + /** + * 批量删除快递订阅 + * + * @param sids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpSubscribeByIds(String[] sids); + + /** + * 根据快递单号查询快递订阅推送信息 + * + * @param number 快递单号List + * @return 快递订阅推送信息 + */ + public List selectExpSubscribeByNumber(List number); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpTopgpLogMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpTopgpLogMapper.java new file mode 100644 index 000000000..3d36ae480 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpTopgpLogMapper.java @@ -0,0 +1,61 @@ +package com.ruoyi.bps.mapper; + +import java.util.List; +import com.ruoyi.bps.domain.ExpTopgpLog; + +/** + * ERP订阅推送日志Mapper接口 + * + * @author Bo + * @date 2021-08-11 + */ +public interface ExpTopgpLogMapper +{ + /** + * 查询ERP订阅推送日志 + * + * @param sid ERP订阅推送日志主键 + * @return ERP订阅推送日志 + */ + public ExpTopgpLog selectExpTopgpLogBySid(Long sid); + + /** + * 查询ERP订阅推送日志列表 + * + * @param expTopgpLog ERP订阅推送日志 + * @return ERP订阅推送日志集合 + */ + public List selectExpTopgpLogList(ExpTopgpLog expTopgpLog); + + /** + * 新增ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + public int insertExpTopgpLog(ExpTopgpLog expTopgpLog); + + /** + * 修改ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + public int updateExpTopgpLog(ExpTopgpLog expTopgpLog); + + /** + * 删除ERP订阅推送日志 + * + * @param sid ERP订阅推送日志主键 + * @return 结果 + */ + public int deleteExpTopgpLogBySid(Long sid); + + /** + * 批量删除ERP订阅推送日志 + * + * @param sids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteExpTopgpLogBySids(String[] sids); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpressInfoMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpressInfoMapper.java new file mode 100644 index 000000000..0d8fcd6a5 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/ExpressInfoMapper.java @@ -0,0 +1,82 @@ +package com.ruoyi.bps.mapper; + +import com.ruoyi.bps.domain.ExpressInfo; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +/** + * 快递信息Mapper接口 + * + * @author box + * @date 2021-05-06 + */ +public interface ExpressInfoMapper +{ + /** + * 查询快递信息 + * + * @param message 快递信息ID + * @return 快递信息 + */ + public ExpressInfo selectExpressInfoById(String message); + + /** + * 查询快递信息列表 + * + * @param expressInfo 快递信息 + * @return 快递信息集合 + */ + public List selectExpressInfoList(ExpressInfo expressInfo); + + /** + * 新增快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + public int insertExpressInfo(ExpressInfo expressInfo); + + /** + * 修改快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + public int updateExpressInfo(ExpressInfo expressInfo); + + /** + * 删除快递信息 + * + * @param message 快递信息ID + * @return 结果 + */ + public int deleteExpressInfoById(String message); + + /** + * 批量删除快递信息 + * + * @param messages 需要删除的数据ID + * @return 结果 + */ + public int deleteExpressInfoByIds(String[] messages); + + /** + * 批量新增快递信息 + * + * @param expressInfoList 角色菜单列表 + * @return 结果 + */ + public int batchInsertExpressInfo(List expressInfoList); + + + /** + * 删除快递信息 + * + * @param queryId 快递信息queryId + * @return 结果 + */ + public int deleteExpressInfoByQueryId(String queryId); + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/mapper/TopgpDdlMapper.java b/box-bps/src/main/java/com/ruoyi/bps/mapper/TopgpDdlMapper.java new file mode 100644 index 000000000..ad06f7d89 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/mapper/TopgpDdlMapper.java @@ -0,0 +1,32 @@ +package com.ruoyi.bps.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Component; + +@Mapper +@Component("TopgpDdlMapper") +public interface TopgpDdlMapper { + // alter table + int alterTableName(@Param("originalTableName") String originalTableName, + @Param("newTableName") String newTableName); + + //truncate table + int truncateTable(@Param("tableName") String tableName); + + //drop table + int dropTable(@Param("tableName") String tableName); + + //copy table + void copyTable(@Param("newTableName") String newTableName, + @Param("originalTableName") String originalTableName); + + //获取表记录数 + int getRecordCount(@Param("tableName") String tableName); + + + + //查询数据库中表是否存在 + int isTableInDb(@Param("dataBaseName") String dataBaseName, + @Param("tableName") String tableName); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpImportQueryService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpImportQueryService.java new file mode 100644 index 000000000..0a08b6692 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpImportQueryService.java @@ -0,0 +1,72 @@ +package com.ruoyi.bps.service; + +import java.util.List; +import com.ruoyi.bps.domain.ExpImportQuery; +import com.ruoyi.bps.domain.ExpressInfo; +import com.ruoyi.common.core.domain.AjaxResult; + +/** + * Excel批量快递查询Service接口 + * + * @author Bo + * @date 2021-07-21 + */ +public interface IExpImportQueryService +{ + /** + * 查询Excel批量快递查询 + * + * @param sid Excel批量快递查询ID + * @return Excel批量快递查询 + */ + public ExpImportQuery selectExpImportQueryById(Long sid); + + /** + * 查询Excel批量快递查询列表 + * + * @param expImportQuery Excel批量快递查询 + * @return Excel批量快递查询集合 + */ + public List selectExpImportQueryList(ExpImportQuery expImportQuery); + + /** + * 新增Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + public int insertExpImportQuery(ExpImportQuery expImportQuery); + + /** + * 修改Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + public int updateExpImportQuery(ExpImportQuery expImportQuery); + + /** + * 批量删除Excel批量快递查询 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpImportQueryByIds(String ids); + + /** + * 删除Excel批量快递查询信息 + * + * @param sid Excel批量快递查询ID + * @return 结果 + */ + public int deleteExpImportQueryById(Long sid); + + /** + * 删除Excel批量快递查询信息 + * + * @param expressInfoList Excel导入的快递列表 + * @return 结果 + */ + public AjaxResult importData(List expressInfoList) throws Exception; + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushApiService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushApiService.java new file mode 100644 index 000000000..6ee2dc1db --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushApiService.java @@ -0,0 +1,43 @@ +package com.ruoyi.bps.service; + +import com.ruoyi.bps.domain.ExpSubscribe; +import com.kuaidi100.sdk.response.SubscribeResp; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +public interface IExpSubsPushApiService { + + /** + * 向快递100推送订阅请求 + * @param expSubscribe + * @return + */ + public SubscribeResp ExpressSubscribe(ExpSubscribe expSubscribe); + + /** + * 快递100订阅推送处理 + * + * 回调接口支持自定义参数,比如订阅时回调地址填写的是 http://www.xxx.com?orderId=1233333 + * 可以通过下面这种方式获取到orderId: String orderId = request.getParameter("orderId"); + * + * 返回值必须是下面这样的格式,否则快递100将认为该推送失败,快递100将会重试3次该推送,时间间隔35分钟; + * 成功结果返回例子: {"result":true,"returnCode":"200","message":"提交成功"} + * + */ + public SubscribeResp ExpressSubscribeCallBackUrl(HttpServletRequest request,String salt); + + /** + * 获取Topgp推送的快递信息,向快递100推送订阅请求 + * @param request + * @return + */ + public String ExpressSubscribeFromTopgp(HttpServletRequest request) throws IOException; + + /** + * Topgp将出货单转为签收单后的信息推送处理 + * @param request + * @return + */ + public String TopgpDeliverySigned(HttpServletRequest request) throws IOException; +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushRespService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushRespService.java new file mode 100644 index 000000000..61a6f4aba --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubsPushRespService.java @@ -0,0 +1,63 @@ +package com.ruoyi.bps.service; + +import com.ruoyi.bps.domain.ExpSubsPushResp; + +import java.util.List; + +/** + * 快递订阅推送信息Service接口 + * + * @author box + * @date 2021-05-13 + */ +public interface IExpSubsPushRespService +{ + /** + * 查询快递订阅推送信息 + * + * @param sid 快递订阅推送信息ID + * @return 快递订阅推送信息 + */ + public ExpSubsPushResp selectExpSubsPushRespById(Long sid); + + /** + * 查询快递订阅推送信息列表 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 快递订阅推送信息集合 + */ + public List selectExpSubsPushRespList(ExpSubsPushResp expSubsPushResp); + + /** + * 新增快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + public int insertExpSubsPushResp(ExpSubsPushResp expSubsPushResp); + + /** + * 修改快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + public int updateExpSubsPushResp(ExpSubsPushResp expSubsPushResp); + + /** + * 批量删除快递订阅推送信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpSubsPushRespByIds(String ids); + + /** + * 删除快递订阅推送信息信息 + * + * @param sid 快递订阅推送信息ID + * @return 结果 + */ + public int deleteExpSubsPushRespById(Long sid); + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubscribeService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubscribeService.java new file mode 100644 index 000000000..58d46a868 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpSubscribeService.java @@ -0,0 +1,71 @@ +package com.ruoyi.bps.service; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.domain.ExpSubscribe; + +import java.util.List; + +/** + * 快递订阅Service接口 + * + * @author box + * @date 2021-05-20 + */ +public interface IExpSubscribeService +{ + /** + * 查询快递订阅 + * + * @param sid 快递订阅ID + * @return 快递订阅 + */ + public ExpSubscribe selectExpSubscribeById(Long sid); + + /** + * 查询快递订阅列表 + * + * @param expSubscribe 快递订阅 + * @return 快递订阅集合 + */ + public List selectExpSubscribeList(ExpSubscribe expSubscribe); + + /** + * 新增快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + public int insertExpSubscribe(ExpSubscribe expSubscribe); + + /** + * 修改快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + public int updateExpSubscribe(ExpSubscribe expSubscribe); + + /** + * 批量删除快递订阅 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpSubscribeByIds(String ids); + + /** + * 删除快递订阅信息 + * + * @param sid 快递订阅ID + * @return 结果 + */ + public int deleteExpSubscribeById(Long sid); + + /** + * 根据快递单号查询快递订阅推送信息 + * + * @param number 快递单号List + * @return 快递订阅推送信息 + */ + public List selectExpSubsPushRespByNumber(List number); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpTopgpLogService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpTopgpLogService.java new file mode 100644 index 000000000..6f205cd66 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpTopgpLogService.java @@ -0,0 +1,61 @@ +package com.ruoyi.bps.service; + +import java.util.List; +import com.ruoyi.bps.domain.ExpTopgpLog; + +/** + * ERP订阅推送日志Service接口 + * + * @author Bo + * @date 2021-08-11 + */ +public interface IExpTopgpLogService +{ + /** + * 查询ERP订阅推送日志 + * + * @param sid ERP订阅推送日志主键 + * @return ERP订阅推送日志 + */ + public ExpTopgpLog selectExpTopgpLogBySid(Long sid); + + /** + * 查询ERP订阅推送日志列表 + * + * @param expTopgpLog ERP订阅推送日志 + * @return ERP订阅推送日志集合 + */ + public List selectExpTopgpLogList(ExpTopgpLog expTopgpLog); + + /** + * 新增ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + public int insertExpTopgpLog(ExpTopgpLog expTopgpLog); + + /** + * 修改ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + public int updateExpTopgpLog(ExpTopgpLog expTopgpLog); + + /** + * 批量删除ERP订阅推送日志 + * + * @param sids 需要删除的ERP订阅推送日志主键集合 + * @return 结果 + */ + public int deleteExpTopgpLogBySids(String sids); + + /** + * 删除ERP订阅推送日志信息 + * + * @param sid ERP订阅推送日志主键 + * @return 结果 + */ + public int deleteExpTopgpLogBySid(Long sid); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpressInfoService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpressInfoService.java new file mode 100644 index 000000000..48c284c68 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpressInfoService.java @@ -0,0 +1,78 @@ +package com.ruoyi.bps.service; + +import com.ruoyi.bps.domain.ExpressInfo; + +import java.util.List; + +/** + * 快递信息Service接口 + * + * @author box + * @date 2021-05-06 + */ +public interface IExpressInfoService +{ + /** + * 查询快递信息 + * + * @param message 快递信息ID + * @return 快递信息 + */ + public ExpressInfo selectExpressInfoById(String message); + + /** + * 查询本地快递信息列表 + * + * @param expressInfo 快递信息 + * @return 快递信息集合 + */ + public List selectLocalExpressInfoList(ExpressInfo expressInfo); + + /** + * 查询本地快递信息列表 + * + * @param expressInfo 快递信息 + * @return 快递信息集合 + */ + public List selectExpressInfoList(ExpressInfo expressInfo); + + /** + * 新增快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + public int insertExpressInfo(ExpressInfo expressInfo); + + /** + * 修改快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + public int updateExpressInfo(ExpressInfo expressInfo); + + /** + * 批量删除快递信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteExpressInfoByIds(String ids); + + /** + * 删除快递信息信息 + * + * @param message 快递信息ID + * @return 结果 + */ + public int deleteExpressInfoById(String message); + + /** + * 查询快递信息 + * + * @param expressInfo 快递信息 + * @return 快递信息 + */ + public ExpressInfo SelectExpressInfo(ExpressInfo expressInfo); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/IExpressService.java b/box-bps/src/main/java/com/ruoyi/bps/service/IExpressService.java new file mode 100644 index 000000000..b3adfa8da --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/IExpressService.java @@ -0,0 +1,33 @@ +package com.ruoyi.bps.service; + +//import com.ruoyi.bps.express.request.QueryTrackParam; +//import com.ruoyi.bps.express.response.QueryTrackResp; + +import com.kuaidi100.sdk.request.QueryTrackParam; +import com.kuaidi100.sdk.response.QueryTrackResp; + +import java.util.List; + +public interface IExpressService { + + public List QueryTrackExpressMultiList(List list); + /** + * 查询多条物流轨迹 + */ + public String QueryTrackExpressMulti(List list); + + /** + * 查询物流轨迹 + */ + public String QueryTrackExpress(QueryTrackParam qt); + + /** + * 订阅 + */ + public String SubscribeExpress(); + + /** + * 测试快递单号合集 + */ + public List GetTestQueryTrackParam(); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/TopgpDdlService.java b/box-bps/src/main/java/com/ruoyi/bps/service/TopgpDdlService.java new file mode 100644 index 000000000..e2d2b4e6a --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/TopgpDdlService.java @@ -0,0 +1,22 @@ +package com.ruoyi.bps.service; + +public interface TopgpDdlService { + //修改表名 + int alterTableName(String originalTableName, String newTableName); + + // truncate指定数据库表的数据 + int truncateTable(String tableName); + + //drop 指定定数据库表 + int dropTable(String tableName); + + + //根据传入的表明,创建新的表并且将原表的数据插入到新的Occur表中 + void copyTable(String newTableName,String originalTableName); + + //统计某张表中的总数据条数 + int getRecordCount(String tableName); + + //从指定数据库中,查询是否存在某张表 + int isTableInDb(String dataBaseName, String tableName); +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpImportQueryServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpImportQueryServiceImpl.java new file mode 100644 index 000000000..1db0f00aa --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpImportQueryServiceImpl.java @@ -0,0 +1,173 @@ +package com.ruoyi.bps.service.impl; + +import com.ruoyi.bps.domain.ExpImportQuery; +import com.ruoyi.bps.domain.ExpressInfo; +import com.ruoyi.bps.mapper.ExpImportQueryMapper; +import com.ruoyi.bps.mapper.ExpressInfoMapper; +import com.ruoyi.bps.service.IExpImportQueryService; +import com.ruoyi.bps.service.IExpressInfoService; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.common.utils.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Excel批量快递查询Service业务层处理 + * + * @author Bo + * @date 2021-07-21 + */ +@Service +public class ExpImportQueryServiceImpl implements IExpImportQueryService +{ + @Autowired + private ExpImportQueryMapper expImportQueryMapper; + + @Autowired + private IExpressInfoService expressInfoService; + + @Autowired + private ExpressInfoMapper expressInfoMapper; + + /** + * 查询Excel批量快递查询 + * + * @param sid Excel批量快递查询ID + * @return Excel批量快递查询 + */ + @Override + public ExpImportQuery selectExpImportQueryById(Long sid) + { + return expImportQueryMapper.selectExpImportQueryById(sid); + } + + /** + * 查询Excel批量快递查询列表 + * + * @param expImportQuery Excel批量快递查询 + * @return Excel批量快递查询 + */ + @Override + public List selectExpImportQueryList(ExpImportQuery expImportQuery) + { + return expImportQueryMapper.selectExpImportQueryList(expImportQuery); + } + + /** + * 新增Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + @Override + public int insertExpImportQuery(ExpImportQuery expImportQuery) + { + return expImportQueryMapper.insertExpImportQuery(expImportQuery); + } + + /** + * 修改Excel批量快递查询 + * + * @param expImportQuery Excel批量快递查询 + * @return 结果 + */ + @Override + public int updateExpImportQuery(ExpImportQuery expImportQuery) + { + return expImportQueryMapper.updateExpImportQuery(expImportQuery); + } + + /** + * 删除Excel批量快递查询对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + @Transactional + public int deleteExpImportQueryByIds(String ids) + { + for(String str:Arrays.asList(ids.split(","))) + { + expressInfoMapper.deleteExpressInfoByQueryId(str); + } + int message= expImportQueryMapper.deleteExpImportQueryByIds(Convert.toStrArray(ids)); + return message; + } + + /** + * 删除Excel批量快递查询信息 + * + * @param sid Excel批量快递查询ID + * @return 结果 + */ + @Override + public int deleteExpImportQueryById(Long sid) + { + return expImportQueryMapper.deleteExpImportQueryById(sid); + } + + + /** + * Excel批量快递查询信息 + * + * @param expressInfoList Excel导入的快递列表 + * @return 结果 + */ + @Override + @Transactional + public AjaxResult importData(List expressInfoList) throws Exception { + String queryTime= DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss"); + String queryId= LocalDateTime.now().toString(); + ExpImportQuery expImportQuery=new ExpImportQuery(); + List expressInfoListForInsert=new ArrayList<>(); + /* try{*/ + //将查询到的快递结果放到expressInfoListForInsert,并插入到数据库表expressInfo + for( ExpressInfo expressInfo:expressInfoList){ + //去除快递单号中的头尾空白符 + expressInfo.setNu(expressInfo.getNu().trim()); + ExpressInfo ei= expressInfoService.SelectExpressInfo(expressInfo); + ei.setQueryId(queryId); + ei.setQueryUserName(ShiroUtils.getSysUser().getUserName()); + ei.setQueryType("excel"); + ei.setQueryTime(queryTime); + ei.setCustomer(expressInfo.getCustomer()); //送货客户 + ei.setCsName(expressInfo.getCsName()); //客服人员 + //expressInfoService.insertExpressInfo(ei); + expressInfoListForInsert.add(ei); + /* for(int i=1;i<1001;i++){ //测试批量插入效率用时打开Mark,产生5万条数据。 + expressInfoListForInsert.add(ei); + }*/ + } + int size= expressInfoListForInsert.size(); + List expressInfos= new ArrayList<>(); + for(int i=1;i<=size;i++){ + expressInfos.add(expressInfoListForInsert.get(i-1)); + if( (i%400==0 ) ||i== size) { + expressInfoMapper.batchInsertExpressInfo(expressInfos); + expressInfos.clear(); + } + } + //将本次excel导入查询记录到数据表exp_import_query + expImportQuery.setQueryTime(queryTime); + expImportQuery.setQueryLoginName(ShiroUtils.getLoginName()); + expImportQuery.setQueryUserName(ShiroUtils.getSysUser().getUserName()); + expImportQuery.setFinishTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss")); + expImportQuery.setQueryIp(ShiroUtils.getIp()); + expImportQuery.setStatus("success"); + expImportQuery.setQueryQty(String.valueOf(expressInfoList.size())); + expImportQuery.setQueryId(queryId); + int message=expImportQueryMapper.insertExpImportQuery(expImportQuery); + return AjaxResult.success(message); + + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushApiServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushApiServiceImpl.java new file mode 100644 index 000000000..4483d28a2 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushApiServiceImpl.java @@ -0,0 +1,442 @@ +package com.ruoyi.bps.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.kuaidi100.sdk.api.Subscribe; +import com.kuaidi100.sdk.contant.ApiInfoConstant; +import com.kuaidi100.sdk.core.IBaseClient; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.SubscribeParam; +import com.kuaidi100.sdk.request.SubscribeParameters; +import com.kuaidi100.sdk.request.SubscribeReq; +import com.kuaidi100.sdk.response.SubscribePushData; +import com.kuaidi100.sdk.response.SubscribePushParamResp; +import com.kuaidi100.sdk.response.SubscribePushResult; +import com.kuaidi100.sdk.response.SubscribeResp; +import com.kuaidi100.sdk.utils.SignUtils; +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.domain.ExpSubscribe; +import com.ruoyi.bps.domain.ExpTopgpLog; +import com.ruoyi.bps.service.IExpSubsPushApiService; +import com.ruoyi.bps.service.IExpSubsPushRespService; +import com.ruoyi.bps.service.IExpSubscribeService; +import com.ruoyi.bps.service.IExpTopgpLogService; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.TopgpXmlUtils; +import com.ruoyi.common.utils.http.HttpUtils; +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.Service; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class ExpSubsPushApiServiceImpl implements IExpSubsPushApiService { + /*String key = PropertiesReader.get("key");*/ + private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + @Value("${express.key}") + private String key; + + @Value("${topgp.webservice.toptest}") + private String webserviceUrl; + + @Autowired + private IExpSubsPushRespService expSubsPushRespService; + + @Autowired + IExpSubscribeService expSubscribeService; + + @Autowired + IExpTopgpLogService expTopgpLogService; + + /** + * 订阅快递 + * @throws Exception + */ + @Override + public SubscribeResp ExpressSubscribe(ExpSubscribe expSubscribe) { + + //如果订阅来源是topgp,则来源为topgp,否则为local + /*String loginFrom= expSubscribe.getSalt(); + String subscribeFrom= StringUtils.isNotEmpty(loginFrom)?loginFrom.equals("topgp")?"topgp":"local":"local";*/ + if(StringUtils.isEmpty(expSubscribe.getRequestFrom())){ + expSubscribe.setRequestFrom("local"); + } + //如果订阅来源是topgp,则取TOPGP的时间戳,否则自己生成时间戳 + if( StringUtils.isEmpty(expSubscribe.getRequestId())) { + //expSubscribe.setRequestId("local"+System.currentTimeMillis()); //获取时间戳,生成本地请求的requestId + expSubscribe.setRequestId("local"+ LocalDateTime.now()); + } + //如果订阅来源是topgp,则取TOPGP的订阅时间,否则自己生成订阅时间 + if(StringUtils.isEmpty(expSubscribe.getSubscribeTime())){ + expSubscribe.setSubscribeTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss")); + } + //如果订阅来源是topgp,则取TOPGP传来的salt值topgp,否则使用bpsemi + if(StringUtils.isEmpty(expSubscribe.getSalt())) { + expSubscribe.setSalt("bpsemi");//定义salt字符串 + } + + //组合订阅参数 + SubscribeParameters subscribeParameters = new SubscribeParameters(); + SubscribeResp subscribeResp = new SubscribeResp(); + subscribeParameters.setCallbackurl("http://report.bpsemi.cn:8081/it_war/anon/subscribeCallBackUrl/"+expSubscribe.getSalt().trim()); + subscribeParameters.setPhone(expSubscribe.getPhone()); + subscribeParameters.setSalt(expSubscribe.getSalt()); + SubscribeParam subscribeParam = new SubscribeParam(); + subscribeParam.setParameters(subscribeParameters); + subscribeParam.setCompany(expSubscribe.getCompany()); + subscribeParam.setNumber(expSubscribe.getNumber()); + subscribeParam.setKey(key); + + SubscribeReq subscribeReq = new SubscribeReq(); + subscribeReq.setSchema(ApiInfoConstant.SUBSCRIBE_SCHEMA); + subscribeReq.setParam(JSONObject.toJSONString(subscribeParam)); + + IBaseClient subscribe = new Subscribe(); + try{ + //推送订阅,并获得快递100响应结果 + HttpResult httpResult= subscribe.execute(subscribeReq); + subscribeResp = JSONObject.parseObject(httpResult.getBody(),SubscribeResp.class); + }catch (Exception e) + { + return subscribeResp; + } + //如果快递公司或快递单号为空,则直接返回订阅结果 + if(StringUtils.isEmpty(expSubscribe.getCompany()) || StringUtils.isEmpty(expSubscribe.getNumber())) + { + return subscribeResp; + } + + //订阅记录写入数据库 + ExpSubscribe newExpSubscribe = new ExpSubscribe(); + newExpSubscribe.setSid(expSubscribe.getSid()); //将时间戳设为Sid 210810 yangbo + newExpSubscribe.setCompany(expSubscribe.getCompany()); + newExpSubscribe.setNumber(expSubscribe.getNumber()); + newExpSubscribe.setPhone(expSubscribe.getPhone()); + newExpSubscribe.setSalt(expSubscribe.getSalt()); + newExpSubscribe.setSubscribeTime(expSubscribe.getSubscribeTime()); + newExpSubscribe.setResult((subscribeResp.isResult())?"true":"false"); + newExpSubscribe.setReturnCode(subscribeResp.getReturnCode()); + newExpSubscribe.setMessage(subscribeResp.getMessage()); + newExpSubscribe.setRequestFrom(expSubscribe.getRequestFrom()); + newExpSubscribe.setRequestId(expSubscribe.getRequestId()); + + /*ExpSubscribe queryExpSubscribe = new ExpSubscribe(); + queryExpSubscribe.setCompany(expSubscribe.getCompany()); + queryExpSubscribe.setNumber(expSubscribe.getNumber()); + queryExpSubscribe.setResult(expSubscribe.getResult()); + queryExpSubscribe.setReturnCode(expSubscribe.getReturnCode()); + List list=expSubscribeService.selectExpSubscribeList(queryExpSubscribe); + if(list.size()>0){ + //如果数据库中存在快递单号+快递公司编码+结果+返回码相同,则更新记录 + for(ExpSubscribe es:list){ + queryExpSubscribe= newExpSubscribe; + queryExpSubscribe.setSid(es.getSid()); + expSubscribeService.updateExpSubscribe(queryExpSubscribe); + } + }else { + //如果数据库中没有快递单号+快递公司编码,则更插入新记录 + expSubscribeService.insertExpSubscribe(newExpSubscribe); + }*/ + //20210802 无论系统里有没有记录,都会记录本次推送。 + expSubscribeService.insertExpSubscribe(newExpSubscribe); + + //返回订阅结果 + return subscribeResp; + } + + + /** + * 处理Topgp推送的快递订阅请求,向快递100推送订阅请求,并将结果返回给TOPGP + * + * @param request + * @return + */ + @Override + public String ExpressSubscribeFromTopgp(HttpServletRequest request) throws IOException { + //定义Return变量 + String returnStr; + + //获取httpServletRequest传过来的Json字符串,并进行解析 + JSONObject contentJson= JSONObject.parseObject(ServletUtils.getRequestContent(request)); + if(StringUtils.isEmpty(contentJson)){ + return "貌似没有接受到任何参数!"; + } + String requestId=contentJson.getString("requestId"); //TOPGP请求ID,年月日时分稍毫秒 + String deliveryNum= contentJson.getString("deliveryNum"); //TOPGP出货单号 + String expressNum = contentJson.getString("expressNum"); //TOPGP快递单号 + String company = contentJson.getString("company"); //TOPGP物流公司编号 + String phone = contentJson.getString("phone"); //TOPGP出货单号 + //Long timeStamp = System.currentTimeMillis(); //获取时间戳 + String subscribeTime= DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss"); //获取订阅时间 + SubscribeResp subscribeResp=new SubscribeResp(); + //如果请求ID、出货单号或者快递单号为空,则不向快递100请求订阅,自己组合返回信息。 + if(StringUtils.isEmpty(deliveryNum) || StringUtils.isEmpty(expressNum) || StringUtils.isEmpty(requestId)){ + subscribeResp.setMessage("请求ID、快递单号或出货单号不可为空"); + subscribeResp.setResult(false); + subscribeResp.setReturnCode("700"); + }else { + //组合向快递100推送订阅请求的参数 + ExpSubscribe expSubscribe=new ExpSubscribe(); + expSubscribe.setSid(Long.getLong(requestId)); //时间戳 + expSubscribe.setNumber(expressNum); + expSubscribe.setCompany(company); + expSubscribe.setPhone(phone); + expSubscribe.setSubscribeTime(subscribeTime); //订阅时间 + expSubscribe.setSalt("topgp"); // + + //向快递100推送订阅请求,取得订阅返回结果 + subscribeResp= ExpressSubscribe(expSubscribe); + + } + //根据快递100的订阅返回结果,组合返回Topgp的JSON字符串 + Map map= new HashMap<>(); + map.put("requestId",requestId); //从TOPGP传过来的requestId, 时间戳 + map.put("deliveryNum",deliveryNum); //出货单号 + map.put("expressNum",expressNum); //快递单号 + map.put("responseStr",subscribeResp.getMessage()); //返回消息 + map.put("responseCode",subscribeResp.getReturnCode()); //返回码 + map.put("result",subscribeResp.isResult()); //订阅结果 + + //返回Json字符串给TOPGP + returnStr= JSONObject.toJSONString(map); + + //记录本次TOPGP订阅请求的Log + ExpTopgpLog expTopgpLog=new ExpTopgpLog(); + expTopgpLog.setRequestId(requestId); + expTopgpLog.setRequestType("fromTopgp"); + expTopgpLog.setExpressNum(expressNum); + expTopgpLog.setDeliveryNum(deliveryNum); + expTopgpLog.setRequestStr(contentJson.toString()); + expTopgpLog.setRequestTime(subscribeTime); + expTopgpLog.setResponseCode(subscribeResp.getReturnCode()); + expTopgpLog.setResponseStr(returnStr); + //插入TOPGPLOG数据库 + expTopgpLogService.insertExpTopgpLog(expTopgpLog); + //返回TOPGP json字符串 + return returnStr; + } + + /** + * Topgp将出货单转为签收单后的信息推送处理 + * + * @param request + * @return + */ + @Override + public String TopgpDeliverySigned(HttpServletRequest request) throws IOException { + //获取httpServletRequest传过来的Json字符串,并进行解析 + String returnStr; + JSONObject contentJson= JSONObject.parseObject(ServletUtils.getRequestContent(request)); + + Map map=new HashMap<>(); + map.put("requestId",contentJson.getString("requestId")); + map.put("responseCode","200"); + map.put("expressNum",contentJson.getString("expressNum")); + returnStr= JSONObject.toJSONString(map); + + //写入TOPGP记录档 + String deliveryNum=""; + JSONArray jsonArray = JSONArray.parseArray(contentJson.getString("signedList")); + for(Object object :jsonArray){ + JSONObject jsonObject= JSONObject.parseObject(object.toString()); + deliveryNum += jsonObject.getString("deliveryNum"); + if(jsonArray.indexOf(object) list=expSubsPushRespService.selectExpSubsPushRespList(expSubsPushResp); + if(list.size()>0){ + //如果数据库中存在快递单号+快递公司编码,则更新记录 + ExpSubsPushResp newExpSubsPushResp= ToExpSubsPushResp(subscribePushParamResp); + for(ExpSubsPushResp expr:list){ + newExpSubsPushResp.setSid(expr.getSid()); + expSubsPushRespService.updateExpSubsPushResp(newExpSubsPushResp); + newExpSubsPushResp.setSid(null); + } + }else { + //如果数据库中没有快递单号+快递公司编码,则更插入新记录 + expSubsPushRespService.insertExpSubsPushResp(ToExpSubsPushResp(subscribePushParamResp)); + }*/ + return subscribeResp; + } + + //根据快递100推送的快递信息,推送给TOPGP,并将TOPGP返回信息记录到exp_topgp_log表 + private void pushExpressInfoToTopgp(SubscribePushResult subscribePushResult){ + Map requestMap = new HashMap<>(); + requestMap.put("requestId","toTopgp"+LocalDateTime.now()); //生成推送requestId + requestMap.put("expressNum", subscribePushResult.getNu()); + requestMap.put("expressCom", subscribePushResult.getCom()); + requestMap.put("expressState", subscribePushResult.getState()); + + //将签收信息推送给TOPGP,让TOPGP处理签收 + String returnXml = HttpUtils.sendXmlPost(webserviceUrl, TopgpXmlUtils.GetTopgpRequestXml("express_testRequest", requestMap)); + JSONObject jsonObject = TopgpXmlUtils.TopgpResponseXmlToJson(returnXml); + log.info(jsonObject.toJSONString()); + + //记录本次TOPGP订阅请求的Log + ExpTopgpLog expTopgpLog=new ExpTopgpLog(); + expTopgpLog.setRequestId(requestMap.get("requestId").toString()); + expTopgpLog.setRequestType("toTopgp"); + expTopgpLog.setExpressNum(requestMap.get("expressNum").toString()); + expTopgpLog.setRequestStr(JSONObject.toJSONString(requestMap)); + expTopgpLog.setRequestTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss")); + JSONObject object = jsonObject.getJSONObject("execution"); + expTopgpLog.setResponseCode(object.getString("code")); + expTopgpLog.setResponseStr(returnXml); + //插入TOPGPLOG数据库 + expTopgpLogService.insertExpTopgpLog(expTopgpLog); + + } + + + + + /** + * 将快递100推送的信息转换为ExpSubsPushResp + * @param subscribePushParamResp + * @return ExpSubsPushResp + */ + private ExpSubsPushResp ToExpSubsPushResp(SubscribePushParamResp subscribePushParamResp){ + ExpSubsPushResp expSubsPushResp=new ExpSubsPushResp(); + + SubscribePushResult subscribePushLastResult = subscribePushParamResp.getLastResult(); + SubscribePushResult subscribePushDestResult = subscribePushParamResp.getDestResult(); + + expSubsPushResp.setStatus(subscribePushParamResp.getStatus()); + expSubsPushResp.setBillStatus(subscribePushParamResp.getBillstatus()); + expSubsPushResp.setMessage(subscribePushParamResp.getMessage()); + expSubsPushResp.setAutoCheck(subscribePushParamResp.getAutoCheck()); + expSubsPushResp.setComOld(subscribePushParamResp.getComOld()); + expSubsPushResp.setComNew(subscribePushParamResp.getComNew()); + + expSubsPushResp.setLastResultMessage(subscribePushLastResult.getMessage()); + expSubsPushResp.setLastResultState(subscribePushLastResult.getState()); + expSubsPushResp.setLastResulStatus(subscribePushLastResult.getStatus()); + expSubsPushResp.setLastResultCondition(subscribePushLastResult.getCondition()); + expSubsPushResp.setLastResultIsCheck(subscribePushLastResult.getIscheck()); + expSubsPushResp.setLastResultCom(subscribePushLastResult.getCom()); + expSubsPushResp.setLastResultNu(subscribePushLastResult.getNu()); + expSubsPushResp.setLastResultData(SubscribePushDataToString(subscribePushLastResult.getData())); + + if(subscribePushDestResult != null) { + //只有邮政国外的快递推送才会有DestResult信息 + expSubsPushResp.setDestResultMessage(subscribePushDestResult.getMessage()); + expSubsPushResp.setDestResultState(subscribePushDestResult.getState()); + expSubsPushResp.setDestResultStatus(subscribePushDestResult.getStatus()); + expSubsPushResp.setDestResultCondition(subscribePushDestResult.getCondition()); + expSubsPushResp.setDestResultIsCheck(subscribePushDestResult.getIscheck()); + expSubsPushResp.setDestResultCom(subscribePushDestResult.getCom()); + expSubsPushResp.setDestResultNu(subscribePushDestResult.getNu()); + expSubsPushResp.setDestResultData(SubscribePushDataToString(subscribePushDestResult.getData())); + } + expSubsPushResp.setLastResponseTime(DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss")); + + return expSubsPushResp; + } + + /** + * + * @param list 将List转化为字符串 + * @return + */ + private String SubscribePushDataToString(List list){ + String str=""; + for(SubscribePushData subscribePushData:list){ + str+="【"+subscribePushData.getTime()+"】 "; + if(StringUtils.isNotEmpty(subscribePushData.getAreaName())) + { + str+=subscribePushData.getAreaName()+"/"; //某些快递没有AreaName信息 + } + str+=subscribePushData.getContext(); + if(list.size()-1>list.indexOf(subscribePushData)){ + str+="\r\n"; + } + } + //System.out.println(str); + return str; + } + + + + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushRespServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushRespServiceImpl.java new file mode 100644 index 000000000..777b2568d --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubsPushRespServiceImpl.java @@ -0,0 +1,95 @@ +package com.ruoyi.bps.service.impl; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.mapper.ExpSubsPushRespMapper; +import com.ruoyi.bps.service.IExpSubsPushRespService; +import com.ruoyi.common.core.text.Convert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 快递订阅推送信息Service业务层处理 + * + * @author box + * @date 2021-05-13 + */ +@Service +public class ExpSubsPushRespServiceImpl implements IExpSubsPushRespService +{ + @Autowired + private ExpSubsPushRespMapper expSubsPushRespMapper; + + /** + * 查询快递订阅推送信息 + * + * @param sid 快递订阅推送信息ID + * @return 快递订阅推送信息 + */ + @Override + public ExpSubsPushResp selectExpSubsPushRespById(Long sid) + { + return expSubsPushRespMapper.selectExpSubsPushRespById(sid); + } + + /** + * 查询快递订阅推送信息列表 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 快递订阅推送信息 + */ + @Override + public List selectExpSubsPushRespList(ExpSubsPushResp expSubsPushResp) + { + return expSubsPushRespMapper.selectExpSubsPushRespList(expSubsPushResp); + } + + /** + * 新增快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + @Override + public int insertExpSubsPushResp(ExpSubsPushResp expSubsPushResp) + { + return expSubsPushRespMapper.insertExpSubsPushResp(expSubsPushResp); + } + + /** + * 修改快递订阅推送信息 + * + * @param expSubsPushResp 快递订阅推送信息 + * @return 结果 + */ + @Override + public int updateExpSubsPushResp(ExpSubsPushResp expSubsPushResp) + { + return expSubsPushRespMapper.updateExpSubsPushResp(expSubsPushResp); + } + + /** + * 删除快递订阅推送信息对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteExpSubsPushRespByIds(String ids) + { + return expSubsPushRespMapper.deleteExpSubsPushRespByIds(Convert.toStrArray(ids)); + } + + /** + * 删除快递订阅推送信息信息 + * + * @param sid 快递订阅推送信息ID + * @return 结果 + */ + @Override + public int deleteExpSubsPushRespById(Long sid) + { + return expSubsPushRespMapper.deleteExpSubsPushRespById(sid); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubscribeServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubscribeServiceImpl.java new file mode 100644 index 000000000..259ac3aa2 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpSubscribeServiceImpl.java @@ -0,0 +1,108 @@ +package com.ruoyi.bps.service.impl; + +import com.ruoyi.bps.domain.ExpSubsPushResp; +import com.ruoyi.bps.domain.ExpSubscribe; +import com.ruoyi.bps.mapper.ExpSubscribeMapper; +import com.ruoyi.bps.service.IExpSubscribeService; +import com.ruoyi.common.core.text.Convert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 快递订阅Service业务层处理 + * + * @author box + * @date 2021-05-20 + */ +@Service +public class ExpSubscribeServiceImpl implements IExpSubscribeService +{ + @Autowired + private ExpSubscribeMapper expSubscribeMapper; + + /** + * 查询快递订阅 + * + * @param sid 快递订阅ID + * @return 快递订阅 + */ + @Override + public ExpSubscribe selectExpSubscribeById(Long sid) + { + return expSubscribeMapper.selectExpSubscribeById(sid); + } + + /** + * 查询快递订阅列表 + * + * @param expSubscribe 快递订阅 + * @return 快递订阅 + */ + @Override + public List selectExpSubscribeList(ExpSubscribe expSubscribe) + { + return expSubscribeMapper.selectExpSubscribeList(expSubscribe); + } + + /** + * 新增快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + @Override + public int insertExpSubscribe(ExpSubscribe expSubscribe) + { + return expSubscribeMapper.insertExpSubscribe(expSubscribe); + } + + /** + * 修改快递订阅 + * + * @param expSubscribe 快递订阅 + * @return 结果 + */ + @Override + public int updateExpSubscribe(ExpSubscribe expSubscribe) + { + return expSubscribeMapper.updateExpSubscribe(expSubscribe); + } + + /** + * 删除快递订阅对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteExpSubscribeByIds(String ids) + { + return expSubscribeMapper.deleteExpSubscribeByIds(Convert.toStrArray(ids)); + } + + /** + * 删除快递订阅信息 + * + * @param sid 快递订阅ID + * @return 结果 + */ + @Override + public int deleteExpSubscribeById(Long sid) + { + return expSubscribeMapper.deleteExpSubscribeById(sid); + } + + + /** + * 根据快递单号查询快递订阅推送信息 + * + * @param number 快递单号List + * @return 快递订阅推送信息 + */ + @Override + public List selectExpSubsPushRespByNumber(List number){ + return expSubscribeMapper.selectExpSubscribeByNumber(number); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpTopgpLogServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpTopgpLogServiceImpl.java new file mode 100644 index 000000000..452441244 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpTopgpLogServiceImpl.java @@ -0,0 +1,94 @@ +package com.ruoyi.bps.service.impl; + +import java.util.List; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.bps.mapper.ExpTopgpLogMapper; +import com.ruoyi.bps.domain.ExpTopgpLog; +import com.ruoyi.bps.service.IExpTopgpLogService; +import com.ruoyi.common.core.text.Convert; + +/** + * ERP订阅推送日志Service业务层处理 + * + * @author Bo + * @date 2021-08-11 + */ +@Service +public class ExpTopgpLogServiceImpl implements IExpTopgpLogService +{ + @Autowired + private ExpTopgpLogMapper expTopgpLogMapper; + + /** + * 查询ERP订阅推送日志 + * + * @param sid ERP订阅推送日志主键 + * @return ERP订阅推送日志 + */ + @Override + public ExpTopgpLog selectExpTopgpLogBySid(Long sid) + { + return expTopgpLogMapper.selectExpTopgpLogBySid(sid); + } + + /** + * 查询ERP订阅推送日志列表 + * + * @param expTopgpLog ERP订阅推送日志 + * @return ERP订阅推送日志 + */ + @Override + public List selectExpTopgpLogList(ExpTopgpLog expTopgpLog) + { + return expTopgpLogMapper.selectExpTopgpLogList(expTopgpLog); + } + + /** + * 新增ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + @Override + public int insertExpTopgpLog(ExpTopgpLog expTopgpLog) + { + return expTopgpLogMapper.insertExpTopgpLog(expTopgpLog); + } + + /** + * 修改ERP订阅推送日志 + * + * @param expTopgpLog ERP订阅推送日志 + * @return 结果 + */ + @Override + public int updateExpTopgpLog(ExpTopgpLog expTopgpLog) + { + return expTopgpLogMapper.updateExpTopgpLog(expTopgpLog); + } + + /** + * 批量删除ERP订阅推送日志 + * + * @param sids 需要删除的ERP订阅推送日志主键 + * @return 结果 + */ + @Override + public int deleteExpTopgpLogBySids(String sids) + { + return expTopgpLogMapper.deleteExpTopgpLogBySids(Convert.toStrArray(sids)); + } + + /** + * 删除ERP订阅推送日志信息 + * + * @param sid ERP订阅推送日志主键 + * @return 结果 + */ + @Override + public int deleteExpTopgpLogBySid(Long sid) + { + return expTopgpLogMapper.deleteExpTopgpLogBySid(sid); + } +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressInfoServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressInfoServiceImpl.java new file mode 100644 index 000000000..159955dd3 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressInfoServiceImpl.java @@ -0,0 +1,318 @@ +package com.ruoyi.bps.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.bps.domain.ExpressInfo; +import com.ruoyi.bps.mapper.ExpressInfoMapper; +import com.ruoyi.bps.service.IExpressInfoService; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.common.utils.StringUtils; +import com.kuaidi100.sdk.api.AutoNum; +import com.kuaidi100.sdk.api.QueryTrack; +import com.kuaidi100.sdk.core.IBaseClient; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.AutoNumReq; +import com.kuaidi100.sdk.request.QueryTrackParam; +import com.kuaidi100.sdk.request.QueryTrackReq; +import com.kuaidi100.sdk.response.AutoNumResp; +import com.kuaidi100.sdk.response.QueryTrackData; +import com.kuaidi100.sdk.response.QueryTrackResp; +import com.kuaidi100.sdk.utils.PropertiesReader; +import com.kuaidi100.sdk.utils.SignUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * 快递信息Service业务层处理 + * + * @author box + * @date 2021-05-06 + */ +@Service +public class ExpressInfoServiceImpl implements IExpressInfoService +{ + /* + String key = PropertiesReader.get("key"); + String customer = PropertiesReader.get("customer"); + String secret = PropertiesReader.get("secret"); + String siid = PropertiesReader.get("siid"); + String userid = PropertiesReader.get("userid"); + String tid = PropertiesReader.get("tid"); + String secret_key = PropertiesReader.get("secret_key"); + String secret_secret = PropertiesReader.get("secret_secret"); + */ + @Value("${express.key}") + private String key; + + @Value("${express.customer}") + private String customer; + + + + + String msg=""; + + @Autowired + private ExpressInfoMapper expressInfoMapper; + + /** + * 查询快递信息 + * + * @param message 快递信息ID + * @return 快递信息 + */ + @Override + public ExpressInfo selectExpressInfoById(String message) + { + return expressInfoMapper.selectExpressInfoById(message); + } + + /** + * 查询本地快递信息列表 + * + * @param expressInfo 快递信息 + * @return 快递信息集合 + */ + @Override + public List selectLocalExpressInfoList(ExpressInfo expressInfo) { + return expressInfoMapper.selectExpressInfoList(expressInfo); + } + + /** + * 查询快递信息列表 + * + * @param expressInfo 快递信息 + * @return 快递信息列表 + */ + @Override + public List selectExpressInfoList(ExpressInfo expressInfo) + { + List expressInfoList=new ArrayList<>(); + //如果没有输入订单号,则返回空信息 + String nuStr=expressInfo.getNu(); + if(StringUtils.isEmpty(nuStr)){ + expressInfo.setData("请输入订单号进行查询!"); + expressInfoList.add(expressInfo); + return expressInfoList; + } + //如果是顺丰,则必须要输入电话号码 + if( StringUtils.isEmpty(expressInfo.getPhone()) + && (expressInfo.getCom().equals("nsf") || expressInfo.getCom().contains("shunfeng"))){ + expressInfo.setData("查询顺丰快递信息,必须要提供收/寄人电话号码"); + expressInfoList.add(expressInfo); + return expressInfoList; + } + + List stringList= Arrays.asList(nuStr.split(",")); + ExpressInfo newExpressInfo= expressInfo; + for(String str:stringList){ + newExpressInfo.setNu(str); + expressInfoList.add(SelectExpressInfo(newExpressInfo)); + } + return expressInfoList; + //return expressInfoMapper.selectExpressInfoList(expressInfo); + } + + @Override + public ExpressInfo SelectExpressInfo(ExpressInfo expressInfo){ + String nu=expressInfo.getNu(); //快递单号 + String com=expressInfo.getCom(); //快递公司 + String phone=expressInfo.getPhone(); //收、寄件人电话号码 + String deliveryNum= expressInfo.getDeliveryNum(); + ExpressInfo callbackExpressInfo=new ExpressInfo(); + + callbackExpressInfo.setNu(nu); + callbackExpressInfo.setPhone(phone); + callbackExpressInfo.setDeliveryNum(deliveryNum); + //如果没有输入快递公司编号,则查询快递公司编号 + if(StringUtils.isEmpty(com)){ + List list= AutoGetExpressCom(nu); + if(null==list || list.size()<1){ + callbackExpressInfo.setData("请提供要查询的快递所属物流公司编号!,且根据快递单号没有查询到物流公司编号!"); + return callbackExpressInfo; + } + if (list.size()>1) + { + callbackExpressInfo.setData("您没有提供要查询的快递所属物流公司编号,且根据快递单号查询到多个物流公司编号"); + return callbackExpressInfo; + } + com=list.get(0).getComCode(); + } + callbackExpressInfo.setCom(com); + + //return callbackExpressInfo; + return QueryExpressInfo(callbackExpressInfo); + } + + private ExpressInfo QueryExpressInfo(ExpressInfo expressInfo){ + //从expressInfo中获取快递单号、物流信息、电话,生成快递请求参数 + QueryTrackParam queryTrackParam= new QueryTrackParam(); + queryTrackParam.setNum(expressInfo.getNu()); + queryTrackParam.setCom(expressInfo.getCom()); + queryTrackParam.setPhone(expressInfo.getPhone()); + + //获取快递信息 + String param= JSONObject.toJSONString(queryTrackParam); + QueryTrackReq queryTrackReq=new QueryTrackReq(); + queryTrackReq.setParam(param); + queryTrackReq.setCustomer(customer); + queryTrackReq.setSign(SignUtils.querySign(param ,key,customer)); + HttpResult httpResult =new HttpResult(); + IBaseClient baseClient = new QueryTrack(); + try { + httpResult = baseClient.execute(queryTrackReq); + msg=httpResult.getBody(); + } + catch (Exception e) { + msg=e.toString(); + } + + //将快递信息转化为QueryTrackResp对象 + QueryTrackResp queryTrackResp= JSONObject.parseObject(msg,QueryTrackResp.class); + + //如果没有查到物流信息,则返回错误信息 + if(StringUtils.isEmpty(queryTrackResp.getStatus()) || !queryTrackResp.getStatus().equals("200")){ + expressInfo.setData(queryTrackResp.getMessage()); + return expressInfo; + } + + //获取签收时间 + String signedTime=null; + if(queryTrackResp.getState().equals("3")) { + signedTime=queryTrackResp.getData().get(0).getFtime(); + } + + //获取最后更新时间 + String lastUpdateTime=queryTrackResp.getData().get(0).getFtime(); + + //获取揽收时间 + String collectTime= queryTrackResp.getData().get(queryTrackResp.getData().size()-1).getTime(); + + //获取查询时间 + String queryTime= StringUtils.isNotEmpty(expressInfo.getQueryTime())?expressInfo.getQueryTime():DateUtils.dateTimeNow("yyyy-MM-dd HH:mm:ss"); + + //获取查询人(登录用户) + String queryUserName= ShiroUtils.getLoginName(); + + //将快递信息中的Context转化为字符 + String dataStr=""; + for(QueryTrackData queryTrackData :queryTrackResp.getData()){ + dataStr+="【"+queryTrackData.getTime()+"】 "; + dataStr+=queryTrackData.getContext(); + if(queryTrackResp.getData().size()-1>queryTrackResp.getData().indexOf(queryTrackData)){ + dataStr+="\r\n"; + } + } + String a= queryTrackResp.getCondition(); + ExpressInfo callbackExpressInfo=new ExpressInfo(); + callbackExpressInfo.setMessage(queryTrackResp.getMessage()); + callbackExpressInfo.setNu(queryTrackResp.getNu()); + callbackExpressInfo.setIscheck(queryTrackResp.getIscheck()); + callbackExpressInfo.setCom(queryTrackResp.getCom()); + callbackExpressInfo.setStatus(queryTrackResp.getStatus()); + callbackExpressInfo.setData(dataStr); + callbackExpressInfo.setState(queryTrackResp.getState()); + callbackExpressInfo.setCondition(queryTrackResp.getCondition()); + callbackExpressInfo.setRouteInfo(""); //出发位置,当前位置,到达位置,暂无信息 + callbackExpressInfo.setReturnCode(queryTrackResp.getReturnCode()); + callbackExpressInfo.setResult(queryTrackResp.isResult()?"Y":"N"); + callbackExpressInfo.setPhone(expressInfo.getPhone()); + callbackExpressInfo.setSingedTime(signedTime); + callbackExpressInfo.setCollectTime(collectTime); + callbackExpressInfo.setLastUpdateTime(lastUpdateTime); + callbackExpressInfo.setQueryTime(queryTime); + callbackExpressInfo.setQueryUserName(queryUserName); + callbackExpressInfo.setDeliveryNum(expressInfo.getDeliveryNum()); + + return callbackExpressInfo; + } + + /** + * 根据快递单号,查询快递公司编码 + * @param num 快递单号 + * @return 快递公司编码 + */ + + private List AutoGetExpressCom(String num){ + AutoNumReq autoNumReq = new AutoNumReq(); + autoNumReq.setKey(key); + autoNumReq.setNum(num.trim()); + + IBaseClient baseClient = new AutoNum(); + //AutoNumResp autoNumResp=new AutoNumResp(); + List autoNumRespList=new ArrayList<>(); + try { + JSONArray jsonArray= JSONArray.parseArray(baseClient.execute(autoNumReq).getBody()); + if(StringUtils.isEmpty(jsonArray)) + { + return null; + } + for (Object object:jsonArray) + { + autoNumRespList.add(JSONObject.parseObject(JSONObject.toJSONString(object),AutoNumResp.class)); + } + + } catch (Exception e) { + e.printStackTrace(); + } + //return autoNumResp; + return autoNumRespList; + } + + /** + * 新增快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + @Override + public int insertExpressInfo(ExpressInfo expressInfo) + { + return expressInfoMapper.insertExpressInfo(expressInfo); + } + + /** + * 修改快递信息 + * + * @param expressInfo 快递信息 + * @return 结果 + */ + @Override + public int updateExpressInfo(ExpressInfo expressInfo) + { + return expressInfoMapper.updateExpressInfo(expressInfo); + } + + /** + * 删除快递信息对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteExpressInfoByIds(String ids) + { + return expressInfoMapper.deleteExpressInfoByIds(Convert.toStrArray(ids)); + } + + /** + * 删除快递信息信息 + * + * @param message 快递信息ID + * @return 结果 + */ + @Override + public int deleteExpressInfoById(String message) + { + return expressInfoMapper.deleteExpressInfoById(message); + } + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressServiceImpl.java new file mode 100644 index 000000000..5e547144f --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/ExpressServiceImpl.java @@ -0,0 +1,175 @@ +package com.ruoyi.bps.service.impl; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.kuaidi100.sdk.api.QueryTrack; +import com.kuaidi100.sdk.api.Subscribe; +import com.kuaidi100.sdk.contant.ApiInfoConstant; +import com.kuaidi100.sdk.core.IBaseClient; +import com.kuaidi100.sdk.pojo.HttpResult; +import com.kuaidi100.sdk.request.*; +import com.kuaidi100.sdk.response.QueryTrackResp; +import com.kuaidi100.sdk.utils.PropertiesReader; +import com.kuaidi100.sdk.utils.SignUtils; +import com.ruoyi.bps.service.IExpressService; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +@Service +public class ExpressServiceImpl implements IExpressService { + /*String key = PropertiesReader.get("key");*/ + + @Value("${express.key}") + private String key; + + @Value("${express.customer}") + private String customer; + + String msg=""; + @Autowired + IExpressService expressService; + + @Override + public List QueryTrackExpressMultiList(List list) { + List qtList=new ArrayList<>(); + + for(QueryTrackParam queryTrackParam:list) + { + QueryTrackResp queryTrackResp= JSONObject.parseObject(expressService.QueryTrackExpress(queryTrackParam),QueryTrackResp.class); + qtList.add(queryTrackResp); + } + return qtList; + } + + /** + *查询多个物流轨迹 + */ + @Override + public String QueryTrackExpressMulti(List list) { + String str=""; + for(QueryTrackParam qt:list){ + str += QueryTrackExpress(qt); + } + return str; + } + + /** + * 查询单个物流轨迹 + */ + @Override + public String QueryTrackExpress(QueryTrackParam queryTrackParam) { + String str=""; + QueryTrackReq queryTrackReq = new QueryTrackReq(); + String param = JSONObject.toJSONString(queryTrackParam); + + queryTrackReq.setParam(param); + queryTrackReq.setCustomer(customer); + queryTrackReq.setSign(SignUtils.querySign(param ,key,customer)); + HttpResult httpResult=new HttpResult(); + IBaseClient baseClient = new QueryTrack(); + try { + httpResult = baseClient.execute(queryTrackReq); + msg=httpResult.getBody(); + } + catch (Exception e) { + msg=e.toString(); + } + + //JSONObject jsonObject = new JSONObject(msg); + JSONObject jsonObject = JSON.parseObject(msg); + + if (jsonObject.containsKey("returnCode")){ + QueryTrackResp queryTrackResp= JSONObject.parseObject(msg,QueryTrackResp.class); + queryTrackResp.setStatus(queryTrackResp.getReturnCode()); + queryTrackResp.setNu(queryTrackParam.getNum()); + msg= JSONObject.toJSONString(queryTrackResp); + } + return msg; + } + + /** + * 查询物流转迹 + */ + @Override + public String SubscribeExpress() { + SubscribeParameters subscribeParameters = new SubscribeParameters(); + subscribeParameters.setCallbackurl("http://www.baidu.com"); //回调接口的地址,必须 + subscribeParameters.setPhone("17725390266"); //收、寄件人电话号码,非必须 + SubscribeParam subscribeParam = new SubscribeParam(); + subscribeParam.setParameters(subscribeParameters); + subscribeParam.setCompany("annengwuliu"); //快递公司编码,小写。必须 + subscribeParam.setNumber("300445967949"); //快递单号, 必须 + subscribeParam.setKey(key); //授权码,必须 + + SubscribeReq subscribeReq = new SubscribeReq(); + subscribeReq.setSchema(ApiInfoConstant.SUBSCRIBE_SCHEMA); //返回的数据格式,必须 + subscribeReq.setParam(JSONObject.toJSONString(subscribeParam)); + + IBaseClient subscribe = new Subscribe(); + try{ + msg=subscribe.execute(subscribeReq).toString(); + } + catch (Exception e) { + msg=e.toString(); + } + System.out.println(msg); + return msg; + } + + @Override + public List GetTestQueryTrackParam() { + QueryTrackParam queryTrackParam = new QueryTrackParam(); + List list=new ArrayList(); + queryTrackParam.setCom("annengwuliu"); + queryTrackParam.setNum("300445967949"); + queryTrackParam.setPhone("17725390266"); + list.add(queryTrackParam); + + QueryTrackParam queryTrackParam1 = new QueryTrackParam(); + queryTrackParam1.setCom("annengwuliu"); + queryTrackParam1.setNum("300445967135"); + queryTrackParam1.setPhone("17725390266"); + list.add(queryTrackParam1); + + + QueryTrackParam queryTrackParam2 = new QueryTrackParam(); + queryTrackParam2.setCom("annengwuliu"); + queryTrackParam2.setNum("3004459670971"); + queryTrackParam2.setPhone("17725390266"); + list.add(queryTrackParam2); +/* + QueryTrackParam queryTrackParam3 = new QueryTrackParam(); + queryTrackParam3.setCom(CompanyConstant.AN); + queryTrackParam3.setNum("300445967045"); + queryTrackParam3.setPhone("17725390266"); + list.add(queryTrackParam3); + + QueryTrackParam queryTrackParam4 = new QueryTrackParam(); + queryTrackParam4.setCom(CompanyConstant.AN); + queryTrackParam4.setNum("300443569920"); + queryTrackParam4.setPhone("17725390266"); + list.add(queryTrackParam4); + + QueryTrackParam queryTrackParam5 = new QueryTrackParam(); + queryTrackParam5.setCom(CompanyConstant.AN); + queryTrackParam5.setNum("300443569878"); + queryTrackParam5.setPhone("17725390266"); + list.add(queryTrackParam5); + + QueryTrackParam queryTrackParam6 = new QueryTrackParam(); + queryTrackParam6.setCom(CompanyConstant.AN); + queryTrackParam6.setNum("300443569880"); + queryTrackParam6.setPhone("17725390266"); + list.add(queryTrackParam6); + */ + return list; + } + + +} diff --git a/box-bps/src/main/java/com/ruoyi/bps/service/impl/TopgpDdlServiceImpl.java b/box-bps/src/main/java/com/ruoyi/bps/service/impl/TopgpDdlServiceImpl.java new file mode 100644 index 000000000..932526508 --- /dev/null +++ b/box-bps/src/main/java/com/ruoyi/bps/service/impl/TopgpDdlServiceImpl.java @@ -0,0 +1,46 @@ +package com.ruoyi.bps.service.impl; + +import com.ruoyi.bps.mapper.TopgpDdlMapper; +import com.ruoyi.bps.service.TopgpDdlService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +//@DataSource(value = DataSourceType.SLAVE) +//@DataSource(value = DataSourceType.TOPTESTDSREPORT) +public class TopgpDdlServiceImpl implements TopgpDdlService { + @Autowired + private TopgpDdlMapper topgpDdlMapper; + + //修改表名 + public int alterTableName(String originalTableName, String newTableName) + { + return topgpDdlMapper.alterTableName(originalTableName,newTableName); + } + + // truncate指定数据库表的数据 + public int truncateTable(String tableName){ + return topgpDdlMapper.truncateTable(tableName); + } + + //drop 指定定数据库表 + public int dropTable(String tableName){ + return topgpDdlMapper.dropTable(tableName); + } + + + //根据传入的表明,创建新的表并且将原表的数据插入到新的Occur表中 + public void copyTable(String newTableName,String originalTableName){ + return ; + } + + //统计某张表中的总数据条数 + public int getRecordCount(String tableName){ + return topgpDdlMapper.getRecordCount(tableName); + } + + //从指定数据库中,查询是否存在某张表 + public int isTableInDb(String dataBaseName, String tableName){ + return topgpDdlMapper.isTableInDb(dataBaseName,tableName); + } +} diff --git a/box-bps/src/main/resources/account.properties b/box-bps/src/main/resources/account.properties new file mode 100644 index 000000000..eca321a27 --- /dev/null +++ b/box-bps/src/main/resources/account.properties @@ -0,0 +1,25 @@ +#快递100的基础账号信息,可以在这里获取 +# https://poll.kuaidi100.com/manager/page/myinfo/enterprise +#key = kzuyKyAE3985 +#customer = 6774D6F41D773B17027EEBE5CC902C9E +#secret = 4fc7633a027c4fe1a68b68237c236d6e +#userid = bfc0389a986f45c4b36e27d9b18b7bd3 + +#电子面单快递公司账号信息(非必填) +partnerId = +partnerKey = +net = +siid = + +#短信模板id(非必填) +tid = + +#云平台相关(非必填) +#登录云平台 https://cloud.kuaidi100.com/buyer/user/info +secret_key = +secret_secret = + +#是否记录快递100接口返回结果,建议记录日志或者入库,方便后期有问题双方排查(true:启用 false: 关闭 ) +#log.return.record = true +#日志记录位置,建议根据自身情况配置 +#logPath = logs \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/ExpImportQueryMapper.xml b/box-bps/src/main/resources/mapper/bps/ExpImportQueryMapper.xml new file mode 100644 index 000000000..1a405a2c7 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/ExpImportQueryMapper.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + select sid, queryId, queryTime, queryLoginName, queryUserName, queryIp, finishTime, status, queryQty from exp_import_query + + + + + + + + insert into exp_import_query + + queryTime, + queryId, + queryLoginName, + queryUserName, + queryIp, + finishTime, + status, + queryQty, + + + #{queryTime}, + #{queryId}, + #{queryLoginName}, + #{queryUserName}, + #{queryIp}, + #{finishTime}, + #{status}, + #{queryQty}, + + + + + update exp_import_query + + queryTime = #{queryTime}, + queryId = #{queryId}, + queryLoginName = #{queryLoginName}, + queryUserName = #{queryUserName}, + queryIp = #{queryIp}, + finishTime = #{finishTime}, + status = #{status}, + queryQty = #{queryQty}, + + where sid = #{sid} + + + + delete from exp_import_query where sid = #{sid} + + + + delete from exp_import_query where sid in + + #{sid} + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/ExpSubsPushRespMapper.xml b/box-bps/src/main/resources/mapper/bps/ExpSubsPushRespMapper.xml new file mode 100644 index 000000000..3c8b7e752 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/ExpSubsPushRespMapper.xml @@ -0,0 +1,153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select sid, status, billStatus, message, autoCheck, comOld, comNew, lastResultMessage, lastResultState, lastResulStatus, lastResultCondition, lastResultIsCheck, lastResultCom, lastResultNu, lastResultData, destResultMessage, destResultState, destResultStatus, destResultCondition, destResultIsCheck, destResultCom, destResultNu, destResultData, lastResponseTime from exp_subs_push_resp + + + + + + + + insert into exp_subs_push_resp + + status, + billStatus, + message, + autoCheck, + comOld, + comNew, + lastResultMessage, + lastResultState, + lastResulStatus, + lastResultCondition, + lastResultIsCheck, + lastResultCom, + lastResultNu, + lastResultData, + destResultMessage, + destResultState, + destResultStatus, + destResultCondition, + destResultIsCheck, + destResultCom, + destResultNu, + destResultData, + lastResponseTime, + + + #{status}, + #{billStatus}, + #{message}, + #{autoCheck}, + #{comOld}, + #{comNew}, + #{lastResultMessage}, + #{lastResultState}, + #{lastResulStatus}, + #{lastResultCondition}, + #{lastResultIsCheck}, + #{lastResultCom}, + #{lastResultNu}, + #{lastResultData}, + #{destResultMessage}, + #{destResultState}, + #{destResultStatus}, + #{destResultCondition}, + #{destResultIsCheck}, + #{destResultCom}, + #{destResultNu}, + #{destResultData}, + #{lastResponseTime}, + + + + + update exp_subs_push_resp + + status = #{status}, + billStatus = #{billStatus}, + message = #{message}, + autoCheck = #{autoCheck}, + comOld = #{comOld}, + comNew = #{comNew}, + lastResultMessage = #{lastResultMessage}, + lastResultState = #{lastResultState}, + lastResulStatus = #{lastResulStatus}, + lastResultCondition = #{lastResultCondition}, + lastResultIsCheck = #{lastResultIsCheck}, + lastResultCom = #{lastResultCom}, + lastResultNu = #{lastResultNu}, + lastResultData = #{lastResultData}, + destResultMessage = #{destResultMessage}, + destResultState = #{destResultState}, + destResultStatus = #{destResultStatus}, + destResultCondition = #{destResultCondition}, + destResultIsCheck = #{destResultIsCheck}, + destResultCom = #{destResultCom}, + destResultNu = #{destResultNu}, + destResultData = #{destResultData}, + lastResponseTime = #{lastResponseTime}, + + where sid = #{sid} + + + + delete from exp_subs_push_resp where sid = #{sid} + + + + delete from exp_subs_push_resp where sid in + + #{sid} + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/ExpSubscribeMapper.xml b/box-bps/src/main/resources/mapper/bps/ExpSubscribeMapper.xml new file mode 100644 index 000000000..03598aec7 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/ExpSubscribeMapper.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + select sid, company, number, phone, salt, subscribeTime, result, returnCode, message, requestFrom, requestId from exp_subscribe + + + + + + + + insert into exp_subscribe + + company, + number, + phone, + salt, + subscribeTime, + result, + returnCode, + message, + requestFrom, + requestId, + + + #{company}, + #{number}, + #{phone}, + #{salt}, + #{subscribeTime}, + #{result}, + #{returnCode}, + #{message}, + #{requestFrom}, + #{requestId}, + + + + + update exp_subscribe + + company = #{company}, + number = #{number}, + phone = #{phone}, + salt = #{salt}, + subscribeTime = #{subscribeTime}, + result = #{result}, + returnCode = #{returnCode}, + message = #{message}, + requestFrom = #{requestFrom}, + requestId = #{requestId}, + + where sid = #{sid} + + + + delete from exp_subscribe where sid = #{sid} + + + + delete from exp_subscribe where sid in + + #{sid} + + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/ExpTopgpLogMapper.xml b/box-bps/src/main/resources/mapper/bps/ExpTopgpLogMapper.xml new file mode 100644 index 000000000..7ccd1e472 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/ExpTopgpLogMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + select sid, requestId, requestType, expressNum, deliveryNum, requestStr, requestTime, responseCode, responseStr from exp_topgp_log + + + + + + + + insert into exp_topgp_log + + requestId, + requestType, + expressNum, + deliveryNum, + requestStr, + requestTime, + responseCode, + responseStr, + + + #{requestId}, + #{requestType}, + #{expressNum}, + #{deliveryNum}, + #{requestStr}, + #{requestTime}, + #{responseCode}, + #{responseStr}, + + + + + update exp_topgp_log + + requestId = #{requestId}, + requestType = #{requestType}, + expressNum = #{expressNum}, + deliveryNum = #{deliveryNum}, + requestStr = #{requestStr}, + requestTime = #{requestTime}, + responseCode = #{responseCode}, + responseStr = #{responseStr}, + + where sid = #{sid} + + + + delete from exp_topgp_log where sid = #{sid} + + + + delete from exp_topgp_log where sid in + + #{sid} + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/ExpressInfoMapper.xml b/box-bps/src/main/resources/mapper/bps/ExpressInfoMapper.xml new file mode 100644 index 000000000..d9df789f5 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/ExpressInfoMapper.xml @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select sid, message, nu, deliveryNum, ischeck, com, status, `data`, `state`, `condition`, routeInfo, returnCode, `result`, phone, + collectTime, singedTime, lastUpdateTime, queryTime, queryUserName, queryId, queryType, csName, customer + from expressInfo + + + + + + + + insert into expressInfo + + message, + nu, + ischeck, + com, + status, + `data`, + `state`, + `condition`, + routeInfo, + returnCode, + `result`, + phone, + collectTime, + singedTime, + lastUpdateTime, + queryTime, + queryUserName, + queryId, + queryType, + deliveryNum, + csName, + customer, + + + #{message}, + #{nu}, + #{ischeck}, + #{com}, + #{status}, + #{data}, + #{state}, + #{condition}, + #{routeInfo}, + #{returnCode}, + #{result}, + #{phone}, + #{collectTime}, + #{singedTime}, + #{lastUpdateTime}, + #{queryTime}, + #{queryUserName}, + #{queryId}, + #{queryType}, + #{deliveryNum}, + #{csName}, + #{customer}, + + + + + update expressInfo + + nu = #{nu}, + ischeck = #{ischeck}, + com = #{com}, + status = #{status}, + `data` = #{data}, + `state` = #{state}, + `condition` = #{condition}, + routeInfo = #{routeInfo}, + returnCode = #{returnCode}, + `result` = #{result}, + phone = #{phone}, + collectTime = #{collectTime}, + singedTime = #{singedTime}, + lastUpdateTime = #{lastUpdateTime}, + queryTime = #{queryTime}, + queryUserName = #{queryUserName}, + queryId = #{queryId}, + queryType = #{queryType}, + deliveryNum = #{deliveryNum}, + csName = #{csName}, + customer = #{customer}, + + where message = #{message} + + + + delete from expressInfo where sid = #{sid} + + + + delete from expressInfo where sid in + + #{sid} + + + + + insert into expressInfo(message, nu, deliveryNum, ischeck, com, status, `data`, `state`, `condition`, routeInfo, returnCode, `result`, phone, + collectTime, singedTime, lastUpdateTime, queryTime, queryUserName, queryId, queryType, csName, customer) values + + ( #{expressInfo.message}, #{expressInfo.nu}, #{expressInfo.deliveryNum}, #{expressInfo.ischeck}, #{expressInfo.com}, #{expressInfo.status}, + #{expressInfo.data}, #{expressInfo.state}, #{expressInfo.condition}, #{expressInfo.routeInfo}, #{expressInfo.returnCode}, + #{expressInfo.result}, #{expressInfo.phone}, #{expressInfo.collectTime}, #{expressInfo.singedTime}, #{expressInfo.lastUpdateTime}, + #{expressInfo.queryTime}, #{expressInfo.queryUserName}, #{expressInfo.queryId}, #{expressInfo.queryType}, #{expressInfo.csName}, #{expressInfo.customer} + ) + + + + + delete from expressInfo where queryId in (select queryId from exp_import_query where sid= #{sid}) + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/mapper/bps/TopgpDdlMapper.xml b/box-bps/src/main/resources/mapper/bps/TopgpDdlMapper.xml new file mode 100644 index 000000000..553117013 --- /dev/null +++ b/box-bps/src/main/resources/mapper/bps/TopgpDdlMapper.xml @@ -0,0 +1,29 @@ + + + + + + alter table ${originalTableName} rename ${newTableName} + + + + truncate table ${tableName} + + + + drop table ${tableName} + + + + create table ${newTableName} as select * from ${originalTableName} + + + + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expImportQuery/add.html b/box-bps/src/main/resources/templates/bps/expImportQuery/add.html new file mode 100644 index 000000000..0f1e8b2be --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expImportQuery/add.html @@ -0,0 +1,75 @@ + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expImportQuery/detail.html b/box-bps/src/main/resources/templates/bps/expImportQuery/detail.html new file mode 100644 index 000000000..a851c1185 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expImportQuery/detail.html @@ -0,0 +1,147 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expImportQuery/edit.html b/box-bps/src/main/resources/templates/bps/expImportQuery/edit.html new file mode 100644 index 000000000..47f9f625b --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expImportQuery/edit.html @@ -0,0 +1,70 @@ + + + + + + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expImportQuery/expImportQuery.html b/box-bps/src/main/resources/templates/bps/expImportQuery/expImportQuery.html new file mode 100644 index 000000000..0bfb6fd02 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expImportQuery/expImportQuery.html @@ -0,0 +1,161 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expTopgpLog/add.html b/box-bps/src/main/resources/templates/bps/expTopgpLog/add.html new file mode 100644 index 000000000..341be5c18 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expTopgpLog/add.html @@ -0,0 +1,73 @@ + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expTopgpLog/edit.html b/box-bps/src/main/resources/templates/bps/expTopgpLog/edit.html new file mode 100644 index 000000000..b2dcedc7a --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expTopgpLog/edit.html @@ -0,0 +1,74 @@ + + + + + + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expTopgpLog/expTopgpLog.html b/box-bps/src/main/resources/templates/bps/expTopgpLog/expTopgpLog.html new file mode 100644 index 000000000..a0a37b094 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expTopgpLog/expTopgpLog.html @@ -0,0 +1,137 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expressInfo/add.html b/box-bps/src/main/resources/templates/bps/expressInfo/add.html new file mode 100644 index 000000000..7a2c96f55 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expressInfo/add.html @@ -0,0 +1,97 @@ + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expressInfo/edit.html b/box-bps/src/main/resources/templates/bps/expressInfo/edit.html new file mode 100644 index 000000000..909109769 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expressInfo/edit.html @@ -0,0 +1,98 @@ + + + + + + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expressInfo/expressInfo.html b/box-bps/src/main/resources/templates/bps/expressInfo/expressInfo.html new file mode 100644 index 000000000..58f71f9ec --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expressInfo/expressInfo.html @@ -0,0 +1,209 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + 实时查询 +  重置 +
  • +
+
+
+
+ +
+ + +
+
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expsubspushresp/add.html b/box-bps/src/main/resources/templates/bps/expsubspushresp/add.html new file mode 100644 index 000000000..111562c1d --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expsubspushresp/add.html @@ -0,0 +1,169 @@ + + + + + + +
+
+
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expsubspushresp/detail.html b/box-bps/src/main/resources/templates/bps/expsubspushresp/detail.html new file mode 100644 index 000000000..b8bf28f5a --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expsubspushresp/detail.html @@ -0,0 +1,229 @@ + + + + + + +
+
+ +
+ + +
+ +
+
+ + +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ + +
+
+
+ +
+ +
+
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expsubspushresp/edit.html b/box-bps/src/main/resources/templates/bps/expsubspushresp/edit.html new file mode 100644 index 000000000..b4a69065b --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expsubspushresp/edit.html @@ -0,0 +1,170 @@ + + + + + + +
+
+ +
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+
+ + +
+ 代码生成请选择字典属性 +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/expsubspushresp/expsubspushresp.html b/box-bps/src/main/resources/templates/bps/expsubspushresp/expsubspushresp.html new file mode 100644 index 000000000..6151f813b --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/expsubspushresp/expsubspushresp.html @@ -0,0 +1,232 @@ + + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/subscribe/add.html b/box-bps/src/main/resources/templates/bps/subscribe/add.html new file mode 100644 index 000000000..bf4238ba8 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/subscribe/add.html @@ -0,0 +1,101 @@ + + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/subscribe/edit.html b/box-bps/src/main/resources/templates/bps/subscribe/edit.html new file mode 100644 index 000000000..035677c9d --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/subscribe/edit.html @@ -0,0 +1,76 @@ + + + + + + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/bps/subscribe/subscribe.html b/box-bps/src/main/resources/templates/bps/subscribe/subscribe.html new file mode 100644 index 000000000..58b59ffc1 --- /dev/null +++ b/box-bps/src/main/resources/templates/bps/subscribe/subscribe.html @@ -0,0 +1,193 @@ + + + + + + + +
+
+
+
+
+
+
    +
  • + 快递公司: + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • + +
  • +  搜索 +  重置 +
  • + +
+
+
+
+ + +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/box-bps/src/main/resources/templates/express/queryExpress.html b/box-bps/src/main/resources/templates/express/queryExpress.html new file mode 100644 index 000000000..2b6bb25ca --- /dev/null +++ b/box-bps/src/main/resources/templates/express/queryExpress.html @@ -0,0 +1,98 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/box-test/pom.xml b/box-test/pom.xml new file mode 100644 index 000000000..e14e2e697 --- /dev/null +++ b/box-test/pom.xml @@ -0,0 +1,45 @@ + + + + ruoyi + com.ruoyi + 4.6.2 + + 4.0.0 + + box-test + + + test系统模块 + + + + + + + com.ruoyi + ruoyi-common + + + + + org.apache.cxf + cxf-spring-boot-starter-jaxws + 3.2.6 + + + org.json + json + 20160810 + compile + + + com.ruoyi + ruoyi-system + + + + + \ No newline at end of file diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/ApiController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/ApiController.java new file mode 100644 index 000000000..e3a4c9f77 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/ApiController.java @@ -0,0 +1,61 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.common.core.domain.AjaxResult; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.apache.shiro.authz.annotation.RequiresRoles; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** + * + * 测试权限登录访问请求 + * + * 登录访问(返回token) POST / http://localhost:80/jwt/login?username=ry&password=admin123 + * + * 测试任意权限(header携带token) GET / http://localhost:80/api/list + * + * 测试菜单权限(header携带token) GET / http://localhost:80/api/user/list + * + * 测试角色权限(header携带token) GET / http://localhost:80/api/role/list + * + */ + + + +@RestController +@RequestMapping("/api") +public class ApiController +{ + /** + * 无权限访问 + * + * @return + */ + @GetMapping("/list") + public AjaxResult list() + { + return AjaxResult.success("list success"); + } + + /** + * 菜单权限 system:user:list + */ + @GetMapping("/user/list") + @RequiresPermissions("system:user:list") + public AjaxResult userlist() + { + return AjaxResult.success("user list success"); + } + + /** + * 角色权限 admin + */ + @GetMapping("/role/list") + @RequiresRoles("admin") + public AjaxResult rolelist() + { + return AjaxResult.success("role list success"); + } +} \ No newline at end of file diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/BoxTestController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/BoxTestController.java new file mode 100644 index 000000000..f282d9344 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/BoxTestController.java @@ -0,0 +1,18 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.test.service.TestService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +public class BoxTestController { + @Autowired + private TestService testService; + @RequestMapping("test") + public String test(){ + testService.test(); + return testService.test(); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/FrForCrController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/FrForCrController.java new file mode 100644 index 000000000..29915a5e8 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/FrForCrController.java @@ -0,0 +1,45 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.test.service.OracleDdlService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalTime; +import java.util.Map; + +@RestController +public class FrForCrController { + @Autowired + private OracleDdlService oracleDdlService; + + @CrossOrigin + @RequestMapping("/test/frforcr") + public void frforcr(@RequestBody Map map){ + for(Object value:map.values()){ + String dbName="ds7"; + String tableName=value.toString(); + String tableNameWithDb=dbName+"."+tableName; + int isTableInDb=oracleDdlService.isTableInDb(dbName,tableName); + if(isTableInDb>0){ + try{ + oracleDdlService.dropTable(tableNameWithDb); + System.out.println(LocalTime.now()+"【"+tableNameWithDb+"】已被删除!"); + }catch (Exception e){ + System.out.println(LocalTime.now()+"【"+tableNameWithDb+"】删除异常!"); + } + } + else{ + System.out.println(LocalTime.now()+"【"+tableNameWithDb+"】不存在!"); + } + } + + } + + + +} + + diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/GetEcologyInfoTestController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/GetEcologyInfoTestController.java new file mode 100644 index 000000000..0d52737dc --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/GetEcologyInfoTestController.java @@ -0,0 +1,152 @@ +package com.ruoyi.test.conrtroller; + + +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.system.service.ISysDeptService; +import com.ruoyi.system.service.ISysUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class GetEcologyInfoTestController extends BaseController { + @Autowired + private ISysDeptService deptService; + @Autowired + private ISysUserService userService; + + @RequestMapping("/anon/getEcologyDept") + public AjaxResult getEcologyDept() throws Exception { + String url="http://192.168.2.85:90/api/hrm/resful/getHrmdepartmentWithPage"; + String params="{\"params\":{\"pagesize\":999999}}"; + //return sendPost(url,params); + return deptService.syncEcologyDept(url,params); + + + } + + @RequestMapping("/anon/getEcologyUser") + public AjaxResult getEcologyUser(){ + String url="http://192.168.2.85:90/api/hrm/resful/getHrmUserInfoWithPage"; + String params="{\"params\":{\"pagesize\":999999}}"; + return userService.syncEcologyUser(url,params); + } + + /* *//*public Map sendPostWithRest(String url,String params){ + RestTemplate restTemplate=new RestTemplate(); + ResponseEntity result=null; + int statusCode=0; + try{ + result=restTemplate.postForEntity(url,params,String.class); + statusCode=result.getStatusCode().value(); + }catch (RestClientException e){ + System.out.println("POST Request uri: "+url+", params:"+params+" error:"+e.getMessage()); + } + Map map=new HashMap<>(); + map.put("statusCode",String.valueOf(statusCode)); + if(statusCode== 200){ + map.put("result",result.getBody()); + } else{ + map.put("result",String.valueOf(statusCode)); + } + + return map; + }*//* + + public SysDept insertEcologyDept(EcologyDept ecologyDept){ + SysDept dept=new SysDept(); + dept.setDeptId(Long.parseLong(ecologyDept.getId())); + dept.setParentId(Long.parseLong(ecologyDept.getSupdepid()) == 0 ? 999999 : Long.parseLong(ecologyDept.getSupdepid())); + dept.setDeptName(ecologyDept.getDepartmentname()); + //dept.setAncestors(pAncestors); + dept.setOrderNum("0"); + dept.setStatus("0"); + dept.setCreateBy("Admin"); + deptMapper.insertDept(dept); + return dept; + } + + public void updateAncestors(List sysDeptList) + { + if(sysDeptList.isEmpty()) + { + return; + } + List list =new ArrayList<>(); + for(SysDept dept:sysDeptList){ + SysDept info = deptMapper.selectDeptById(dept.getParentId()); + if(StringUtils.isNotEmpty(info.getAncestors())) { + dept.setAncestors(info.getAncestors()+","+dept.getParentId()); + deptMapper.updateDept(dept); + }else{ + list.add(dept); + } + } + updateAncestors(list); + } + + + + public String deptSync(Map mapResult){ + //如果接口返回状态码不为200,则不做同步处理 + String statusCode=mapResult.get("statusCode"); + String result= mapResult.get("result"); + if(!statusCode.equals("200")) + { + return mapResult.get("result"); + } + //取Ecology返回信息中的部门信息 + Map map = (Map) JSON.parse(result); + Map o= (Map) map.get("data"); + JSONArray json = (JSONArray) o.get("dataList"); + List depts = JSONArray.parseArray(json.toJSONString(), EcologyDept.class); + + //清空部门表,并插入顶级部门 + SysDept sysDept=deptMapper.selectDeptById(Long.parseLong("999999")); + deptMapper.truncateDept(); + deptMapper.insertDept(sysDept); + List list=new ArrayList<>(); + //同步Ecology部门信息 + for(EcologyDept ecologyDept:depts){ + if(ecologyDept.getSubcompanyid1().equals("1")) { //只取分部ID为“1”的部门,排除代理商 + *//* String pAncestors=null; + if(ecologyDept.getSupdepid().equals("0")){ //如果Ecology部门为一级部门,则设定ancestors="0,999999" + pAncestors="0,999999"; + }*//* + SysDept dept= insertEcologyDept(ecologyDept); + list.add(dept); + } + } + //更新祖级信息 + updateAncestors(list); + + return result; + } + + *//* public String sendPost(String url,String params) throws Exception { + RestTemplate restTemplate=new RestTemplate(); + ResponseEntity result = null; + int statusCode = 0; + try { + result = restTemplate.postForEntity(url, params, String.class); + statusCode = result.getStatusCode().value(); + } catch (RestClientException e) { + //logger.error("POST Request uri: {}, params: {}, error: {}", url, params, e.getMessage()); + } + if (statusCode == 200) { + + return result.getBody(); + } + if (statusCode >= 300 || statusCode < 200) { + throw new Exception("Response code is " + statusCode); + } + //return ResponseCreator.writeJsonErr(result.getStatusCode().toString()); + + return result.getStatusCode().toString(); + } +*//* + +*/ + +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/GetJsonReqController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/GetJsonReqController.java new file mode 100644 index 000000000..e5ad72c9e --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/GetJsonReqController.java @@ -0,0 +1,67 @@ +package com.ruoyi.test.conrtroller; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.test.domain.Beauty; +import org.springframework.web.bind.annotation.CrossOrigin; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.text.SimpleDateFormat; +import java.util.HashMap; +import java.util.Map; + +@RestController +@RequestMapping("/test") +public class GetJsonReqController { + + @CrossOrigin + @RequestMapping(value = "simple") + // json的结构和内部字段名称可以与POJO/DTO/javabean完全对应 + public Map getJsonBean(@RequestBody Beauty beauty) { + Map result = null; + + if (beauty != null) { + System.out.println("姓名:" + beauty.getName()); + System.out.println("年龄:" + beauty.getAge()); + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + System.out.println("出生日期:" + sdf.format(beauty.getDate())); + + System.out.println("年收入:" + beauty.getSalary()); + result = new HashMap<>(); + result.put("code", "1"); + result.put("msg", "ok"); + } + + return result; + } + + @CrossOrigin + @RequestMapping(value = "complex") + //json的结构较为复杂,不直接与POJO/DTO/javabean对应。 + public Map getJsonComplex(@RequestBody JSONObject param) { + Map result = null; + + if (param != null) { + JSONObject master = param.getJSONObject("master"); + Beauty beauty = (Beauty) JSONObject.toJavaObject(master, Beauty.class); + System.out.println(beauty); + + JSONArray mm = param.getJSONArray("MM"); + for (int i = 0; i < mm.size(); i++) { + // 这里不能使用get(i),因为get(i)只会得到键值对。 + JSONObject json = mm.getJSONObject(i); + Beauty bt = (Beauty) JSONObject.toJavaObject(json, Beauty.class); + System.out.println(bt); + } + + result = new HashMap<>(); + result.put("code", "1"); + result.put("msg", "ok"); + } + + return result; + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/OracleDdlController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/OracleDdlController.java new file mode 100644 index 000000000..5f0b67f56 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/OracleDdlController.java @@ -0,0 +1,55 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.test.service.OracleDdlService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/test") +@DataSource(value = DataSourceType.SLAVE) +public class OracleDdlController { + @Autowired + private OracleDdlService oracleDdlService; + + //查询表录数 + @RequestMapping("getRecordCount") + + public String getRecordCount() { + String tableName = "tc_user"; + int result = oracleDdlService.getRecordCount(tableName); + return result + ""; + } + + //检查表是否存在 + @RequestMapping("isTableInDb") + public String isTableInDb() { + String dbName = "ds7"; + String tableName = "tc_user"; + return (oracleDdlService.isTableInDb(dbName, tableName)>0?"实例ds7中存在表tc_user":"实例ds7中不存在表tc_user"); + } + + //复制表 + @RequestMapping("copyTable") + public String copyTable() { + String originalTalble = "tc_user"; + String newTable = "new_tc_user"; + + if (oracleDdlService.isTableInDb("ds7", originalTalble) > 0) { + try { + oracleDdlService.copyTable(newTable, originalTalble); + return "复制" + originalTalble + "成功!新表名:" + newTable; + } catch (Exception e) { + System.out.println(e); + return "复制" + originalTalble + "失败!"; + } + } else { + return "表"+originalTalble+"不存在"; + + } + + } + +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/SendJsonController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/SendJsonController.java new file mode 100644 index 000000000..689d2ce84 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/SendJsonController.java @@ -0,0 +1,15 @@ +package com.ruoyi.test.conrtroller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; + + +@Controller +public class SendJsonController { + @RequestMapping("/test/sendjson") + public String ajaxSend(){ + return ("test/sendJson"); + + } + +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/SoapTestController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/SoapTestController.java new file mode 100644 index 000000000..9f5872a3e --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/SoapTestController.java @@ -0,0 +1,171 @@ +package com.ruoyi.test.conrtroller; + +import org.apache.cxf.endpoint.Client; +import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory; +import org.json.XML; +import org.springframework.web.bind.annotation.*; +import org.w3c.dom.Document; + +import javax.xml.namespace.QName; +import javax.xml.soap.MessageFactory; +import javax.xml.soap.SOAPBody; +import javax.xml.soap.SOAPBodyElement; +import javax.xml.soap.SOAPMessage; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.Dispatch; +import javax.xml.ws.Service; +import javax.xml.ws.WebServiceException; +import javax.xml.ws.soap.SOAPBinding; +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; + + +@RestController +@RequestMapping("/anon/soap") +public class SoapTestController { + + + /** + * https://www.jianshu.com/p/cdbcdd724813 + * 方法一:用cxf框架 + * @param url + * @param method + * @param args + * @return + */ + @CrossOrigin + @RequestMapping("/hello") + @ResponseBody + public Map hello(String url, String method, String[] args) { + if (args.length < 1) { + args = new String[]{""}; + } + + // 创建动态客户端 + JaxWsDynamicClientFactory dcf = JaxWsDynamicClientFactory.newInstance(); + Client client = dcf.createClient(url); + // 需要密码的情况需要加上用户名和密码 + // client.getOutInterceptors().add(new ClientLoginInterceptor(USER_NAME,PASS_WORD)); + // 命名空间,方法名 + QName name = new QName("http://WebXml.com.cn/", method); + HashMap map = new HashMap<>(); + try { + // invoke("方法名",参数1,参数2,参数3....); + Object[] objects = client.invoke(name, args); + map.put("result", objects); + return map; + } catch (java.lang.Exception e) { + e.printStackTrace(); + map.put("result", "接口调用异常"); + return map; + } + } + + /** + * 方法二:自己用jax-ws的方式实现 + * @param url 请求地址 + * @param targetNamespace 名称空间 + * @param pName 端口名 + * @param method 方法名 + * @param argsName 参数名 + * @param args 参数 + * @return + * @throws Exception + */ + @RequestMapping("/getSoap") + @ResponseBody + public Map getSoap(String url, String targetNamespace, String pName, String method, String[] argsName, String[] args) throws Exception { + QName serviceName = new QName(targetNamespace, method); + + //WSDL中定义的端口的QName + QName portName = new QName(targetNamespace, pName); + + //创建动态Service实例 + Service service = Service.create(serviceName); + service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, url); + + //创建一个dispatch实例 + Dispatch dispatch = service.createDispatch(portName, SOAPMessage.class, Service.Mode.MESSAGE); + + // Use Dispatch as BindingProvider + BindingProvider bp = (BindingProvider) dispatch; + + // 配置RequestContext以发送SOAPAction HTTP标头 + Map rc = dispatch.getRequestContext(); + rc.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE); + rc.put(BindingProvider.SOAPACTION_URI_PROPERTY, targetNamespace + method); + + // 获取预配置的SAAJ MessageFactory + MessageFactory factory = ((SOAPBinding) bp.getBinding()).getMessageFactory(); + + // 创建SOAPMessage请求 + SOAPMessage request = null; + request = factory.createMessage(); + // 请求体 + SOAPBody body = request.getSOAPBody(); + + // Compose the soap:Body payload + QName payloadName = new QName(targetNamespace, method); + + SOAPBodyElement payload = body.addBodyElement(payloadName); + if (args.length > 0) { + for (int i = 0; i < argsName.length; i++) { + payload.addChildElement(argsName[i]).setValue(args[i]); + } + } +// payload.addChildElement("startCity").setValue("北京"); +// payload.addChildElement("lastCity").setValue("上海"); +// payload.addChildElement("theDate").setValue("2019-06-07"); +// payload.addChildElement("userID").setValue(""); + +// SOAPElement message = payload.addChildElement(INPUT_NMAE); +// message.addTextNode("88888"); + + SOAPMessage reply = null; + + try { + //调用端点操作并读取响应 + //request.writeTo(System.out); + reply = dispatch.invoke(request); + //reply.writeTo(System.out); + } catch (WebServiceException wse) { + wse.printStackTrace(); + } + + // 处理响应结果 + Document doc = reply.getSOAPPart().getEnvelope().getBody().extractContentAsDocument(); + HashMap map = new HashMap<>(); + map.put("result", XML.toJSONObject(format(doc)).toString()); + //System.out.println(XML.toJSONObject(format(doc)).toString()); + return map; + + } + + // Document对象转字符串 + public static String format(Document doc) throws Exception { + // XML转字符串 + String xmlStr = ""; + try { + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer t = tf.newTransformer(); + t.setOutputProperty("encoding", "UTF-8"); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + t.transform(new DOMSource(doc), new StreamResult(bos)); + xmlStr = bos.toString(); + } catch (TransformerConfigurationException e) { + e.printStackTrace(); + } catch (TransformerException e) { + e.printStackTrace(); + } + return xmlStr; + } + + +} \ No newline at end of file diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/SysCustomerController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/SysCustomerController.java new file mode 100644 index 000000000..9e4b8e568 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/SysCustomerController.java @@ -0,0 +1,123 @@ +package com.ruoyi.test.conrtroller; + +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.test.domain.SysCustomer; +import com.ruoyi.test.service.ISysCustomerService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 客户Controller + * + * @author box + * @date 2021-02-13 + */ +@Controller +@RequestMapping("/test/customer") +public class SysCustomerController extends BaseController +{ + private String prefix = "test/customer"; + + @Autowired + private ISysCustomerService sysCustomerService; + + @RequiresPermissions("test:customer:view") + @GetMapping() + public String customer() + { + return prefix + "/customer"; + } + + /** + * 查询客户列表 + */ + @RequiresPermissions("test:customer:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysCustomer sysCustomer) + { + startPage(); + List list = sysCustomerService.selectSysCustomerList(sysCustomer); + return getDataTable(list); + } + + /** + * 导出客户列表 + */ + @RequiresPermissions("test:customer:export") + @Log(title = "客户", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysCustomer sysCustomer) + { + List list = sysCustomerService.selectSysCustomerList(sysCustomer); + ExcelUtil util = new ExcelUtil(SysCustomer.class); + return util.exportExcel(list, "customer"); + } + + /** + * 新增客户 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存客户 + */ + @RequiresPermissions("test:customer:add") + @Log(title = "客户", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(SysCustomer sysCustomer) + { + return toAjax(sysCustomerService.insertSysCustomer(sysCustomer)); + } + + /** + * 修改客户 + */ + @GetMapping("/edit/{customerId}") + public String edit(@PathVariable("customerId") Long customerId, ModelMap mmap) + { + SysCustomer sysCustomer = sysCustomerService.selectSysCustomerById(customerId); + mmap.put("sysCustomer", sysCustomer); + return prefix + "/edit"; + } + + /** + * 修改保存客户 + */ + @RequiresPermissions("test:customer:edit") + @Log(title = "客户", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(SysCustomer sysCustomer) + { + return toAjax(sysCustomerService.updateSysCustomer(sysCustomer)); + } + + /** + * 删除客户 + */ + @RequiresPermissions("test:customer:remove") + @Log(title = "客户", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(sysCustomerService.deleteSysCustomerByIds(ids)); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/SysFileInfoController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/SysFileInfoController.java new file mode 100644 index 000000000..e7a610ef5 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/SysFileInfoController.java @@ -0,0 +1,123 @@ +package com.ruoyi.test.conrtroller; + +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.test.domain.SysFileInfo; +import com.ruoyi.test.service.ISysFileInfoService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 文件信息Controller + * + * @author box + * @date 2021-05-06 + */ +@Controller +@RequestMapping("/test/fileinfo") +public class SysFileInfoController extends BaseController +{ + private String prefix = "test/fileinfo"; + + @Autowired + private ISysFileInfoService sysFileInfoService; + + @RequiresPermissions("test:fileinfo:view") + @GetMapping() + public String fileinfo() + { + return prefix + "/fileinfo"; + } + + /** + * 查询文件信息列表 + */ + @RequiresPermissions("test:fileinfo:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysFileInfo sysFileInfo) + { + startPage(); + List list = sysFileInfoService.selectSysFileInfoList(sysFileInfo); + return getDataTable(list); + } + + /** + * 导出文件信息列表 + */ + @RequiresPermissions("test:fileinfo:export") + @Log(title = "文件信息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysFileInfo sysFileInfo) + { + List list = sysFileInfoService.selectSysFileInfoList(sysFileInfo); + ExcelUtil util = new ExcelUtil(SysFileInfo.class); + return util.exportExcel(list, "文件信息数据"); + } + + /** + * 新增文件信息 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存文件信息 + */ + @RequiresPermissions("test:fileinfo:add") + @Log(title = "文件信息", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(SysFileInfo sysFileInfo) + { + return toAjax(sysFileInfoService.insertSysFileInfo(sysFileInfo)); + } + + /** + * 修改文件信息 + */ + @GetMapping("/edit/{fileId}") + public String edit(@PathVariable("fileId") Long fileId, ModelMap mmap) + { + SysFileInfo sysFileInfo = sysFileInfoService.selectSysFileInfoById(fileId); + mmap.put("sysFileInfo", sysFileInfo); + return prefix + "/edit"; + } + + /** + * 修改保存文件信息 + */ + @RequiresPermissions("test:fileinfo:edit") + @Log(title = "文件信息", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(SysFileInfo sysFileInfo) + { + return toAjax(sysFileInfoService.updateSysFileInfo(sysFileInfo)); + } + + /** + * 删除文件信息 + */ + @RequiresPermissions("test:fileinfo:remove") + @Log(title = "文件信息", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(sysFileInfoService.deleteSysFileInfoByIds(ids)); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/TcUserController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/TcUserController.java new file mode 100644 index 000000000..55953e6fc --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/TcUserController.java @@ -0,0 +1,64 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.test.domain.TcUser; +import com.ruoyi.test.service.TcUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/test") +public class TcUserController { + @Autowired + private TcUserService tcUserService; + + //查询所有 + // http://localhost/test/selectall + @RequestMapping("selectAll") + @ResponseBody + public String selectAll(){ + List users=tcUserService.selectAll(); + users.forEach(System.out::println); + return users.toString()+""; + } + + + //查询 by id + // http://localhost/test/selectById/1(此处1为要获取的id) + @RequestMapping(value = "selectById/{id}", method = RequestMethod.GET) + public String selectById(@PathVariable int id) { + return tcUserService.selectById(id).toString(); + } + + //插入新用户 + // http://localhost/test/insert?id=100&name=张三&password=20 + @RequestMapping(value = "/insert", method = RequestMethod.GET) + public TcUser insert(TcUser tcUser) { + return tcUserService.insert(tcUser); + } + + //通过用户id删除用户 + // http://localhost/test/deleteById?id=1(此处1为要删除的id) + @RequestMapping(value = "/deleteById", method = RequestMethod.GET) + public String delete(int id) { + int result = tcUserService.deleteById(id); + if (result >= 1) { + return "删除成功"; + } else { + return "删除失败"; + } + } + + //更新 by id + // http://localhost/test/updateById?id=2&name=波波&password=12 + @RequestMapping(value = "/updateById", method = RequestMethod.GET) + public String update(TcUser tcUser) { + int result = tcUserService.updateById(tcUser); + if (result >= 1) { + return "修改成功"; + } else { + return "修改失败"; + } + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/TestIndexController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/TestIndexController.java new file mode 100644 index 000000000..4900631cc --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/TestIndexController.java @@ -0,0 +1,16 @@ +package com.ruoyi.test.conrtroller; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.time.LocalDateTime; + +@Controller +public class TestIndexController { + @RequestMapping("/test") + public String index(ModelMap modelMap){ + modelMap.put("date", LocalDateTime.now()); + return ("test/index"); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/TestVerifyController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/TestVerifyController.java new file mode 100644 index 000000000..f2d3c63f1 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/TestVerifyController.java @@ -0,0 +1,34 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.test.service.TestVerifyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +public class TestVerifyController { + @Autowired + private TestVerifyService testVerifyService; + + @RequestMapping("/test/testVerify") + public String testVerify(){ + return "/test/testVerify"; + } + + + + + /** + * 校验用户名 + */ + + @PostMapping("/test/testVerifyName") + @ResponseBody + public int testVerifyName(String name) + { + return testVerifyService.isNameUnique(name); + } + +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/WechatApiController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/WechatApiController.java new file mode 100644 index 000000000..ef71ebbee --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/WechatApiController.java @@ -0,0 +1,85 @@ +package com.ruoyi.test.conrtroller; + +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.system.service.IWechatApiService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Controller +public class WechatApiController extends BaseController { + @Autowired + IWechatApiService wechatApiService; + + @RequestMapping("anon/getAccessToken") + @ResponseBody + public String getAccessToken() { + return wechatApiService.GetAccessToken(); + } + + @GetMapping("anon/userInfo") + @ResponseBody + public Object getJSON(HttpServletRequest request, HttpServletResponse response) throws IOException { + + BufferedReader streamReader = new BufferedReader( new InputStreamReader(request.getInputStream(), "UTF-8")); + StringBuilder responseStrBuilder = new StringBuilder(); + String inputStr; + while ((inputStr = streamReader.readLine()) != null) { + responseStrBuilder.append(inputStr); + } + //return JSON.parseObject(responseStrBuilder.toString(), Map.class); + return JSON.parse(responseStrBuilder.toString()); + + } + + + @GetMapping("anon/SendTextMessageToWechatUser") + @ResponseBody + public Map SendTextMessageToWechatUser() { + List userIdList = new ArrayList<>(); + userIdList.add("2342343243");//错误userId示例 + userIdList.add("erqrqwe");//错误userId示例 + userIdList.add(""); //空UserId示例 + userIdList.add("359"); + if(! String.valueOf(ShiroUtils.getUserId()).equals("359")){ + userIdList.add(String.valueOf(ShiroUtils.getUserId())); + } + Map resultMap = wechatApiService.SendTextMessageToWechatUser(userIdList,"哈哈哈!"); + return resultMap; + } + + @GetMapping("anon/SendTextCardMessageToWechatUser") + @ResponseBody + public Map SendTextCardMessageToWechatUser() { + List userIdList = new ArrayList<>(); + userIdList.add("23456667"); //错误userId示例 + userIdList.add("355354354"); //错误userId示例 + userIdList.add(""); //空UserId示例 + userIdList.add("359"); + //userIdList.add("454"); + //userIdList.add("408"); + if(!String.valueOf(ShiroUtils.getUserId()).equals("359")){ + userIdList.add(String.valueOf(ShiroUtils.getUserId())); + } + String title="号外:特大优惠!限时抢购"; + String description="今年仅此一次,苹果手机1000元起!欢迎前来购买!走过路过,不要错过!"; + String dtailUrl="https://item.jd.com/100008348530.html"; + + Map resultMap = wechatApiService.SendTextCardMessageToWechatUser(userIdList,title,description,dtailUrl); + return resultMap; + } + +} diff --git a/box-test/src/main/java/com/ruoyi/test/conrtroller/XmlWebserviceController.java b/box-test/src/main/java/com/ruoyi/test/conrtroller/XmlWebserviceController.java new file mode 100644 index 000000000..d8bf9f162 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/conrtroller/XmlWebserviceController.java @@ -0,0 +1,26 @@ +package com.ruoyi.test.conrtroller; + +import com.ruoyi.common.utils.TopgpXmlUtils; +import com.ruoyi.common.utils.http.HttpUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Map; + +@RestController + +public class XmlWebserviceController { + //private static final Logger log = LoggerFactory.getLogger(HttpUtils.class); + + @PostMapping("/anon/sendXml") + public String SendXml() { + Map map = new HashMap<>(); + map.put("responseInfo", "此处为测试消息"); + String param = TopgpXmlUtils.GetTopgpRequestXml("express_testRequest", map); + String url = "http://192.168.2.81:85/web/ws/r/aws_ttsrv2_toptest"; + String returnXml = HttpUtils.sendXmlPost(url,param); + return TopgpXmlUtils.TopgpResponseXmlToJson(returnXml).toString(); + + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/domain/Beauty.java b/box-test/src/main/java/com/ruoyi/test/domain/Beauty.java new file mode 100644 index 000000000..41c2ec760 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/domain/Beauty.java @@ -0,0 +1,55 @@ +package com.ruoyi.test.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.util.Date; + +public class Beauty { + private String name; + private int age; + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date date; + private double salary; + + public Beauty() { + + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public double getSalary() { + return salary; + } + + public void setSalary(double salary) { + this.salary = salary; + } + + @Override + public String toString() { + return "Beauty [name=" + name + ", age=" + age + ", date=" + date + ", salary=" + salary + "]"; + } + +} diff --git a/box-test/src/main/java/com/ruoyi/test/domain/SysCustomer.java b/box-test/src/main/java/com/ruoyi/test/domain/SysCustomer.java new file mode 100644 index 000000000..0f34bbe05 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/domain/SysCustomer.java @@ -0,0 +1,113 @@ +package com.ruoyi.test.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.util.Date; +import java.util.List; + +/** + * 客户对象 sys_customer + * + * @author box + * @date 2021-02-13 + */ +public class SysCustomer extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 客户id */ + private Long customerId; + + /** 客户姓名 */ + @Excel(name = "客户姓名") + private String customerName; + + /** 手机号码 */ + @Excel(name = "手机号码") + private String phonenumber; + + /** 客户性别 */ + @Excel(name = "客户性别") + private String sex; + + /** 客户生日 */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "客户生日", width = 30, dateFormat = "yyyy-MM-dd") + private Date birthday; + + /** 商品信息 */ + private List sysGoodsList; + + public void setCustomerId(Long customerId) + { + this.customerId = customerId; + } + + public Long getCustomerId() + { + return customerId; + } + public void setCustomerName(String customerName) + { + this.customerName = customerName; + } + + public String getCustomerName() + { + return customerName; + } + public void setPhonenumber(String phonenumber) + { + this.phonenumber = phonenumber; + } + + public String getPhonenumber() + { + return phonenumber; + } + public void setSex(String sex) + { + this.sex = sex; + } + + public String getSex() + { + return sex; + } + public void setBirthday(Date birthday) + { + this.birthday = birthday; + } + + public Date getBirthday() + { + return birthday; + } + + public List getSysGoodsList() + { + return sysGoodsList; + } + + public void setSysGoodsList(List sysGoodsList) + { + this.sysGoodsList = sysGoodsList; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("customerId", getCustomerId()) + .append("customerName", getCustomerName()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("birthday", getBirthday()) + .append("remark", getRemark()) + .append("sysGoodsList", getSysGoodsList()) + .toString(); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/domain/SysFileInfo.java b/box-test/src/main/java/com/ruoyi/test/domain/SysFileInfo.java new file mode 100644 index 000000000..003c5f57a --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/domain/SysFileInfo.java @@ -0,0 +1,65 @@ +package com.ruoyi.test.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 文件信息对象 sys_file_info + * + * @author box + * @date 2021-05-06 + */ +public class SysFileInfo extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 文件id */ + private Long fileId; + + /** 文件名称 */ + @Excel(name = "文件名称") + private String fileName; + + /** 文件路径 */ + @Excel(name = "文件路径") + private String filePath; + + public void setFileId(Long fileId) + { + this.fileId = fileId; + } + + public Long getFileId() + { + return fileId; + } + public void setFileName(String fileName) + { + this.fileName = fileName; + } + + public String getFileName() + { + return fileName; + } + public void setFilePath(String filePath) + { + this.filePath = filePath; + } + + public String getFilePath() + { + return filePath; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("fileId", getFileId()) + .append("fileName", getFileName()) + .append("filePath", getFilePath()) + .toString(); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/domain/SysGoods.java b/box-test/src/main/java/com/ruoyi/test/domain/SysGoods.java new file mode 100644 index 000000000..85740959a --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/domain/SysGoods.java @@ -0,0 +1,124 @@ +package com.ruoyi.test.domain; + +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.core.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 商品对象 sys_goods + * + * @author box + * @date 2021-02-13 + */ +public class SysGoods extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 商品id */ + private Long goodsId; + + /** 客户id */ + @Excel(name = "客户id") + private Long customerId; + + /** 商品名称 */ + @Excel(name = "商品名称") + private String name; + + /** 商品重量 */ + @Excel(name = "商品重量") + private Integer weight; + + /** 商品价格 */ + @Excel(name = "商品价格") + private BigDecimal price; + + /** 商品时间 */ + @Excel(name = "商品时间", width = 30, dateFormat = "yyyy-MM-dd") + private Date date; + + /** 商品种类 */ + @Excel(name = "商品种类") + private String type; + + public void setGoodsId(Long goodsId) + { + this.goodsId = goodsId; + } + + public Long getGoodsId() + { + return goodsId; + } + public void setCustomerId(Long customerId) + { + this.customerId = customerId; + } + + public Long getCustomerId() + { + return customerId; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setWeight(Integer weight) + { + this.weight = weight; + } + + public Integer getWeight() + { + return weight; + } + public void setPrice(BigDecimal price) + { + this.price = price; + } + + public BigDecimal getPrice() + { + return price; + } + public void setDate(Date date) + { + this.date = date; + } + + public Date getDate() + { + return date; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("goodsId", getGoodsId()) + .append("customerId", getCustomerId()) + .append("name", getName()) + .append("weight", getWeight()) + .append("price", getPrice()) + .append("date", getDate()) + .append("type", getType()) + .toString(); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/domain/TcUser.java b/box-test/src/main/java/com/ruoyi/test/domain/TcUser.java new file mode 100644 index 000000000..da8a2d3cc --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/domain/TcUser.java @@ -0,0 +1,40 @@ +package com.ruoyi.test.domain; + +public class TcUser { + private int id; + private String name; + private String password; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public String toString() { + return "TcUser{" + + "id=" + id + + ", name='" + name + '\'' + + ", age='" + password + '\'' + + '}'; + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/mapper/OracleDdlMapper.java b/box-test/src/main/java/com/ruoyi/test/mapper/OracleDdlMapper.java new file mode 100644 index 000000000..e7c2f3a16 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/mapper/OracleDdlMapper.java @@ -0,0 +1,32 @@ +package com.ruoyi.test.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Component; + +@Mapper +@Component("OracleDdlMapper") +public interface OracleDdlMapper { + // alter table + int alterTableName(@Param("originalTableName") String originalTableName, + @Param("newTableName") String newTableName); + + //truncate table + int truncateTable(@Param("tableName") String tableName); + + //drop table + int dropTable(@Param("tableName") String tableName); + + //copy table + void copyTable(@Param("newTableName") String newTableName, + @Param("originalTableName") String originalTableName); + + //获取表记录数 + int getRecordCount(@Param("tableName") String tableName); + + + + //查询数据库中表是否存在 + int isTableInDb(@Param("dataBaseName") String dataBaseName, + @Param("tableName") String tableName); +} diff --git a/box-test/src/main/java/com/ruoyi/test/mapper/SysCustomerMapper.java b/box-test/src/main/java/com/ruoyi/test/mapper/SysCustomerMapper.java new file mode 100644 index 000000000..3ae177354 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/mapper/SysCustomerMapper.java @@ -0,0 +1,88 @@ +package com.ruoyi.test.mapper; + +import com.ruoyi.test.domain.SysCustomer; +import com.ruoyi.test.domain.SysGoods; + +import java.util.List; + +/** + * 客户Mapper接口 + * + * @author box + * @date 2021-02-13 + */ +public interface SysCustomerMapper +{ + /** + * 查询客户 + * + * @param customerId 客户ID + * @return 客户 + */ + public SysCustomer selectSysCustomerById(Long customerId); + + /** + * 查询客户列表 + * + * @param sysCustomer 客户 + * @return 客户集合 + */ + public List selectSysCustomerList(SysCustomer sysCustomer); + + /** + * 新增客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + public int insertSysCustomer(SysCustomer sysCustomer); + + /** + * 修改客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + public int updateSysCustomer(SysCustomer sysCustomer); + + /** + * 删除客户 + * + * @param customerId 客户ID + * @return 结果 + */ + public int deleteSysCustomerById(Long customerId); + + /** + * 批量删除客户 + * + * @param customerIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysCustomerByIds(String[] customerIds); + + /** + * 批量删除商品 + * + * @param customerIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysGoodsByCustomerIds(String[] customerIds); + + /** + * 批量新增商品 + * + * @param sysGoodsList 商品列表 + * @return 结果 + */ + public int batchSysGoods(List sysGoodsList); + + + /** + * 通过客户ID删除商品信息 + * + * @param roleId 角色ID + * @return 结果 + */ + public int deleteSysGoodsByCustomerId(Long customerId); +} diff --git a/box-test/src/main/java/com/ruoyi/test/mapper/SysFileInfoMapper.java b/box-test/src/main/java/com/ruoyi/test/mapper/SysFileInfoMapper.java new file mode 100644 index 000000000..923f598cf --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/mapper/SysFileInfoMapper.java @@ -0,0 +1,62 @@ +package com.ruoyi.test.mapper; + +import com.ruoyi.test.domain.SysFileInfo; + +import java.util.List; + +/** + * 文件信息Mapper接口 + * + * @author box + * @date 2021-05-06 + */ +public interface SysFileInfoMapper +{ + /** + * 查询文件信息 + * + * @param fileId 文件信息ID + * @return 文件信息 + */ + public SysFileInfo selectSysFileInfoById(Long fileId); + + /** + * 查询文件信息列表 + * + * @param sysFileInfo 文件信息 + * @return 文件信息集合 + */ + public List selectSysFileInfoList(SysFileInfo sysFileInfo); + + /** + * 新增文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + public int insertSysFileInfo(SysFileInfo sysFileInfo); + + /** + * 修改文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + public int updateSysFileInfo(SysFileInfo sysFileInfo); + + /** + * 删除文件信息 + * + * @param fileId 文件信息ID + * @return 结果 + */ + public int deleteSysFileInfoById(Long fileId); + + /** + * 批量删除文件信息 + * + * @param fileIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFileInfoByIds(String[] fileIds); +} diff --git a/box-test/src/main/java/com/ruoyi/test/mapper/TcUserMapper.java b/box-test/src/main/java/com/ruoyi/test/mapper/TcUserMapper.java new file mode 100644 index 000000000..39f541866 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/mapper/TcUserMapper.java @@ -0,0 +1,26 @@ +package com.ruoyi.test.mapper; + +import com.ruoyi.test.domain.TcUser; +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Mapper +@Component("TCUserMapper") +public interface TcUserMapper { + //查询所有 + List selectAll(); + + //查询 by id + TcUser selectById(int id); + + //增加 + int insert(TcUser tcUser); + + //删除 by id + int deleteById(int id); + + //更新 by id + int updateById(TcUser tcUser); +} diff --git a/box-test/src/main/java/com/ruoyi/test/mapper/TestVerifyMapper.java b/box-test/src/main/java/com/ruoyi/test/mapper/TestVerifyMapper.java new file mode 100644 index 000000000..36327dd82 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/mapper/TestVerifyMapper.java @@ -0,0 +1,11 @@ +package com.ruoyi.test.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.springframework.stereotype.Component; + +@Mapper +@Component("TestVerifyMapper") +public interface TestVerifyMapper { + //检查姓名是否唯一 + int isNameUnique(String name); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/ISysCustomerService.java b/box-test/src/main/java/com/ruoyi/test/service/ISysCustomerService.java new file mode 100644 index 000000000..4a3aafa4d --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/ISysCustomerService.java @@ -0,0 +1,62 @@ +package com.ruoyi.test.service; + +import com.ruoyi.test.domain.SysCustomer; + +import java.util.List; + +/** + * 客户Service接口 + * + * @author box + * @date 2021-02-13 + */ +public interface ISysCustomerService +{ + /** + * 查询客户 + * + * @param customerId 客户ID + * @return 客户 + */ + public SysCustomer selectSysCustomerById(Long customerId); + + /** + * 查询客户列表 + * + * @param sysCustomer 客户 + * @return 客户集合 + */ + public List selectSysCustomerList(SysCustomer sysCustomer); + + /** + * 新增客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + public int insertSysCustomer(SysCustomer sysCustomer); + + /** + * 修改客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + public int updateSysCustomer(SysCustomer sysCustomer); + + /** + * 批量删除客户 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysCustomerByIds(String ids); + + /** + * 删除客户信息 + * + * @param customerId 客户ID + * @return 结果 + */ + public int deleteSysCustomerById(Long customerId); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/ISysFileInfoService.java b/box-test/src/main/java/com/ruoyi/test/service/ISysFileInfoService.java new file mode 100644 index 000000000..7df14d3ac --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/ISysFileInfoService.java @@ -0,0 +1,62 @@ +package com.ruoyi.test.service; + +import com.ruoyi.test.domain.SysFileInfo; + +import java.util.List; + +/** + * 文件信息Service接口 + * + * @author box + * @date 2021-05-06 + */ +public interface ISysFileInfoService +{ + /** + * 查询文件信息 + * + * @param fileId 文件信息ID + * @return 文件信息 + */ + public SysFileInfo selectSysFileInfoById(Long fileId); + + /** + * 查询文件信息列表 + * + * @param sysFileInfo 文件信息 + * @return 文件信息集合 + */ + public List selectSysFileInfoList(SysFileInfo sysFileInfo); + + /** + * 新增文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + public int insertSysFileInfo(SysFileInfo sysFileInfo); + + /** + * 修改文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + public int updateSysFileInfo(SysFileInfo sysFileInfo); + + /** + * 批量删除文件信息 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFileInfoByIds(String ids); + + /** + * 删除文件信息信息 + * + * @param fileId 文件信息ID + * @return 结果 + */ + public int deleteSysFileInfoById(Long fileId); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/OracleDdlService.java b/box-test/src/main/java/com/ruoyi/test/service/OracleDdlService.java new file mode 100644 index 000000000..e6b5b6200 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/OracleDdlService.java @@ -0,0 +1,22 @@ +package com.ruoyi.test.service; + +public interface OracleDdlService { + //修改表名 + int alterTableName(String originalTableName, String newTableName); + + // truncate指定数据库表的数据 + int truncateTable(String tableName); + + //drop 指定定数据库表 + int dropTable(String tableName); + + + //根据传入的表明,创建新的表并且将原表的数据插入到新的Occur表中 + void copyTable(String newTableName,String originalTableName); + + //统计某张表中的总数据条数 + int getRecordCount(String tableName); + + //从指定数据库中,查询是否存在某张表 + int isTableInDb(String dataBaseName, String tableName); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/TcUserService.java b/box-test/src/main/java/com/ruoyi/test/service/TcUserService.java new file mode 100644 index 000000000..2f32e20cd --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/TcUserService.java @@ -0,0 +1,22 @@ +package com.ruoyi.test.service; + +import com.ruoyi.test.domain.TcUser; + +import java.util.List; + +public interface TcUserService { + //查询所有用户 + List selectAll(); + + //根据id查询用户信息 + TcUser selectById(int id); + + //新增用户 + TcUser insert (TcUser tcUser); + + // 根据id删除 + int deleteById (int id); + + //更新用户信息 + int updateById(TcUser tcUser); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/TestService.java b/box-test/src/main/java/com/ruoyi/test/service/TestService.java new file mode 100644 index 000000000..8b6b40b97 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/TestService.java @@ -0,0 +1,10 @@ +package com.ruoyi.test.service; + +import org.springframework.stereotype.Service; + +@Service +public class TestService { + public String test(){ + return "hello,box-test.test"; + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/TestVerifyService.java b/box-test/src/main/java/com/ruoyi/test/service/TestVerifyService.java new file mode 100644 index 000000000..6f6e9b0b4 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/TestVerifyService.java @@ -0,0 +1,9 @@ +package com.ruoyi.test.service; + +import org.springframework.stereotype.Service; + +@Service +public interface TestVerifyService { + //检查姓名是否唯一 + int isNameUnique(String name); +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/impl/OracleDdlServiceImpl.java b/box-test/src/main/java/com/ruoyi/test/service/impl/OracleDdlServiceImpl.java new file mode 100644 index 000000000..1ebf0b930 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/impl/OracleDdlServiceImpl.java @@ -0,0 +1,45 @@ +package com.ruoyi.test.service.impl; + +import com.ruoyi.test.mapper.OracleDdlMapper; +import com.ruoyi.test.service.OracleDdlService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +//@DataSource(value = DataSourceType.SLAVE) +public class OracleDdlServiceImpl implements OracleDdlService { + @Autowired + private OracleDdlMapper oracleDdlMapper; + + //修改表名 + public int alterTableName(String originalTableName, String newTableName) + { + return oracleDdlMapper.alterTableName(originalTableName,newTableName); + } + + // truncate指定数据库表的数据 + public int truncateTable(String tableName){ + return oracleDdlMapper.truncateTable(tableName); + } + + //drop 指定定数据库表 + public int dropTable(String tableName){ + return oracleDdlMapper.dropTable(tableName); + } + + + //根据传入的表明,创建新的表并且将原表的数据插入到新的Occur表中 + public void copyTable(String newTableName,String originalTableName){ + return ; + } + + //统计某张表中的总数据条数 + public int getRecordCount(String tableName){ + return oracleDdlMapper.getRecordCount(tableName); + } + + //从指定数据库中,查询是否存在某张表 + public int isTableInDb(String dataBaseName, String tableName){ + return oracleDdlMapper.isTableInDb(dataBaseName,tableName); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/impl/SysCustomerServiceImpl.java b/box-test/src/main/java/com/ruoyi/test/service/impl/SysCustomerServiceImpl.java new file mode 100644 index 000000000..a7d13ef6b --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/impl/SysCustomerServiceImpl.java @@ -0,0 +1,132 @@ +package com.ruoyi.test.service.impl; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.test.domain.SysCustomer; +import com.ruoyi.test.domain.SysGoods; +import com.ruoyi.test.mapper.SysCustomerMapper; +import com.ruoyi.test.service.ISysCustomerService; +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.List; + +/** + * 客户Service业务层处理 + * + * @author box + * @date 2021-02-13 + */ +@Service +public class SysCustomerServiceImpl implements ISysCustomerService +{ + @Autowired + private SysCustomerMapper sysCustomerMapper; + + /** + * 查询客户 + * + * @param customerId 客户ID + * @return 客户 + */ + @Override + public SysCustomer selectSysCustomerById(Long customerId) + { + return sysCustomerMapper.selectSysCustomerById(customerId); + } + + /** + * 查询客户列表 + * + * @param sysCustomer 客户 + * @return 客户 + */ + @Override + public List selectSysCustomerList(SysCustomer sysCustomer) + { + return sysCustomerMapper.selectSysCustomerList(sysCustomer); + } + + /** + * 新增客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + @Transactional + @Override + public int insertSysCustomer(SysCustomer sysCustomer) + { + int rows = sysCustomerMapper.insertSysCustomer(sysCustomer); + insertSysGoods(sysCustomer); + return rows; + } + + /** + * 修改客户 + * + * @param sysCustomer 客户 + * @return 结果 + */ + @Transactional + @Override + public int updateSysCustomer(SysCustomer sysCustomer) + { + sysCustomerMapper.deleteSysGoodsByCustomerId(sysCustomer.getCustomerId()); + insertSysGoods(sysCustomer); + return sysCustomerMapper.updateSysCustomer(sysCustomer); + } + + /** + * 删除客户对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Transactional + @Override + public int deleteSysCustomerByIds(String ids) + { + sysCustomerMapper.deleteSysGoodsByCustomerIds(Convert.toStrArray(ids)); + return sysCustomerMapper.deleteSysCustomerByIds(Convert.toStrArray(ids)); + } + + /** + * 删除客户信息 + * + * @param customerId 客户ID + * @return 结果 + */ + @Override + public int deleteSysCustomerById(Long customerId) + { + sysCustomerMapper.deleteSysGoodsByCustomerId(customerId); + return sysCustomerMapper.deleteSysCustomerById(customerId); + } + + /** + * 新增商品信息 + * + * @param sysCustomer 客户对象 + */ + public void insertSysGoods(SysCustomer sysCustomer) + { + List sysGoodsList = sysCustomer.getSysGoodsList(); + Long customerId = sysCustomer.getCustomerId(); + if (StringUtils.isNotNull(sysGoodsList)) + { + List list = new ArrayList(); + for (SysGoods sysGoods : sysGoodsList) + { + sysGoods.setCustomerId(customerId); + list.add(sysGoods); + } + if (list.size() > 0) + { + sysCustomerMapper.batchSysGoods(list); + } + } + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/impl/SysFileInfoServiceImpl.java b/box-test/src/main/java/com/ruoyi/test/service/impl/SysFileInfoServiceImpl.java new file mode 100644 index 000000000..a7407bbce --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/impl/SysFileInfoServiceImpl.java @@ -0,0 +1,95 @@ +package com.ruoyi.test.service.impl; + +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.test.domain.SysFileInfo; +import com.ruoyi.test.mapper.SysFileInfoMapper; +import com.ruoyi.test.service.ISysFileInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 文件信息Service业务层处理 + * + * @author box + * @date 2021-05-06 + */ +@Service +public class SysFileInfoServiceImpl implements ISysFileInfoService +{ + @Autowired + private SysFileInfoMapper sysFileInfoMapper; + + /** + * 查询文件信息 + * + * @param fileId 文件信息ID + * @return 文件信息 + */ + @Override + public SysFileInfo selectSysFileInfoById(Long fileId) + { + return sysFileInfoMapper.selectSysFileInfoById(fileId); + } + + /** + * 查询文件信息列表 + * + * @param sysFileInfo 文件信息 + * @return 文件信息 + */ + @Override + public List selectSysFileInfoList(SysFileInfo sysFileInfo) + { + return sysFileInfoMapper.selectSysFileInfoList(sysFileInfo); + } + + /** + * 新增文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + @Override + public int insertSysFileInfo(SysFileInfo sysFileInfo) + { + return sysFileInfoMapper.insertSysFileInfo(sysFileInfo); + } + + /** + * 修改文件信息 + * + * @param sysFileInfo 文件信息 + * @return 结果 + */ + @Override + public int updateSysFileInfo(SysFileInfo sysFileInfo) + { + return sysFileInfoMapper.updateSysFileInfo(sysFileInfo); + } + + /** + * 删除文件信息对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteSysFileInfoByIds(String ids) + { + return sysFileInfoMapper.deleteSysFileInfoByIds(Convert.toStrArray(ids)); + } + + /** + * 删除文件信息信息 + * + * @param fileId 文件信息ID + * @return 结果 + */ + @Override + public int deleteSysFileInfoById(Long fileId) + { + return sysFileInfoMapper.deleteSysFileInfoById(fileId); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/impl/TcUserServiceImpl.java b/box-test/src/main/java/com/ruoyi/test/service/impl/TcUserServiceImpl.java new file mode 100644 index 000000000..ca691ce98 --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/impl/TcUserServiceImpl.java @@ -0,0 +1,48 @@ +package com.ruoyi.test.service.impl; + +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.test.domain.TcUser; +import com.ruoyi.test.mapper.TcUserMapper; +import com.ruoyi.test.service.TcUserService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@DataSource(value = DataSourceType.SQLSVR) +public class TcUserServiceImpl implements TcUserService { + @Autowired + private TcUserMapper tcUserMapper; + + //查询所有用户 + @Override + public List selectAll() { + return tcUserMapper.selectAll(); + } + + //查询 by id + @Override + public TcUser selectById(int id) { + return tcUserMapper.selectById(id); + } + + //新增 + @Override + public TcUser insert(TcUser tcUser) { + int user=tcUserMapper.insert(tcUser); + return tcUser; + } + + //删除by id + @Override + public int deleteById(int id) { + return tcUserMapper.deleteById(id); + } + //更新 by id + @Override + public int updateById(TcUser tcUser) { + return tcUserMapper.updateById(tcUser); + } +} diff --git a/box-test/src/main/java/com/ruoyi/test/service/impl/TestVerifyServiceImpl.java b/box-test/src/main/java/com/ruoyi/test/service/impl/TestVerifyServiceImpl.java new file mode 100644 index 000000000..0293b1c6d --- /dev/null +++ b/box-test/src/main/java/com/ruoyi/test/service/impl/TestVerifyServiceImpl.java @@ -0,0 +1,19 @@ +package com.ruoyi.test.service.impl; + +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.enums.DataSourceType; +import com.ruoyi.test.mapper.TestVerifyMapper; +import com.ruoyi.test.service.TestVerifyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@DataSource(value = DataSourceType.SQLSVR) +public class TestVerifyServiceImpl implements TestVerifyService { + @Autowired + private TestVerifyMapper testVerifyMapper; + @Override + public int isNameUnique(String name) { + return testVerifyMapper.isNameUnique(name); + } +} diff --git a/box-test/src/main/resources/mapper/test/OracleDdlMapper.xml b/box-test/src/main/resources/mapper/test/OracleDdlMapper.xml new file mode 100644 index 000000000..08cb35d57 --- /dev/null +++ b/box-test/src/main/resources/mapper/test/OracleDdlMapper.xml @@ -0,0 +1,29 @@ + + + + + + alter table ${originalTableName} rename ${newTableName} + + + + truncate table ${tableName} + + + + drop table ${tableName} + + + + create table ${newTableName} as select * from ${originalTableName} + + + + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/mapper/test/SysCustomerMapper.xml b/box-test/src/main/resources/mapper/test/SysCustomerMapper.xml new file mode 100644 index 000000000..d4e97c0b1 --- /dev/null +++ b/box-test/src/main/resources/mapper/test/SysCustomerMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + select customer_id, customer_name, phonenumber, sex, birthday, remark from sys_customer + + + + + + + + insert into sys_customer + + customer_name, + phonenumber, + sex, + birthday, + remark, + + + #{customerName}, + #{phonenumber}, + #{sex}, + #{birthday}, + #{remark}, + + + + + update sys_customer + + customer_name = #{customerName}, + phonenumber = #{phonenumber}, + sex = #{sex}, + birthday = #{birthday}, + remark = #{remark}, + + where customer_id = #{customerId} + + + + delete from sys_customer where customer_id = #{customerId} + + + + delete from sys_customer where customer_id in + + #{customerId} + + + + + delete from sys_goods where customer_id in + + #{customerId} + + + + + delete from sys_goods where customer_id = #{customerId} + + + + insert into sys_goods( goods_id, customer_id, name, weight, price, date, type) values + + ( #{item.goodsId}, #{item.customerId}, #{item.name}, #{item.weight}, #{item.price}, #{item.date}, #{item.type}) + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/mapper/test/SysFileInfoMapper.xml b/box-test/src/main/resources/mapper/test/SysFileInfoMapper.xml new file mode 100644 index 000000000..1fa4beeac --- /dev/null +++ b/box-test/src/main/resources/mapper/test/SysFileInfoMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + select file_id, file_name, file_path from sys_file_info + + + + + + + + insert into sys_file_info + + file_name, + file_path, + + + #{fileName}, + #{filePath}, + + + + + update sys_file_info + + file_name = #{fileName}, + file_path = #{filePath}, + + where file_id = #{fileId} + + + + delete from sys_file_info where file_id = #{fileId} + + + + delete from sys_file_info where file_id in + + #{fileId} + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/mapper/test/TcUserMapper.xml b/box-test/src/main/resources/mapper/test/TcUserMapper.xml new file mode 100644 index 000000000..ee5f415f7 --- /dev/null +++ b/box-test/src/main/resources/mapper/test/TcUserMapper.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + insert into tc_user + + + id, + + + name, + + + password, + + + + + #{id,jdbcType=INTEGER}, + + + #{name,jdbcType=VARCHAR}, + + + #{password,jdbcType=VARCHAR}, + + + + + + + delete from tc_user where id=#{id} + + + + + update tc_user + + + name = #{name,jdbcType=VARCHAR}, + + + password = #{password,jdbcType=VARCHAR}, + + + where id = #{id,jdbcType=INTEGER} + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/mapper/test/TestVerifyMapper.xml b/box-test/src/main/resources/mapper/test/TestVerifyMapper.xml new file mode 100644 index 000000000..9995edb00 --- /dev/null +++ b/box-test/src/main/resources/mapper/test/TestVerifyMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/customer/add.html b/box-test/src/main/resources/templates/test/customer/add.html new file mode 100644 index 000000000..c22d83716 --- /dev/null +++ b/box-test/src/main/resources/templates/test/customer/add.html @@ -0,0 +1,167 @@ + + + + + + + +
+
+

客户信息

+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 代码生成请选择字典属性 +
+
+
+ +
+
+ + +
+
+
+
+ +
+ +
+
+

商品信息

+
+
+ + +
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/customer/customer.html b/box-test/src/main/resources/templates/test/customer/customer.html new file mode 100644 index 000000000..484e01d18 --- /dev/null +++ b/box-test/src/main/resources/templates/test/customer/customer.html @@ -0,0 +1,117 @@ + + + + + + +
+
+
+
+
+
    +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • +  搜索 +  重置 +
  • +
+
+
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/customer/edit.html b/box-test/src/main/resources/templates/test/customer/edit.html new file mode 100644 index 000000000..41c5e9093 --- /dev/null +++ b/box-test/src/main/resources/templates/test/customer/edit.html @@ -0,0 +1,186 @@ + + + + + + + + +
+
+

客户信息

+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ + 代码生成请选择字典属性 +
+
+
+ +
+
+ + +
+
+
+
+ +
+ +
+
+

商品信息

+
+
+ + +
+
+
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/fileinfo/add.html b/box-test/src/main/resources/templates/test/fileinfo/add.html new file mode 100644 index 000000000..a3a91273a --- /dev/null +++ b/box-test/src/main/resources/templates/test/fileinfo/add.html @@ -0,0 +1,37 @@ + + + + + + +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/fileinfo/edit.html b/box-test/src/main/resources/templates/test/fileinfo/edit.html new file mode 100644 index 000000000..3d7f13c34 --- /dev/null +++ b/box-test/src/main/resources/templates/test/fileinfo/edit.html @@ -0,0 +1,38 @@ + + + + + + +
+
+ +
+ +
+ +
+
+
+ +
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/fileinfo/fileinfo.html b/box-test/src/main/resources/templates/test/fileinfo/fileinfo.html new file mode 100644 index 000000000..a4b81a52c --- /dev/null +++ b/box-test/src/main/resources/templates/test/fileinfo/fileinfo.html @@ -0,0 +1,94 @@ + + + + + + +
+
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/index.html b/box-test/src/main/resources/templates/test/index.html new file mode 100644 index 000000000..8579cdc2d --- /dev/null +++ b/box-test/src/main/resources/templates/test/index.html @@ -0,0 +1,39 @@ + + + + + BPS后台管理系统-测试首页 + + +

BPS后台管理系统-测试首页



+ +一、集成企业微信测试 +
  • 推送文本消息到企业微信
  • +
  • 推送文本卡片消息到企业微信
  • +

    + +二、Mybaits配置DML测试-Oracle(第二数据源) +
  • 查询所有用户
  • +
  • 新增ID为1000的用户
  • +
  • 根据ID查询用户,查询id=1000的用户
  • +
  • 修改用户为1000的用户姓名为Xia
  • +
  • 删除ID为1000的用户
  • + +

    +三、Mybaits配置DDL测试-Oracle +
  • 查询表中的记录数
  • +
  • 查询tc_user是否存在
  • +

    +Ajax发送获取Json配置测试 +

    +四、输入后验证测试 + +

  • 验证数据表中是否有记录
  • +

    + + +三、当前系统时间 + + + + \ No newline at end of file diff --git a/box-test/src/main/resources/templates/test/sendJson.html b/box-test/src/main/resources/templates/test/sendJson.html new file mode 100644 index 000000000..9c01a77e4 --- /dev/null +++ b/box-test/src/main/resources/templates/test/sendJson.html @@ -0,0 +1,99 @@ + + + + + 发送json + + + + + + +
    +


    + +
    +
    + + + diff --git a/box-test/src/main/resources/templates/test/testVerify.html b/box-test/src/main/resources/templates/test/testVerify.html new file mode 100644 index 000000000..b58ae6111 --- /dev/null +++ b/box-test/src/main/resources/templates/test/testVerify.html @@ -0,0 +1,65 @@ + + + + + + +
    +
    +
    +
    用户名: + + +
    + +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/bps-kettle/pom.xml b/bps-kettle/pom.xml new file mode 100644 index 000000000..e8db1e4f9 --- /dev/null +++ b/bps-kettle/pom.xml @@ -0,0 +1,168 @@ + + + + ruoyi + com.ruoyi + 4.6.2 + + 4.0.0 + + bps-kettle + + 9.0.0.0-423 + 2.5.16 + 1.7.2 + 2.6.12 + + + + + + com.ruoyi + ruoyi-common + + + com.ruoyi + ruoyi-system + + + com.ruoyi + ruoyi-quartz + + + + + pentaho-kettle + kettle-core + ${kettle-version} + + + pentaho-kettle + kettle-engine + ${kettle-version} + + + org.apache.poi + poi + + + org.apache.poi + poi-ooxml + + + org.apache.poi + poi-ooxml-schemas + + + + + pentaho-kettle + kettle-dbdialog + ${kettle-version} + + + org.pentaho.di.plugins + kettle-sap-plugin-core + ${kettle-version} + + + pentaho-kettle + kettle-ui-swt + ${kettle-version} + + + + org.codehaus.janino + janino + ${janino-version} + + + org.eclipse.birt.runtime.3_7_1 + org.mozilla.javascript + ${javascript-version} + + + + net.sourceforge.jexcelapi + jxl + ${jxl-version} + + + + + mysql + mysql-connector-java + 8.0.11 + + + + mm.mysql + mm.mysql + 2.0.7 + + + + + com.microsoft.sqlserver + sqljdbc4 + 4.0 + + + + net.sourceforge.jtds + jtds + 1.2.4 + + + org.springframework.boot + spring-boot-starter-data-redis + + + redis.clients + jedis + + + + org.apache.poi + poi-ooxml + + + + + + + + pentaho + https://nexus.pentaho.org/content/groups/omni/ + + + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/config/RedisConfig.java b/bps-kettle/src/main/java/com/ruoyi/kettle/config/RedisConfig.java new file mode 100644 index 000000000..dcf2025c2 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/config/RedisConfig.java @@ -0,0 +1,195 @@ +package com.ruoyi.kettle.config; +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.lettuce.core.cluster.ClusterClientOptions; +import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.data.redis.RedisProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.RedisNode; +import org.springframework.data.redis.connection.RedisStandaloneConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.StringRedisSerializer; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; + +import java.time.Duration; +import java.util.HashSet; +import java.util.Set; + + +/** + * @author zh + * @date 2020/12/1 16:49 + */ +@Configuration +public class RedisConfig { + /** redis 服务器地址 */ + @Value("${spring.redis.host}") + private String host; + + /** redis 端口号 */ + @Value("${spring.redis.port}") + private int port; + + /** redis 服务器密码 */ + @Value("${spring.redis.password}") + private String password; + + /** redis 连接池最大连接数(使用负值无限制) */ + @Value("${spring.redis.lettuce.pool.max-active}") + private int maxActive; + + /** redis 连接池最大空闲数 */ + @Value("${spring.redis.lettuce.pool.max-idle}") + private int maxIdle; + + /** redis 连接池小空闲数 */ + @Value("${spring.redis.lettuce.pool.min-idle}") + private int minIdle; + + /** redis 连接池最大阻塞等待时间(负值无限制) */ + @Value("${spring.redis.lettuce.pool.max-wait}") + private int maxWait; + + /** redis 数据库索引(默认0) */ + @Value("${spring.redis.database}") + private int database; + + /** redis 超时时间 */ + @Value("${spring.redis.timeout}") + private int timeout; + + @Autowired + private RedisProperties redisProperties; + + + //这是固定的模板 + //自己定义了一个RedisTemplate + @Bean + @SuppressWarnings("all") + public RedisTemplate redisTemplate(@Qualifier("lettuceConnectionFactoryUvPv") RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + + //Json序列化配置 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); + ObjectMapper om = new ObjectMapper(); + om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + om.activateDefaultTyping(om.getPolymorphicTypeValidator()); + om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); + //解决序列化问题 + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + jackson2JsonRedisSerializer.setObjectMapper(om); + + //String的序列化 + StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); + + //key采用String的序列化方式 + template.setKeySerializer(stringRedisSerializer); + //hash的key也采用String的序列化方式 + template.setHashKeySerializer(stringRedisSerializer); + + //value序列化方式采用jackson + template.setValueSerializer(jackson2JsonRedisSerializer); + + //hash的value序列化方式采用jackson + template.setHashValueSerializer(jackson2JsonRedisSerializer); + template.afterPropertiesSet(); + + return template; + } + + /** + * 为RedisTemplate配置Redis连接工厂实现 + * LettuceConnectionFactory实现了RedisConnectionFactory接口 + * UVPV用Redis + * + * @return 返回LettuceConnectionFactory + */ + @Bean(destroyMethod = "destroy") + //这里要注意的是,在构建LettuceConnectionFactory 时,如果不使用内置的destroyMethod,可能会导致Redis连接早于其它Bean被销毁 + public LettuceConnectionFactory lettuceConnectionFactoryUvPv() throws Exception { + +// List clusterNodes = redisProperties.getCluster().getNodes(); +// Set nodes = new HashSet<>(); +// clusterNodes.forEach(address -> nodes.add(new RedisNode(address.split(":")[0].trim(), Integer.parseInt(address.split(":")[1])))); +// RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration(); +// clusterConfiguration.setClusterNodes(nodes); +// clusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword())); +// clusterConfiguration.setMaxRedirects(redisProperties.getCluster().getMaxRedirects()); + + //我使用的是单机redis,集群使用上面注释的代码 + Set nodes = new HashSet<>(); + nodes.add(new RedisNode(redisProperties.getHost(), redisProperties.getPort())); + + + RedisStandaloneConfiguration redisStandaloneConfiguration=new RedisStandaloneConfiguration(); + redisStandaloneConfiguration.setHostName(redisProperties.getHost()); + redisStandaloneConfiguration.setPassword(redisProperties.getPassword()); + redisStandaloneConfiguration.setDatabase(redisProperties.getDatabase()); + redisStandaloneConfiguration.setPort(redisProperties.getPort()); + + GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); + poolConfig.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle()); + poolConfig.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle()); + poolConfig.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive()); + + return new LettuceConnectionFactory(redisStandaloneConfiguration, getLettuceClientConfiguration(poolConfig)); + } + + /** + * 配置LettuceClientConfiguration 包括线程池配置和安全项配置 + * + * @param genericObjectPoolConfig common-pool2线程池 + * @return lettuceClientConfiguration + */ + private LettuceClientConfiguration getLettuceClientConfiguration(GenericObjectPoolConfig genericObjectPoolConfig) { + /* + ClusterTopologyRefreshOptions配置用于开启自适应刷新和定时刷新。如自适应刷新不开启,Redis集群变更时将会导致连接异常! + */ + ClusterTopologyRefreshOptions topologyRefreshOptions = ClusterTopologyRefreshOptions.builder() + //开启自适应刷新 + //.enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT, ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS) + //开启所有自适应刷新,MOVED,ASK,PERSISTENT都会触发 + .enableAllAdaptiveRefreshTriggers() + // 自适应刷新超时时间(默认30秒) + .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(25)) //默认关闭开启后时间为30秒 + // 开周期刷新 + .enablePeriodicRefresh(Duration.ofSeconds(20)) // 默认关闭开启后时间为60秒 ClusterTopologyRefreshOptions.DEFAULT_REFRESH_PERIOD 60 .enablePeriodicRefresh(Duration.ofSeconds(2)) = .enablePeriodicRefresh().refreshPeriod(Duration.ofSeconds(2)) + .build(); + return LettucePoolingClientConfiguration.builder() + .poolConfig(genericObjectPoolConfig) + .clientOptions(ClusterClientOptions.builder().topologyRefreshOptions(topologyRefreshOptions).build()) + //将appID传入连接,方便Redis监控中查看 + //.clientName(appName + "_lettuce") + .build(); + } + + @Bean + public JedisPool jedisPool() { + JedisPool jedisPool = new JedisPool(getRedisConfig(), host, port, timeout,password); + return jedisPool; + } + @Bean + public JedisPoolConfig getRedisConfig(){ + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(maxActive); + config.setMaxIdle(maxIdle); + config.setMinIdle(minIdle); + config.setMaxWaitMillis(maxWait); + return config; + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XJobStatus.java b/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XJobStatus.java new file mode 100644 index 000000000..a95e5101d --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XJobStatus.java @@ -0,0 +1,43 @@ +package com.ruoyi.kettle.cons; + +public enum XJobStatus { + UNKNOWN("-1", "未知状态"), + PENDING("10", "等待中"), + RUNNING("20", "运行中"), + HALTING("21", "终止中"), + STOPPED("30", "已中断"), + FINISHED("40", "已完成"), + SUCCESS("88", "运行成功"), + FAILED("99", "异常停止"); + + private String status; + private String description; + + private XJobStatus(String status, String description) { + this.status = status; + this.description = description; + } + + public String value() { + return this.status; + } + + public String description() { + return this.description; + } + + public static XJobStatus forName(Integer value) { + XJobStatus[] statuses = values(); + XJobStatus[] var2 = statuses; + int var3 = statuses.length; + + for(int var4 = 0; var4 < var3; ++var4) { + XJobStatus status = var2[var4]; + if (status.value().equals(value)) { + return status; + } + } + + return UNKNOWN; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XTransStatus.java b/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XTransStatus.java new file mode 100644 index 000000000..7017e4fc5 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/cons/XTransStatus.java @@ -0,0 +1,46 @@ +package com.ruoyi.kettle.cons; + +public enum XTransStatus { + UNKNOWN("-1", "未知状态"), + WAITING("10", "等待中"), + INITIALIZING("20", "转换初始化"), + PREPARING("21", "转换准备执行"), + STOPPED("30", "已终止"), + FINISHED("40", "已结束"), + RUNNING("50", "转换运行中"), + PAUSED("60", "转换已被暂停"), + HALTING("70", "转换被挂起"), + SUCCESS("88", "转换运行成功"), + FAILED("99", "异常停止"); + + private String status; + private String description; + + private XTransStatus(String status, String description) { + this.status = status; + this.description = description; + } + + public String value() { + return this.status; + } + + public String description() { + return this.description; + } + + public static XTransStatus forName(Integer value) { + XTransStatus[] statuses = values(); + XTransStatus[] var2 = statuses; + int var3 = statuses.length; + + for(int var4 = 0; var4 < var3; ++var4) { + XTransStatus status = var2[var4]; + if (status.value().equals(value)) { + return status; + } + } + + return UNKNOWN; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleJobController.java b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleJobController.java new file mode 100644 index 000000000..88507ee22 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleJobController.java @@ -0,0 +1,186 @@ +package com.ruoyi.kettle.controller; + +import java.util.List; +import java.util.stream.Collectors; + +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.kettle.domain.KettleTrans; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.system.service.ISysRoleService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.kettle.domain.KettleJob; +import com.ruoyi.kettle.service.IKettleJobService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 作业调度Controller + * + * @author kone + * @date 2021-07-22 + */ +@Controller +@RequestMapping("/kettle/job") +public class KettleJobController extends BaseController +{ + private String prefix = "kettle/job"; + + @Autowired + private IKettleJobService kettleJobService; + @Autowired + private ISysRoleService roleService; + @Autowired + private ISysJobService jobService; + @RequiresPermissions("kettle:job:view") + @GetMapping() + public String job() + { + return prefix + "/job"; + } + + /** + * 查询作业调度列表 + */ + @RequiresPermissions("kettle:job:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(KettleJob kettleJob) + { + startPage(); + List list = kettleJobService.selectKettleJobList(kettleJob); + return getDataTable(list); + } + + /** + * 导出作业调度列表 + */ + @RequiresPermissions("kettle:job:export") + @Log(title = "作业调度", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(KettleJob kettleJob) + { + List list = kettleJobService.selectKettleJobList(kettleJob); + ExcelUtil util = new ExcelUtil(KettleJob.class); + return util.exportExcel(list, "作业调度数据"); + } + + /** + * 新增作业调度 + */ + @GetMapping("/add") + public String add(ModelMap mmap) + { + List roleKeys=roleService.selectRoleAll().stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + mmap.put("allRoles",roleKeys); + return prefix + "/add"; + } + + /** + * 新增保存作业调度 + */ + @RequiresPermissions("kettle:job:add") + @Log(title = "作业调度", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(KettleJob kettleJob) + { + return kettleJobService.insertKettleJob(kettleJob); + } + + /** + * 修改作业调度 + */ + @GetMapping("/edit/{id}") + public String edit(@PathVariable("id") Long id, ModelMap mmap) + { + KettleJob kettleJob = kettleJobService.selectKettleJobById(id); + List roleKeys=roleService.selectRoleAll().stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + String rks=""; + if(kettleJob!=null && kettleJob.getRoleKey()!=null){ + rks=kettleJob.getRoleKey(); + } + String[] rkArray=rks.split(","); + mmap.put("allRoles",roleKeys); + mmap.put("rkArray",rkArray); + mmap.put("kettleJob", kettleJob); + return prefix + "/edit"; + } + + /** + * 修改保存作业调度 + */ + @RequiresPermissions("kettle:job:edit") + @Log(title = "作业调度", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(KettleJob kettleJob) + { + return toAjax(kettleJobService.updateKettleJob(kettleJob)); + } + + /** + * 删除作业调度 + */ + @RequiresPermissions("kettle:job:remove") + @Log(title = "作业调度", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(kettleJobService.deleteKettleJobByIds(ids)); + } + + + @Log(title = "立即执行作业", businessType = BusinessType.UPDATE) + @RequiresPermissions("kettle:job:run") + @PostMapping("/run") + @ResponseBody + public AjaxResult run(KettleJob job) + { + AjaxResult result = kettleJobService.run(job); + return result; + } + @RequiresPermissions("kettle:job:log") + @GetMapping("/detail/{id}") + public String detail(@PathVariable("id") Long id, ModelMap mmap) + { + KettleJob kettleJob = kettleJobService.selectKettleJobById(id); + List jobLog= kettleJobService.queryJobLog(kettleJob); + mmap.put("kettleJob", kettleJob); + mmap.put("jobLog",jobLog); + return prefix + "/detail"; + } + /** + * 跳转到新增调度定时任务页面 + */ + @RequiresPermissions("kettle:job:setquartz") + @GetMapping("/jobQuartz/{id}") + public String jobQuartz(@PathVariable("id") Long id,ModelMap mmap) + { + KettleJob kettleJob = kettleJobService.selectKettleJobById(id); + //kettleTransServiceImpl.runTransQuartz('12','text') + String checkStr="kettleJobServiceImpl.runJobQuartz('"+kettleJob.getId()+"','"+kettleJob.getJobName()+"')"; + Long jobId = kettleJobService.checkQuartzExist(checkStr); + if(jobId != null){ + mmap.put("job", jobService.selectJobById(Long.valueOf(jobId))); + return "kettle/quartz/editquartz"; + }else{ + mmap.put("invokeTarget", checkStr); + //mmap.put("job", kettleJob); + return "kettle/quartz/addquartz"; + } + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleTransController.java b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleTransController.java new file mode 100644 index 000000000..1245529fd --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/KettleTransController.java @@ -0,0 +1,196 @@ +package com.ruoyi.kettle.controller; + +import java.util.List; +import java.util.stream.Collectors; + +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.quartz.service.ISysJobService; +import com.ruoyi.system.service.ISysRoleService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.kettle.domain.KettleTrans; +import com.ruoyi.kettle.service.IKettleTransService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 转换Controller + * + * @author kone + * @date 2021-07-14 + */ +@Controller +@RequestMapping("/kettle/trans") +public class KettleTransController extends BaseController +{ + private String prefix = "kettle/trans"; + + @Autowired + private IKettleTransService kettleTransService; + @Autowired + private ISysRoleService roleService; + @Autowired + private ISysJobService jobService; + @RequiresPermissions("kettle:trans:view") + @GetMapping() + public String trans() + { + return prefix + "/trans"; + } + + /** + * 查询转换列表 + */ + @RequiresPermissions("kettle:trans:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(KettleTrans kettleTrans) + { + startPage(); + List list = kettleTransService.selectKettleTransList(kettleTrans); + return getDataTable(list); + } + + /** + * 导出转换列表 + */ + @RequiresPermissions("kettle:trans:export") + @Log(title = "转换", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(KettleTrans kettleTrans) + { + List list = kettleTransService.selectKettleTransList(kettleTrans); + ExcelUtil util = new ExcelUtil(KettleTrans.class); + return util.exportExcel(list, "转换数据"); + } + + /** + * 新增转换 + */ + @GetMapping("/add") + public String add(ModelMap mmap) + { + List roleKeys=roleService.selectRoleAll().stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + mmap.put("allRoles",roleKeys); + return prefix + "/add"; + } + + /** + * 新增保存转换 + */ + @RequiresPermissions("kettle:trans:add") + @Log(title = "转换", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(KettleTrans kettleTrans) + { + return kettleTransService.insertKettleTrans(kettleTrans) ; + } + + /** + * 修改转换 + */ + @GetMapping("/edit/{id}") + public String edit(@PathVariable("id") Long id, ModelMap mmap) + { + KettleTrans kettleTrans = kettleTransService.selectKettleTransById(id); + List roleKeys=roleService.selectRoleAll().stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + String rks=""; + if(kettleTrans!=null && kettleTrans.getRoleKey()!=null){ + rks=kettleTrans.getRoleKey(); + } + String[] rkArray=rks.split(","); + mmap.put("allRoles",roleKeys); + mmap.put("kettleTrans", kettleTrans); + mmap.put("rkArray",rkArray); + return prefix + "/edit"; + } + + /** + * 修改保存转换 + */ + @RequiresPermissions("kettle:trans:edit") + @Log(title = "转换", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(KettleTrans kettleTrans) + { + return toAjax(kettleTransService.updateKettleTrans(kettleTrans)); + } + + /** + * 删除转换 + */ + @RequiresPermissions("kettle:trans:remove") + @Log(title = "转换", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(kettleTransService.deleteKettleTransByIds(ids)); + } + + /** + * 转换执行得日志 + */ + @RequiresPermissions("kettle:trans:log") + @GetMapping("/detail/{id}") + public String detail(@PathVariable("id") Long id, ModelMap mmap) + { + KettleTrans kettleTrans = kettleTransService.selectKettleTransById(id); + List transLog= kettleTransService.queryTransLog(kettleTrans); + mmap.put("kettleTrans", kettleTrans); + mmap.put("transLog",transLog); + return prefix + "/detail"; + } + /** + * 转换立即执行一次 + */ + @Log(title = "立即执行转换", businessType = BusinessType.UPDATE) + @RequiresPermissions("kettle:trans:run") + @PostMapping("/run") + @ResponseBody + public AjaxResult runToQueue(KettleTrans trans) + { + AjaxResult result = kettleTransService.runToQueue(trans); + return result; + } + + + /** + * 跳转到新增调度定时任务页面 + */ + @RequiresPermissions("kettle:trans:setquartz") + @GetMapping("/transQuartz/{id}") + public String transQuartz(@PathVariable("id") Long id,ModelMap mmap) + { + KettleTrans trans = kettleTransService.selectKettleTransById(id); + //kettleTransServiceImpl.runTransQuartz('12','text') + String checkStr="kettleTransServiceImpl.runTransQuartz('"+trans.getId()+"','"+trans.getTransName()+"')"; + Long jobId = kettleTransService.checkQuartzExist(checkStr); + if(jobId != null){ + mmap.put("job", jobService.selectJobById(Long.valueOf(jobId))); + return "kettle/quartz/editquartz"; + }else{ + + mmap.put("invokeTarget", checkStr); + //mmap.put("trans", trans); + return "kettle/quartz/addquartz"; + } + } + + + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/controller/XRepositoryController.java b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/XRepositoryController.java new file mode 100644 index 000000000..cb8f61be1 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/controller/XRepositoryController.java @@ -0,0 +1,153 @@ +package com.ruoyi.kettle.controller; + +import java.util.List; + +import com.ruoyi.common.core.domain.Ztree; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.kettle.repo.RepoTree; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.kettle.domain.XRepository; +import com.ruoyi.kettle.service.IXRepositoryService; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.common.core.page.TableDataInfo; + +/** + * 资源库Controller + * + * @author kone + * @date 2021-07-12 + */ +@Controller +@RequestMapping("/kettle/repository") +public class XRepositoryController extends BaseController +{ + private String prefix = "kettle/repository"; + + @Autowired + private IXRepositoryService xRepositoryService; + + @RequiresPermissions("kettle:repository:view") + @GetMapping() + public String repository() + { + return prefix + "/repository"; + } + + /** + * 查询资源库列表 + */ + @RequiresPermissions("kettle:repository:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(XRepository xRepository) + { + startPage(); + List list = xRepositoryService.selectXRepositoryList(xRepository); + return getDataTable(list); + } + @GetMapping(value = { "/selectRepositoryTree", "/selectRepositoryTree/{type}" }) + public String selectRepositoryTree( @PathVariable(value = "type", required = false) String type, ModelMap mmap) + { + XRepository r=new XRepository(); + List repoTree = xRepositoryService.selectXRepositoryList(r); + //XRepository repository=xRepositoryService.selectXRepositoryById(2L); + //mmap.put("repository", repository); + mmap.put("repoTree", repoTree); + mmap.put("type", type); + return "kettle/common/repository_tree"; + } + @GetMapping("/repositoryRoot") + @ResponseBody + public List repositoryRoot() + { + List ztrees = xRepositoryService.selectRepoRoot(new XRepository()); + return ztrees; + } + @PostMapping("/qryRepoSubTree/{id}/{type}") + @ResponseBody + public List qryRepoSubTree(@PathVariable("id") Long id,@PathVariable("type") String type, ModelMap mmapy) { + List ztrees = xRepositoryService.selectRepoTree(id,type); + return ztrees; + } + /** + * 导出资源库列表 + */ + @RequiresPermissions("kettle:repository:export") + @Log(title = "资源库", businessType = BusinessType.EXPORT) + @PostMapping("/export") + @ResponseBody + public AjaxResult export(XRepository xRepository) + { + List list = xRepositoryService.selectXRepositoryList(xRepository); + ExcelUtil util = new ExcelUtil(XRepository.class); + return util.exportExcel(list, "资源库数据"); + } + + /** + * 新增资源库 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存资源库 + */ + @RequiresPermissions("kettle:repository:add") + @Log(title = "资源库", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(XRepository xRepository) + { + return toAjax(xRepositoryService.insertXRepository(xRepository)); + } + + /** + * 修改资源库 + */ + @GetMapping("/edit/{id}") + public String edit(@PathVariable("id") Long id, ModelMap mmap) + { + XRepository xRepository = xRepositoryService.selectXRepositoryById(id); + mmap.put("xRepository", xRepository); + return prefix + "/edit"; + } + + /** + * 修改保存资源库 + */ + @RequiresPermissions("kettle:repository:edit") + @Log(title = "资源库", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(XRepository xRepository) + { + return toAjax(xRepositoryService.updateXRepository(xRepository)); + } + + /** + * 删除资源库 + */ + @RequiresPermissions("kettle:repository:remove") + @Log(title = "资源库", businessType = BusinessType.DELETE) + @PostMapping( "/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(xRepositoryService.deleteXRepositoryByIds(ids)); + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleJob.java b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleJob.java new file mode 100644 index 000000000..d1d3edb80 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleJob.java @@ -0,0 +1,230 @@ +package com.ruoyi.kettle.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +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; + +/** + * 作业调度对象 kettle_job + * + * @author kone + * @date 2021-07-22 + */ +public class KettleJob extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** */ + private Long id; + + /** */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "", width = 30, dateFormat = "yyyy-MM-dd") + private Date createdTime; + + /** */ + @Excel(name = "") + private String createdBy; + + /** 作业名称 */ + @Excel(name = "作业名称") + private String jobName; + + /** 描述 */ + @Excel(name = "描述") + private String jobDescription; + + /** 作业类型(file,ftp,sf) */ + @Excel(name = "作业类型(file,ftp,sf)") + private String jobType; + + /** 路径 */ + @Excel(name = "路径") + private String jobPath; + + /** 资源库id */ + @Excel(name = "资源库id") + private Long jobRepositoryId; + + /** 日志级别 */ + @Excel(name = "日志级别") + private String jobLogLevel; + + /** 状态 */ + @Excel(name = "状态") + private String jobStatus; + + /** 是否删除 */ + @Excel(name = "是否删除") + private Integer isDel; + + /** 是否监控 */ + @Excel(name = "是否监控") + private Integer isMonitorEnabled; + + /** 可执行角色key,用+号拼接 */ + @Excel(name = "可执行角色key,用+号拼接") + private String roleKey; + + /** */ + @Excel(name = "") + private String tplKey; + @Excel(name = "最后一次成功时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastSucceedTime; + + public Date getLastSucceedTime() { + return lastSucceedTime; + } + + public void setLastSucceedTime(Date lastSucceedTime) { + this.lastSucceedTime = lastSucceedTime; + } + + public Long getId() + { + return id; + } + public void setCreatedTime(Date createdTime) + { + this.createdTime = createdTime; + } + + public Date getCreatedTime() + { + return createdTime; + } + public void setCreatedBy(String createdBy) + { + this.createdBy = createdBy; + } + + public String getCreatedBy() + { + return createdBy; + } + public void setJobName(String jobName) + { + this.jobName = jobName; + } + + public String getJobName() + { + return jobName; + } + public void setJobDescription(String jobDescription) + { + this.jobDescription = jobDescription; + } + + public String getJobDescription() + { + return jobDescription; + } + public void setJobType(String jobType) + { + this.jobType = jobType; + } + + public String getJobType() + { + return jobType; + } + public void setJobPath(String jobPath) + { + this.jobPath = jobPath; + } + + public String getJobPath() + { + return jobPath; + } + public void setJobRepositoryId(Long jobRepositoryId) + { + this.jobRepositoryId = jobRepositoryId; + } + + public Long getJobRepositoryId() + { + return jobRepositoryId; + } + public void setJobLogLevel(String jobLogLevel) + { + this.jobLogLevel = jobLogLevel; + } + + public String getJobLogLevel() + { + return jobLogLevel; + } + public void setJobStatus(String jobStatus) + { + this.jobStatus = jobStatus; + } + + public String getJobStatus() + { + return jobStatus; + } + public void setIsDel(Integer isDel) + { + this.isDel = isDel; + } + + public Integer getIsDel() + { + return isDel; + } + public void setIsMonitorEnabled(Integer isMonitorEnabled) + { + this.isMonitorEnabled = isMonitorEnabled; + } + + public Integer getIsMonitorEnabled() + { + return isMonitorEnabled; + } + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + public String getRoleKey() + { + return roleKey; + } + public void setTplKey(String tplKey) + { + this.tplKey = tplKey; + } + + public String getTplKey() + { + return tplKey; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("createdTime", getCreatedTime()) + .append("updateTime", getUpdateTime()) + .append("createdBy", getCreatedBy()) + .append("updateBy", getUpdateBy()) + .append("jobName", getJobName()) + .append("jobDescription", getJobDescription()) + .append("jobType", getJobType()) + .append("jobPath", getJobPath()) + .append("jobRepositoryId", getJobRepositoryId()) + .append("jobLogLevel", getJobLogLevel()) + .append("jobStatus", getJobStatus()) + .append("isDel", getIsDel()) + .append("isMonitorEnabled", getIsMonitorEnabled()) + .append("roleKey", getRoleKey()) + .append("tplKey", getTplKey()) + .toString(); + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleTrans.java b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleTrans.java new file mode 100644 index 000000000..26d0700ff --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/KettleTrans.java @@ -0,0 +1,232 @@ +package com.ruoyi.kettle.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +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; + +/** + * 转换对象 kettle_trans + * + * @author kone + * @date 2021-07-14 + */ +public class KettleTrans extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** */ + private Long id; + + /** 转换名称 */ + @Excel(name = "转换名称") + private String transName; + + /** 转换描述 */ + @Excel(name = "转换描述") + private String transDescription; + + /** */ + private Date createdTime; + + /** */ + private String createdBy; + + /** 转换类型(file,ftp,sf) */ + private String transType; + + /** 路径 */ + @Excel(name = "路径") + private String transPath; + + /** 所属资源库id */ + @Excel(name = "所属资源库id") + private Long transRepositoryId; + + /** 日志级别 */ + @Excel(name = "日志级别") + private String transLogLevel; + + /** 状态 */ + @Excel(name = "状态") + private String transStatus; + + /** 是否删除 */ + @Excel(name = "是否删除") + private Integer isDel; + + /** 是否启用 */ + @Excel(name = "是否启用") + private Integer isMonitorEnabled; + + /** 保留备用 */ + private String tplKey; + + /** 可执行角色key,用+号拼接 */ + @Excel(name = "可执行角色key") + private String roleKey; + + @Excel(name = "最后一次成功时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date lastSucceedTime; + + public Date getLastSucceedTime() { + return lastSucceedTime; + } + + public void setLastSucceedTime(Date lastSucceedTime) { + this.lastSucceedTime = lastSucceedTime; + } + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setTransName(String transName) + { + this.transName = transName; + } + + public String getTransName() + { + return transName; + } + public void setTransDescription(String transDescription) + { + this.transDescription = transDescription; + } + + public String getTransDescription() + { + return transDescription; + } + public void setCreatedTime(Date createdTime) + { + this.createdTime = createdTime; + } + + public Date getCreatedTime() + { + return createdTime; + } + public void setCreatedBy(String createdBy) + { + this.createdBy = createdBy; + } + + public String getCreatedBy() + { + return createdBy; + } + public void setTransType(String transType) + { + this.transType = transType; + } + + public String getTransType() + { + return transType; + } + public void setTransPath(String transPath) + { + this.transPath = transPath; + } + + public String getTransPath() + { + return transPath; + } + public void setTransRepositoryId(Long transRepositoryId) + { + this.transRepositoryId = transRepositoryId; + } + + public Long getTransRepositoryId() + { + return transRepositoryId; + } + public void setTransLogLevel(String transLogLevel) + { + this.transLogLevel = transLogLevel; + } + + public String getTransLogLevel() + { + return transLogLevel; + } + public void setTransStatus(String transStatus) + { + this.transStatus = transStatus; + } + + public String getTransStatus() + { + return transStatus; + } + public void setIsDel(Integer isDel) + { + this.isDel = isDel; + } + + public Integer getIsDel() + { + return isDel; + } + public void setIsMonitorEnabled(Integer isMonitorEnabled) + { + this.isMonitorEnabled = isMonitorEnabled; + } + + public Integer getIsMonitorEnabled() + { + return isMonitorEnabled; + } + public void setTplKey(String tplKey) + { + this.tplKey = tplKey; + } + + public String getTplKey() + { + return tplKey; + } + public void setRoleKey(String roleKey) + { + this.roleKey = roleKey; + } + + public String getRoleKey() + { + return roleKey; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("transName", getTransName()) + .append("transDescription", getTransDescription()) + .append("createdTime", getCreatedTime()) + .append("updateTime", getUpdateTime()) + .append("createdBy", getCreatedBy()) + .append("updateBy", getUpdateBy()) + .append("transType", getTransType()) + .append("transPath", getTransPath()) + .append("transRepositoryId", getTransRepositoryId()) + .append("transLogLevel", getTransLogLevel()) + .append("transStatus", getTransStatus()) + .append("isDel", getIsDel()) + .append("isMonitorEnabled", getIsMonitorEnabled()) + .append("tplKey", getTplKey()) + .append("roleKey", getRoleKey()) + .append("remark", getRemark()) + .toString(); + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/domain/XRepository.java b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/XRepository.java new file mode 100644 index 000000000..769576acf --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/domain/XRepository.java @@ -0,0 +1,256 @@ +package com.ruoyi.kettle.domain; + +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; +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; + +/** + * 资源库对象 kettle_repository + * + * @author kone + * @date 2021-07-12 + */ +public class XRepository extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** */ + private Long id; + + /** */ + @Excel(name = "") + private String repoId; + + /** 资源库名称 */ + @Excel(name = "资源库名称") + private String repoName; + + /** 当资源库类型是db时候的用户名 */ + private String repoUsername; + + /** 当资源库类型是db时候的密码 */ + private String repoPassword; + + /** db类型 */ + private String repoType; + + /** 当资源库类型是db时候的连接类型 */ + private String dbAccess; + + /** 当资源库类型是db时候的ip */ + private String dbHost; + + /** 当资源库类型是db时候的端口 */ + private String dbPort; + + /** 当资源库类型是db时候的db库名 */ + private String dbName; + + /** 当资源库类型是db时候的db用户名 */ + private String dbUsername; + + /** 当资源库类型是db时候的db用户密码 */ + private String dbPassword; + + /** 软删除 */ + private int isDel; + + /** */ + @JsonFormat(pattern = "yyyy-MM-dd") + @Excel(name = "", width = 30, dateFormat = "yyyy-MM-dd") + private Date createdTime; + + /** */ + @Excel(name = "") + private String createdBy; + + /** 资源库类型 */ + @Excel(name = "资源库类型") + private String type; + + /** 基础路径 */ + @Excel(name = "基础路径") + private String baseDir; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setRepoId(String repoId) + { + this.repoId = repoId; + } + + public String getRepoId() + { + return repoId; + } + public void setRepoName(String repoName) + { + this.repoName = repoName; + } + + public String getRepoName() + { + return repoName; + } + public void setRepoUsername(String repoUsername) + { + this.repoUsername = repoUsername; + } + + public String getRepoUsername() + { + return repoUsername; + } + public void setRepoPassword(String repoPassword) + { + this.repoPassword = repoPassword; + } + + public String getRepoPassword() + { + return repoPassword; + } + public void setRepoType(String repoType) + { + this.repoType = repoType; + } + + public String getRepoType() + { + return repoType; + } + public void setDbAccess(String dbAccess) + { + this.dbAccess = dbAccess; + } + + public String getDbAccess() + { + return dbAccess; + } + public void setDbHost(String dbHost) + { + this.dbHost = dbHost; + } + + public String getDbHost() + { + return dbHost; + } + public void setDbPort(String dbPort) + { + this.dbPort = dbPort; + } + + public String getDbPort() + { + return dbPort; + } + public void setDbName(String dbName) + { + this.dbName = dbName; + } + + public String getDbName() + { + return dbName; + } + public void setDbUsername(String dbUsername) + { + this.dbUsername = dbUsername; + } + + public String getDbUsername() + { + return dbUsername; + } + public void setDbPassword(String dbPassword) + { + this.dbPassword = dbPassword; + } + + public String getDbPassword() + { + return dbPassword; + } + public void setIsDel(int isDel) + { + this.isDel = isDel; + } + + public int getIsDel() + { + return isDel; + } + public void setCreatedTime(Date createdTime) + { + this.createdTime = createdTime; + } + + public Date getCreatedTime() + { + return createdTime; + } + public void setCreatedBy(String createdBy) + { + this.createdBy = createdBy; + } + + public String getCreatedBy() + { + return createdBy; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + public void setBaseDir(String baseDir) + { + this.baseDir = baseDir; + } + + public String getBaseDir() + { + return baseDir; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("repoId", getRepoId()) + .append("repoName", getRepoName()) + .append("repoUsername", getRepoUsername()) + .append("repoPassword", getRepoPassword()) + .append("repoType", getRepoType()) + .append("dbAccess", getDbAccess()) + .append("dbHost", getDbHost()) + .append("dbPort", getDbPort()) + .append("dbName", getDbName()) + .append("dbUsername", getDbUsername()) + .append("dbPassword", getDbPassword()) + .append("isDel", getIsDel()) + .append("createdTime", getCreatedTime()) + .append("updateTime", getUpdateTime()) + .append("createdBy", getCreatedBy()) + .append("updateBy", getUpdateBy()) + .append("type", getType()) + .append("baseDir", getBaseDir()) + .toString(); + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleJobMapper.java b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleJobMapper.java new file mode 100644 index 000000000..05d31bb1c --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleJobMapper.java @@ -0,0 +1,69 @@ +package com.ruoyi.kettle.mapper; + +import java.util.List; +import com.ruoyi.kettle.domain.KettleJob; +import org.apache.ibatis.annotations.Param; + +/** + * 作业调度Mapper接口 + * + * @author kone + * @date 2021-07-22 + */ +public interface KettleJobMapper +{ + /** + * 查询作业调度 + * + * @param id 作业调度ID + * @return 作业调度 + */ + public KettleJob selectKettleJobById(Long id); + + /** + * 查询作业调度列表 + * + * @param kettleJob 作业调度 + * @param roleKeys + * @return 作业调度集合 + */ + public List selectKettleJobList(@Param("kettleJob") KettleJob kettleJob,@Param("roleKey") List roleKeys); + + /** + * 新增作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + public int insertKettleJob(KettleJob kettleJob); + + /** + * 修改作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + public int updateKettleJob(KettleJob kettleJob); + + /** + * 删除作业调度 + * + * @param id 作业调度ID + * @return 结果 + */ + public int deleteKettleJobById(Long id); + + /** + * 批量删除作业调度 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteKettleJobByIds(String[] ids); + + int selectJobByNameAndRepoId(@Param("jobName")String jobName, @Param("jobRepositoryId")Long jobRepositoryId); + + List queryJobLog(String jobName); + + Long checkQuartzExist(String checkStr); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleTransMapper.java b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleTransMapper.java new file mode 100644 index 000000000..2152fe8f2 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/KettleTransMapper.java @@ -0,0 +1,69 @@ +package com.ruoyi.kettle.mapper; + +import java.util.List; +import com.ruoyi.kettle.domain.KettleTrans; +import org.apache.ibatis.annotations.Param; + +/** + * 转换Mapper接口 + * + * @author kone + * @date 2021-07-14 + */ +public interface KettleTransMapper +{ + /** + * 查询转换 + * + * @param id 转换ID + * @return 转换 + */ + public KettleTrans selectKettleTransById(Long id); + + /** + * 查询转换列表 + * + * @param kettleTrans 转换 + * @param roleKey + * @return 转换集合 + */ + public List selectKettleTransList(@Param("KettleTrans") KettleTrans kettleTrans,@Param("roleKey") List roleKey); + + /** + * 新增转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + public int insertKettleTrans(KettleTrans kettleTrans); + + /** + * 修改转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + public int updateKettleTrans(KettleTrans kettleTrans); + + /** + * 删除转换 + * + * @param id 转换ID + * @return 结果 + */ + public int deleteKettleTransById(Long id); + + /** + * 批量删除转换 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteKettleTransByIds(String[] ids); + + int selectKettleTransByTransName(String transName); + + List queryTransLog(String transName); + + Long checkQuartzExist(String checkStr); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/XRepositoryMapper.java b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/XRepositoryMapper.java new file mode 100644 index 000000000..b3893f0f3 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/mapper/XRepositoryMapper.java @@ -0,0 +1,77 @@ +package com.ruoyi.kettle.mapper; + +import java.util.List; +import com.ruoyi.kettle.domain.XRepository; + +/** + * 资源库Mapper接口 + * + * @author kone + * @date 2021-07-12 + */ +public interface XRepositoryMapper +{ + /** + * 查询资源库 + * + * @param id 资源库ID + * @return 资源库 + */ + public XRepository selectXRepositoryById(Long id); + + /** + * 查询资源库列表 + * + * @param xRepository 资源库 + * @return 资源库集合 + */ + public List selectXRepositoryList(XRepository xRepository); + + /** + * 新增资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + public int insertXRepository(XRepository xRepository); + + /** + * 修改资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + public int updateXRepository(XRepository xRepository); + + /** + * 删除资源库 + * + * @param id 资源库ID + * @return 结果 + */ + public int deleteXRepositoryById(Long id); + + /** + * 批量删除资源库 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteXRepositoryByIds(String[] ids); + /** + * @Description:软删除 + * @Author: Kone.wang + * @Date: 14:07 + * @param id: + * @return: int + **/ + int updateIsDel(Long id); + /** + * @Description:批量软删除 + * @Author: Kone.wang + * @Date: 2021/7/19 14:07 + * @param ids: + * @return: int + **/ + public int updateIsDelBatch(String[] ids); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepoTree.java b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepoTree.java new file mode 100644 index 000000000..1f2f5baa2 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepoTree.java @@ -0,0 +1,96 @@ +package com.ruoyi.kettle.repo; + +public class RepoTree { + private static final long serialVersionUID = 1L; + + /** 节点ID */ + private String id; + + /** 节点父ID */ + private String pId; + + /** 节点名称 */ + private String name; + + /** 节点标题 */ + private String title; + + /** 是否勾选 */ + private boolean checked = false; + + /** 是否展开 */ + private boolean open = false; + + /** 是否能勾选 */ + private boolean nocheck = false; + + public String getId() + { + return id; + } + + public void setId(String id) + { + this.id = id; + } + + public String getpId() + { + return pId; + } + + public void setpId(String pId) + { + this.pId = pId; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public boolean isChecked() + { + return checked; + } + + public void setChecked(boolean checked) + { + this.checked = checked; + } + + public boolean isOpen() + { + return open; + } + + public void setOpen(boolean open) + { + this.open = open; + } + + public boolean isNocheck() + { + return nocheck; + } + + public void setNocheck(boolean nocheck) + { + this.nocheck = nocheck; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepositoryTree.java b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepositoryTree.java new file mode 100644 index 000000000..cbb747bb2 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/RepositoryTree.java @@ -0,0 +1,94 @@ +package com.ruoyi.kettle.repo; + +public class RepositoryTree { + private String id; + private String parent; + private String text; + private String icon; + private Object state; + private String type; + private boolean isLasted; + private String path; + + public String getType() { + return this.type; + } + + public void setType(String type) { + this.type = type; + } + + public String getPath() { + return this.path; + } + + public void setPath(String path) { + this.path = path; + } + + public boolean isLasted() { + return this.isLasted; + } + + public void setLasted(boolean isLasted) { + this.isLasted = isLasted; + } + + public String getId() { + return this.id; + } + + public void setId(String id) { + this.id = id; + } + + public String getParent() { + return this.parent; + } + + public void setParent(String parent) { + this.parent = parent; + } + + public String getText() { + return this.text; + } + + public void setText(String text) { + this.text = text; + } + + public String getIcon() { + return this.icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public Object getState() { + return this.state; + } + + public void setState(Object state) { + this.state = state; + } + + public RepositoryTree(String id, String parent, String text, String icon, Object state, String type, boolean isLasted, String path) { + this.id = id; + this.parent = parent; + this.text = text; + this.icon = icon; + this.state = state; + this.type = type; + this.isLasted = isLasted; + this.path = path; + } + + public RepositoryTree() { + } + + public String toString() { + return "RepositoryTree [id=" + this.id + ", parent=" + this.parent + ", text=" + this.text + ", icon=" + this.icon + ", state=" + this.state + ", type=" + this.type + ", isLasted=" + this.isLasted + ", path=" + this.path + "]"; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/repo/XRepoManager.java b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/XRepoManager.java new file mode 100644 index 000000000..ad01cc53b --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/repo/XRepoManager.java @@ -0,0 +1,266 @@ +package com.ruoyi.kettle.repo; + + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import org.pentaho.di.core.KettleEnvironment; +import org.pentaho.di.core.ProgressMonitorListener; +import org.pentaho.di.core.database.DatabaseMeta; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.repository.*; +import org.pentaho.di.repository.filerep.KettleFileRepository; +import org.pentaho.di.repository.filerep.KettleFileRepositoryMeta; +import org.pentaho.di.repository.kdr.KettleDatabaseRepository; +import org.pentaho.di.repository.kdr.KettleDatabaseRepositoryMeta; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class XRepoManager { + private static final Logger log = LoggerFactory.getLogger(XRepoManager.class); + public static Map repositoryCache = new HashMap(); + public static Map databaseMeta = new ConcurrentHashMap(); + + public XRepoManager() { + } + public static List getAllDirectoryTreeList(String repoId, Repository repository, String path, List allRepositoryTreeList,String type) throws KettleException { + List repositoryTreeList = getJobAndTrans(repoId, repository, path,type); + if (repositoryTreeList.size() != 0) { + Iterator var5 = repositoryTreeList.iterator(); + + while(var5.hasNext()) { + RepositoryTree repositoryTree = (RepositoryTree)var5.next(); + if (!repositoryTree.isLasted()) { + getAllDirectoryTreeList(repoId, repository, repositoryTree.getPath(), allRepositoryTreeList,type); + allRepositoryTreeList.add(repositoryTree); + } else { + allRepositoryTreeList.add(repositoryTree); + } + } + } + + return allRepositoryTreeList; + } + public static List getJobAndTrans(String repoId, Repository repository, String path,String type) throws KettleException { + RepositoryDirectoryInterface rDirectory = repository.loadRepositoryDirectoryTree().findDirectory(path); + List repositoryTreeList = getDirectory(repoId, repository, rDirectory); + List li = repository.getJobAndTransformationObjects(rDirectory.getObjectId(), false); + if (null != li) { + Iterator var6 = li.iterator(); + + while(var6.hasNext()) { + RepositoryElementMetaInterface repel = (RepositoryElementMetaInterface)var6.next(); + RepositoryTree repositoryTree; + StringBuilder stringBuilder; + if ("job".equals(repel.getObjectType().toString()) && type.equals("job")) { + repositoryTree = new RepositoryTree(); + stringBuilder = new StringBuilder(); + stringBuilder.append("job").append(rDirectory.getObjectId().toString()).append("@").append(repel.getObjectId().toString()); + repositoryTree.setId(stringBuilder.toString()); + repositoryTree.setParent(repoId + "@" + rDirectory.getObjectId().toString()); + repositoryTree.setText(repel.getName()); + if (repository instanceof KettleDatabaseRepository) { + repositoryTree.setType(repoId + "@db@" + "job"); + } else if (repository instanceof KettleFileRepository) { + repositoryTree.setType(repoId + "@file@" + "job"); + } + + repositoryTree.setLasted(true); + repositoryTree.setPath(repel.getRepositoryDirectory().getPath()); + repositoryTreeList.add(repositoryTree); + } else if ("transformation".equals(repel.getObjectType().toString())&& type.equals("trans")) { + repositoryTree = new RepositoryTree(); + stringBuilder = new StringBuilder(); + stringBuilder.append("transformation").append(rDirectory.getObjectId().toString()).append("@").append(repel.getObjectId().toString()); + repositoryTree.setId(stringBuilder.toString()); + repositoryTree.setParent(repoId + "@" + rDirectory.getObjectId().toString()); + repositoryTree.setText(repel.getName()); + if (repository instanceof KettleDatabaseRepository) { + repositoryTree.setType(repoId + "@db@" + "transformation"); + } else if (repository instanceof KettleFileRepository) { + repositoryTree.setType(repoId + "@file@" + "transformation"); + } + + repositoryTree.setLasted(true); + repositoryTree.setPath(repel.getRepositoryDirectory().getPath()); + repositoryTreeList.add(repositoryTree); + } + } + } + + return repositoryTreeList; + } + private static List getDirectory(String repoId, Repository repository, RepositoryDirectoryInterface rDirectory) throws KettleException { + List repositoryTreeList = new ArrayList(); + if (null != repository && null != rDirectory) { + RepositoryDirectoryInterface tree = repository.loadRepositoryDirectoryTree().findDirectory(rDirectory.getObjectId()); + if (rDirectory.getNrSubdirectories() > 0) { + for(int i = 0; i < rDirectory.getNrSubdirectories(); ++i) { + RepositoryDirectory subTree = tree.getSubdirectory(i); + RepositoryTree repositoryTree = new RepositoryTree(); + repositoryTree.setId(repoId + "@" + subTree.getObjectId().toString()); + repositoryTree.setParent(repoId + "@" + rDirectory.getObjectId().toString()); + repositoryTree.setText(subTree.getName()); + repositoryTree.setPath(subTree.getPath()); + repositoryTree.setType("subTree"); + List RepositoryElementMetaInterfaceList = repository.getJobAndTransformationObjects(subTree.getObjectId(), false); + if (subTree.getNrSubdirectories() <= 0 && RepositoryElementMetaInterfaceList.size() <= 0) { + repositoryTree.setLasted(true); + } else { + repositoryTree.setLasted(false); + } + + repositoryTreeList.add(repositoryTree); + } + } + } + + return repositoryTreeList; + } + public static Repository createRep(BaseRepositoryMeta baseRepositoryMeta, String id) throws KettleException { + if (get(id) != null) { + return get(id); + } else { + Repository repo = null; + if (baseRepositoryMeta instanceof KettleDatabaseRepositoryMeta) { + repo = new KettleDatabaseRepository(); + ((Repository)repo).init((KettleDatabaseRepositoryMeta)baseRepositoryMeta); + } else { + repo = new KettleFileRepository(); + ((Repository)repo).init((KettleFileRepositoryMeta)baseRepositoryMeta); + } + + repositoryCache.put(id, repo); + log.info(((Repository)repo).getName() + "资源库初始化成功"); + return (Repository)repo; + } + } + + public static Repository createFileRep(String repoId, String repName, String description, String baseDirectory) throws KettleException { + if (!KettleEnvironment.isInitialized()) { + KettleEnvironment.init(); + } + + KettleFileRepositoryMeta fileRepMeta = new KettleFileRepositoryMeta(repoId, repName, description, baseDirectory); + return createRep(fileRepMeta, repoId); + } + private static Repository get(String repoId) { + return (Repository)repositoryCache.get(repoId); + } + /* public static String[] getDataBaseAccess() { + String[] dataBaseAccess = DatabaseMeta.dbAccessTypeCode; + return dataBaseAccess; + } + + public static DatabaseMeta createDatabaseMeta(String name, String type, String access, String host, String db, String port, String user, String pass, Map params, boolean replace, Repository repository) { + DatabaseMeta dm = null; + if (repository != null) { + try { + ObjectId dbId = repository.getDatabaseID(name); + if (dbId != null && !replace) { + dm = repository.loadDatabaseMeta(dbId, (String)null); + } else if (dbId != null && replace) { + repository.deleteDatabaseMeta(name); + } + } catch (KettleException var15) { + log.error("创建数据库元数据失败", var15); + } + } + + if (dm == null) { + dm = new DatabaseMeta(name, type, access, host, db, port, user, pass); + if (params != null) { + Iterator var13 = params.entrySet().iterator(); + + while(var13.hasNext()) { + Map.Entry ent = (Map.Entry)var13.next(); + dm.addExtraOption(type, (String)ent.getKey(), "" + ent.getValue()); + } + } + + dm.setForcingIdentifiersToLowerCase(true); + if (repository != null) { + try { + repository.save(dm, (String)null, (ProgressMonitorListener)null, true); + } catch (KettleException var14) { + log.error("保存数据库元数据失败", var14); + } + } + } + + if (!StringUtils.isEmpty(dm)) { + databaseMeta.put("databaseMeta", dm); + log.info("第一次连接,获取到资源库数据库信息,{}", JSON.toJSON(dm)); + } + + return dm; + } + + + + + + public static Repository createDBRepByJndi(String repoId, String name, String type, String db) throws KettleException { + return createBaseMetaRep(repoId, (String)null, (String)null, (JSONObject)null, name, type, DatabaseMeta.dbAccessTypeCode[4], (String)null, db, (String)null, (String)null, (String)null); + } + + public static Repository createDBRepByParams(String repoId, String repoName, JSONObject params, String databaseMetaName, String type, String access, String host, String db, String port, String user, String pass) throws KettleException { + return createBaseMetaRep(repoId, repoName, repoName, params, databaseMetaName, type, access, host, db, port, user, pass); + } + + public static Repository createDBRepByDesc(String repoId, String repoName, String description, String databaseMetaName, String type, String access, String host, String db, String port, String user, String pass) throws KettleException { + return createBaseMetaRep(repoId, repoName, description, (JSONObject)null, databaseMetaName, type, access, host, db, port, user, pass); + } + + public static Repository createBaseMetaRep(String repoId, String repoName, String description, JSONObject params, String databaseMetaName, String type, String access, String host, String db, String port, String user, String pass) throws KettleException { + DatabaseMeta dataMeta = createDatabaseMeta(databaseMetaName, type, access, host, db, port, user, pass, params, false, (Repository)null); + return createBaseRep(dataMeta, repoId, repoName, description); + } + + public static Repository createBaseRep(DatabaseMeta dataMeta, String repoId, String repoName, String description) throws KettleException { + KettleDatabaseRepositoryMeta kettleDatabaseMeta = new KettleDatabaseRepositoryMeta(repoId, repoName, description, dataMeta); + return createRep(kettleDatabaseMeta, repoId); + } + + public static KettleFileRepository createKFR(String id, String repName, String description, String baseDirectory) throws KettleException { + KettleFileRepositoryMeta kettleFileRepositoryMeta = (KettleFileRepositoryMeta)createFileRep(id, repName, repName + "文件资源库", baseDirectory); + KettleFileRepository kettleFileRepository = new KettleFileRepository(); + kettleFileRepository.init(kettleFileRepositoryMeta); + return kettleFileRepository; + } + + public static KettleDatabaseRepository createKDR(String name, String repoType, String dbAccess, String dbHost, String dbName, String dbPort, String dbUserName, String dbPass, String repoName, String repoId) throws KettleException { + createDatabaseMeta(name, repoType, dbAccess, dbHost, dbName, dbPort, dbUserName, dbPass, (Map)null, false, (Repository)null); + KettleDatabaseRepositoryMeta kettleDatabaseRepositoryMeta = (KettleDatabaseRepositoryMeta)createDBRepByDesc(repoId, repoName, repoName, name, repoType, dbAccess, dbHost, dbName, dbPort, dbUserName, dbPass); + KettleDatabaseRepository kettleDatabaseRepository = new KettleDatabaseRepository(); + kettleDatabaseRepository.init(kettleDatabaseRepositoryMeta); + return kettleDatabaseRepository; + } + + public static void disConnectionRepository(String id) { + if (repositoryCache.containsKey(id)) { + Repository repository = (Repository)repositoryCache.get(id); + repository.disconnect(); + repository.clearSharedObjectCache(); + repositoryCache.remove(id); + } + + } + + public static void destroyAll() { + repositoryCache.forEach((id, repository) -> { + repository.disconnect(); + repository.clearSharedObjectCache(); + }); + repositoryCache.clear(); + } + + + + + + */ +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleJobService.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleJobService.java new file mode 100644 index 000000000..83d83e014 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleJobService.java @@ -0,0 +1,73 @@ +package com.ruoyi.kettle.service; + +import java.util.List; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.kettle.domain.KettleJob; + +/** + * 作业调度Service接口 + * + * @author kone + * @date 2021-07-22 + */ +public interface IKettleJobService +{ + /** + * 查询作业调度 + * + * @param id 作业调度ID + * @return 作业调度 + */ + public KettleJob selectKettleJobById(Long id); + + /** + * 查询作业调度列表 + * + * @param kettleJob 作业调度 + * @return 作业调度集合 + */ + public List selectKettleJobList(KettleJob kettleJob); + + /** + * 新增作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + public AjaxResult insertKettleJob(KettleJob kettleJob); + + /** + * 修改作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + public int updateKettleJob(KettleJob kettleJob); + + /** + * 批量删除作业调度 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteKettleJobByIds(String ids); + + /** + * 删除作业调度信息 + * + * @param id 作业调度ID + * @return 结果 + */ + public int deleteKettleJobById(Long id); + + AjaxResult run(KettleJob job); + + List queryJobLog(KettleJob kettleJob); + + Long checkQuartzExist(String checkStr); + + public AjaxResult runJobQuartz(String id, String jobName); + + void runJobRightNow(Long valueOf, String userId); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleTransService.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleTransService.java new file mode 100644 index 000000000..3a950b7a4 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IKettleTransService.java @@ -0,0 +1,86 @@ +package com.ruoyi.kettle.service; + +import java.util.List; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.kettle.domain.KettleTrans; + +/** + * 转换Service接口 + * + * @author kone + * @date 2021-07-14 + */ +public interface IKettleTransService +{ + /** + * 查询转换 + * + * @param id 转换ID + * @return 转换 + */ + public KettleTrans selectKettleTransById(Long id); + + /** + * 查询转换列表 + * + * @param kettleTrans 转换 + * @return 转换集合 + */ + public List selectKettleTransList(KettleTrans kettleTrans); + + /** + * 新增转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + public AjaxResult insertKettleTrans(KettleTrans kettleTrans); + + /** + * 修改转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + public int updateKettleTrans(KettleTrans kettleTrans); + + /** + * 批量删除转换 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteKettleTransByIds(String ids); + + /** + * 删除转换信息 + * + * @param id 转换ID + * @return 结果 + */ + public int deleteKettleTransById(Long id); + /** + * @Description:立即执行一次转换 + * @Author: Kone.wang + * @Date: 2021/7/15 14:31 + * @param trans : + * @return: void + **/ + AjaxResult runToQueue(KettleTrans trans); + + List queryTransLog(KettleTrans trans) ; + /** + * @Description:设置定时执行转换 + * @Author: Kone.wang + * @Date: 2021/7/21 14:59 + * @param id: + * @param transName: + * @return: com.ruoyi.common.core.domain.AjaxResult + **/ + public AjaxResult runTransQuartz(String id,String transName); + + Long checkQuartzExist(String checkStr); + + void runTransRightNow(Long valueOf, String userId); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/IXRepositoryService.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IXRepositoryService.java new file mode 100644 index 000000000..eca79054a --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/IXRepositoryService.java @@ -0,0 +1,69 @@ +package com.ruoyi.kettle.service; + +import java.util.List; + +import com.ruoyi.common.core.domain.Ztree; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.kettle.domain.XRepository; +import com.ruoyi.kettle.repo.RepoTree; + +/** + * 资源库Service接口 + * + * @author kone + * @date 2021-07-12 + */ +public interface IXRepositoryService +{ + /** + * 查询资源库 + * + * @param id 资源库ID + * @return 资源库 + */ + public XRepository selectXRepositoryById(Long id); + + /** + * 查询资源库列表 + * + * @param xRepository 资源库 + * @return 资源库集合 + */ + public List selectXRepositoryList(XRepository xRepository); + + /** + * 新增资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + public int insertXRepository(XRepository xRepository); + + /** + * 修改资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + public int updateXRepository(XRepository xRepository); + + /** + * 批量删除资源库 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteXRepositoryByIds(String ids); + + /** + * 删除资源库信息 + * + * @param id 资源库ID + * @return 结果 + */ + public int deleteXRepositoryById(Long id); + + List selectRepoTree(Long id,String type); + + List selectRepoRoot(XRepository repository); +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleJobServiceImpl.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleJobServiceImpl.java new file mode 100644 index 000000000..07e0d1b46 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleJobServiceImpl.java @@ -0,0 +1,251 @@ +package com.ruoyi.kettle.service.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.security.PermissionUtils; +import com.ruoyi.kettle.domain.XRepository; +import com.ruoyi.kettle.mapper.XRepositoryMapper; +import com.ruoyi.kettle.tools.KettleUtil; +import com.ruoyi.kettle.tools.RedisStreamUtil; +import com.ruoyi.system.service.IWechatApiService; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.kettle.mapper.KettleJobMapper; +import com.ruoyi.kettle.domain.KettleJob; +import com.ruoyi.kettle.service.IKettleJobService; +import com.ruoyi.common.core.text.Convert; + +/** + * 作业调度Service业务层处理 + * + * @author kone + * @date 2021-07-22 + */ +@Service("kettleJobServiceImpl") +public class KettleJobServiceImpl implements IKettleJobService +{ + private static final Logger log = LoggerFactory.getLogger(KettleJobServiceImpl.class); + @Autowired + private KettleJobMapper kettleJobMapper; + @Autowired + private XRepositoryMapper repositoryMapper; + + + @Autowired + private KettleUtil kettleUtil; + + @Autowired + private RedisStreamUtil redisStreamUtil; + @Autowired + IWechatApiService wechatApiService; + /** + * 查询作业调度 + * + * @param id 作业调度ID + * @return 作业调度 + */ + @Override + public KettleJob selectKettleJobById(Long id) + { + return kettleJobMapper.selectKettleJobById(id); + } + + /** + * 查询作业调度列表 + * + * @param kettleJob 作业调度 + * @return 作业调度 + */ + @Override + public List selectKettleJobList(KettleJob kettleJob) + { + Object o=PermissionUtils.getPrincipalProperty("roles"); + List roleList=new ArrayList<>(); + // roleList= (List) PermissionUtils.getPrincipalProperty("roles"); + if(o != null && o instanceof List){ + for(Object r:(List)o){ + roleList.add(SysRole.class.cast(r)); + } + } //当前用户的roleKey + List roleKeys=roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + return kettleJobMapper.selectKettleJobList(kettleJob,roleKeys); + } + + /** + * 新增作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + @Override + public AjaxResult insertKettleJob(KettleJob kettleJob) + { + String jobName=kettleJob.getJobName(); + if(kettleJobMapper.selectJobByNameAndRepoId(jobName,kettleJob.getJobRepositoryId())>0){ + return AjaxResult.error("已存在同名作业"); + } + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + if(kettleJob.getRoleKey()==null){ + kettleJob.setRoleKey("admin,bpsadmin"); + }else{ + if(!kettleJob.getRoleKey().contains("admin")){ + kettleJob.setRoleKey(kettleJob.getRoleKey().concat(",admin")); + } + if(!kettleJob.getRoleKey().contains("bpsadmin")){ + kettleJob.setRoleKey(kettleJob.getRoleKey().concat(",bpsadmin")); + } + } + kettleJob.setCreatedBy(userName); + kettleJob.setUpdateBy(userName); + kettleJob.setJobType("File"); + return AjaxResult.success(kettleJobMapper.insertKettleJob(kettleJob)); + } + + /** + * 修改作业调度 + * + * @param kettleJob 作业调度 + * @return 结果 + */ + @Override + public int updateKettleJob(KettleJob kettleJob) + { + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + + kettleJob.setUpdateTime(DateUtils.getNowDate()); + kettleJob.setUpdateBy(userName); + kettleJob.setJobType("File"); + if(kettleJob.getRoleKey()==null){ + kettleJob.setRoleKey("admin,bpsadmin"); + }else{ + if(!kettleJob.getRoleKey().contains("admin")){ + kettleJob.setRoleKey(kettleJob.getRoleKey().concat(",admin")); + } + if(!kettleJob.getRoleKey().contains("bpsadmin")){ + kettleJob.setRoleKey(kettleJob.getRoleKey().concat(",bpsadmin")); + } + } + return kettleJobMapper.updateKettleJob(kettleJob); + } + + /** + * 删除作业调度对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteKettleJobByIds(String ids) + { + return kettleJobMapper.deleteKettleJobByIds(Convert.toStrArray(ids)); + } + + /** + * 删除作业调度信息 + * + * @param id 作业调度ID + * @return 结果 + */ + @Override + public int deleteKettleJobById(Long id) + { + return kettleJobMapper.deleteKettleJobById(id); + } + + @Override + public AjaxResult run(KettleJob job) { + Long id = job.getId(); + KettleJob kettleJob = kettleJobMapper.selectKettleJobById(id); + if(kettleJob ==null){ + return AjaxResult.error("作业不存在!"); + } + XRepository repository=repositoryMapper.selectXRepositoryById(kettleJob.getJobRepositoryId()); + if(repository==null){ + return AjaxResult.error("资源库不存在!"); + } + //加入队列中,等待执行 + redisStreamUtil.addKettleJob(kettleJob); + //更新一下状态 + kettleJob.setJobStatus("等待中"); + kettleJobMapper.updateKettleJob(kettleJob); + return AjaxResult.success("已加入执行队列,请等待运行结果通知!"); +// String path = kettleJob.getJobPath(); +// try { +// kettleUtil.KETTLE_LOG_LEVEL=kettleJob.getJobLogLevel(); +// kettleUtil.KETTLE_REPO_ID=String.valueOf(kettleJob.getJobRepositoryId()); +// kettleUtil.KETTLE_REPO_NAME=repository.getRepoName(); +// kettleUtil.KETTLE_REPO_PATH=repository.getBaseDir(); +// kettleUtil.callJob(path,kettleJob.getJobName(),null,null); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// + + } + + @Override + public void runJobRightNow(Long id, String userId) { + if(userId.equals("1")){ + userId="408"; + } + KettleJob kettleJob = kettleJobMapper.selectKettleJobById(id); + if(kettleJob ==null){ + log.error("作业不存在!"); + return; + } + XRepository repository=repositoryMapper.selectXRepositoryById(kettleJob.getJobRepositoryId()); + if(repository==null){ + log.error("资源库不存在!"); + return; + } + List userIdList = new ArrayList<>(); + //更新一下状态 + kettleJob.setJobStatus("运行中"); + kettleJobMapper.updateKettleJob(kettleJob); + StringBuilder title = new StringBuilder(kettleJob.getJobName()).append(".kjb 执行结果:"); + StringBuilder msg = new StringBuilder(kettleJob.getJobName()).append(".kjb 描述:").append(kettleJob.getJobDescription()); + try { + kettleUtil.callJob(kettleJob,repository,null,null); + kettleJob.setJobStatus("成功"); + kettleJob.setLastSucceedTime(DateUtils.getNowDate()); + kettleJobMapper.updateKettleJob(kettleJob); + title.append("成功!"); + } catch (Exception e) { + kettleJob.setJobStatus("异常"); + kettleJobMapper.updateKettleJob(kettleJob); + title.append("异常!"); + log.error(id+"的job执行失败:"+e.getMessage()); + if(!userId.equals("408")){ + userIdList.add("408"); + } + } + userIdList.add(userId); + Map resultMap = wechatApiService.SendTextCardMessageToWechatUser(userIdList,title.toString(),msg.toString(),"http://report.bpsemi.cn:8081/it_war"); + log.info("job微信消息发送结果"+resultMap); + } + @Override + public List queryJobLog(KettleJob kettleJob) { + List logs=kettleJobMapper.queryJobLog(kettleJob.getJobName()); + return logs; + } + + @Override + public Long checkQuartzExist(String checkStr) { + return kettleJobMapper.checkQuartzExist(checkStr); + } + @Override + public AjaxResult runJobQuartz(String id, String jobName) { + KettleJob kettleJob = kettleJobMapper.selectKettleJobById(Long.valueOf(id)); + return run(kettleJob); + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleTransServiceImpl.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleTransServiceImpl.java new file mode 100644 index 000000000..3b1e5e36f --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/KettleTransServiceImpl.java @@ -0,0 +1,275 @@ +package com.ruoyi.kettle.service.impl; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.security.PermissionUtils; +import com.ruoyi.kettle.domain.XRepository; +import com.ruoyi.kettle.mapper.XRepositoryMapper; +import com.ruoyi.kettle.service.IKettleTransService; +import com.ruoyi.kettle.tools.KettleUtil; +import com.ruoyi.kettle.tools.RedisStreamUtil; +import com.ruoyi.system.service.IWechatApiService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.kettle.mapper.KettleTransMapper; +import com.ruoyi.kettle.domain.KettleTrans; +import com.ruoyi.common.core.text.Convert; + +/** + * 转换Service业务层处理 + * + * @author kone + * @date 2021-07-14 + */ +@Service("kettleTransServiceImpl") +public class KettleTransServiceImpl implements IKettleTransService +{ + + private static final Logger log = LoggerFactory.getLogger(KettleTransServiceImpl.class); + @Autowired + private KettleTransMapper kettleTransMapper; + @Autowired + private XRepositoryMapper repositoryMapper; + + @Autowired + private KettleUtil kettleUtil; + + @Autowired + private RedisStreamUtil redisStreamUtil; + @Autowired + IWechatApiService wechatApiService; + /** + * 查询转换 + * + * @param id 转换ID + * @return 转换 + */ + @Override + public KettleTrans selectKettleTransById(Long id) + { + return kettleTransMapper.selectKettleTransById(id); + } + + /** + * 查询转换列表 + * + * @param kettleTrans 转换 + * @return 转换 + */ + @Override + public List selectKettleTransList(KettleTrans kettleTrans) + { + Object o=PermissionUtils.getPrincipalProperty("roles"); + List roleList=new ArrayList<>(); + // roleList= (List) PermissionUtils.getPrincipalProperty("roles"); + if(o != null && o instanceof List){ + for(Object r:(List)o){ + roleList.add(SysRole.class.cast(r)); + } + } + + //当前用户的roleKey + List roleKeys=roleList.stream().map(SysRole::getRoleKey).collect(Collectors.toList()); + + return kettleTransMapper.selectKettleTransList(kettleTrans,roleKeys); + } + + /** + * 新增转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + @Override + public AjaxResult insertKettleTrans(KettleTrans kettleTrans) + { + String transName=kettleTrans.getTransName(); + if(kettleTransMapper.selectKettleTransByTransName(transName)>0){ + return AjaxResult.error("已存在同名转换"); + } + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + if(kettleTrans.getRoleKey()==null){ + kettleTrans.setRoleKey("admin,bpsadmin"); + }else{ + if(!kettleTrans.getRoleKey().contains("admin")){ + kettleTrans.setRoleKey(kettleTrans.getRoleKey().concat(",admin")); + } + if(!kettleTrans.getRoleKey().contains("bpsadmin")){ + kettleTrans.setRoleKey(kettleTrans.getRoleKey().concat(",bpsadmin")); + } + } + kettleTrans.setCreatedBy(userName); + kettleTrans.setUpdateBy(userName); + kettleTrans.setTransType("File"); + return AjaxResult.success(kettleTransMapper.insertKettleTrans(kettleTrans)); + } + + /** + * 修改转换 + * + * @param kettleTrans 转换 + * @return 结果 + */ + @Override + public int updateKettleTrans(KettleTrans kettleTrans) + { + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + kettleTrans.setUpdateBy(userName); + kettleTrans.setUpdateTime(DateUtils.getNowDate()); + kettleTrans.setTransType("File"); + if(kettleTrans.getRoleKey()==null){ + kettleTrans.setRoleKey("admin,bpsadmin"); + }else{ + if(!kettleTrans.getRoleKey().contains("admin")){ + kettleTrans.setRoleKey(kettleTrans.getRoleKey().concat(",admin")); + } + if(!kettleTrans.getRoleKey().contains("bpsadmin")){ + kettleTrans.setRoleKey(kettleTrans.getRoleKey().concat(",bpsadmin")); + } + } return kettleTransMapper.updateKettleTrans(kettleTrans); + } + + /** + * 删除转换对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteKettleTransByIds(String ids) + { + return kettleTransMapper.deleteKettleTransByIds(Convert.toStrArray(ids)); + } + + /** + * 删除转换信息 + * + * @param id 转换ID + * @return 结果 + */ + @Override + public int deleteKettleTransById(Long id) + { + return kettleTransMapper.deleteKettleTransById(id); + } + + + /** + * @Description:立即执行一次转换,放到redis队列中 + * @Author: Kone.wang + * @Date: 2021/7/15 14:31 + * @param trans : + * @return: void + **/ + @Override + public AjaxResult runToQueue(KettleTrans trans) { + Long id = trans.getId(); + KettleTrans kettleTrans = kettleTransMapper.selectKettleTransById(id); + if(kettleTrans ==null || kettleTrans.getId()==null){ + return AjaxResult.error("转换不存在!"); + } + XRepository repository=repositoryMapper.selectXRepositoryById(kettleTrans.getTransRepositoryId()); + if(repository==null){ + return AjaxResult.error("资源库不存在!"); + } + File file=new File(repository.getBaseDir()+kettleTrans.getTransPath()+kettleTrans.getTransName()+".ktr"); + if(!file.exists()){ + return AjaxResult.error(file.getPath()+"未找到文件!"); + } + //加入队列中,等待执行 + redisStreamUtil.addKettleTrans(kettleTrans); + //更新一下状态 + trans.setTransStatus("等待中"); + kettleTransMapper.updateKettleTrans(trans); + return AjaxResult.success("已加入执行队列,请等待运行结果通知!"); + } + + @Override + public void runTransRightNow(Long id, String userId) { + if(userId.equals("1")){ + userId="408"; + } + KettleTrans kettleTrans = kettleTransMapper.selectKettleTransById(id); + if(kettleTrans ==null || kettleTrans.getId()==null){ + log.error("转换不存在!:"+id); + return; + } + XRepository repository=repositoryMapper.selectXRepositoryById(kettleTrans.getTransRepositoryId()); + if(repository==null){ + log.error("资源库不存在!"); + return; + } + //更新状态未运行中 + kettleTrans.setTransStatus("运行中"); + kettleTransMapper.updateKettleTrans(kettleTrans); + StringBuilder title = new StringBuilder(kettleTrans.getTransName()).append(".ktr 执行结果:"); + StringBuilder msg = new StringBuilder(kettleTrans.getTransName()).append(":描述:").append(kettleTrans.getTransDescription()); + List userIdList = new ArrayList<>(); + try { + kettleUtil.callTrans(kettleTrans,repository,null,null); + kettleTrans.setTransStatus("成功"); + kettleTrans.setLastSucceedTime(DateUtils.getNowDate()); + kettleTransMapper.updateKettleTrans(kettleTrans); + title.append("成功!"); + } catch (Exception e) { + kettleTrans.setTransStatus("异常"); + kettleTransMapper.updateKettleTrans(kettleTrans); + title.append("异常!"); + log.error(id+"的trans执行失败:"+e.getMessage()); + if(!userId.equals("408")){ + userIdList.add("408"); + } + } + userIdList.add(userId); + Map resultMap = wechatApiService.SendTextCardMessageToWechatUser(userIdList,title.toString(),msg.toString(),"http://report.bpsemi.cn:8081/it_war"); + log.info("trans微信消息发送结果"+resultMap); + + } + /** + * @Description:查询抓换执行日志 + * @Author: Kone.wang + * @Date: 2021/7/28 16:24 + * @param kettleTrans: + * @return: java.util.List + **/ + @Override + public List queryTransLog(KettleTrans kettleTrans) { + List transLogs=kettleTransMapper.queryTransLog(kettleTrans.getTransName()); + return transLogs; + } + /** + * @Description:设置定时执行转换 + * @Author: Kone.wang + * @Date: 2021/7/21 14:59 + * @param id: + * @param transName: + * @return: com.ruoyi.common.core.domain.AjaxResult + **/ + @Override + public AjaxResult runTransQuartz(String id, String transName) { + KettleTrans kettleTrans = kettleTransMapper.selectKettleTransById(Long.valueOf(id)); + return runToQueue(kettleTrans); + } + /** + * @Description:检查该转换是否设置了定时任务 + * @Author: Kone.wang + * @Date: 2021/7/21 16:37 + * @param checkStr: + * @return: int + **/ + @Override + public Long checkQuartzExist(String checkStr) { + + return kettleTransMapper.checkQuartzExist(checkStr); + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/XRepositoryServiceImpl.java b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/XRepositoryServiceImpl.java new file mode 100644 index 000000000..d103612c3 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/service/impl/XRepositoryServiceImpl.java @@ -0,0 +1,249 @@ +package com.ruoyi.kettle.service.impl; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.List; + +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.security.PermissionUtils; +import com.ruoyi.kettle.repo.RepoTree; +import com.ruoyi.kettle.repo.RepositoryTree; +import com.ruoyi.kettle.repo.XRepoManager; +import com.ruoyi.kettle.tools.KettleUtil_2; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.repository.RepositoryDirectoryInterface; +import org.pentaho.di.repository.filerep.KettleFileRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import com.ruoyi.kettle.mapper.XRepositoryMapper; +import com.ruoyi.kettle.domain.XRepository; +import com.ruoyi.kettle.service.IXRepositoryService; +import com.ruoyi.common.core.text.Convert; +import org.springframework.util.CollectionUtils; + +/** + * 资源库Service业务层处理 + * + * @author kone + * @date 2021-07-12 + */ +@Service +public class XRepositoryServiceImpl implements IXRepositoryService +{ + @Autowired + private XRepositoryMapper xRepositoryMapper; + + /** + * 查询资源库 + * + * @param id 资源库ID + * @return 资源库 + */ + @Override + public XRepository selectXRepositoryById(Long id) + { + return xRepositoryMapper.selectXRepositoryById(id); + } + + /** + * 查询资源库列表 + * + * @param xRepository 资源库 + * @return 资源库 + */ + @Override + public List selectXRepositoryList(XRepository xRepository) + { + return xRepositoryMapper.selectXRepositoryList(xRepository); + } + + /** + * 新增资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + @Override + public int insertXRepository(XRepository xRepository) + { + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + xRepository.setCreatedBy(userName); + xRepository.setUpdateBy(userName); + xRepository.setType("File"); + return xRepositoryMapper.insertXRepository(xRepository); + } + + /** + * 修改资源库 + * + * @param xRepository 资源库 + * @return 结果 + */ + @Override + public int updateXRepository(XRepository xRepository) + { + String userName = (String) PermissionUtils.getPrincipalProperty("userName"); + xRepository.setUpdateTime(DateUtils.getNowDate()); + xRepository.setUpdateBy(userName); + + return xRepositoryMapper.updateXRepository(xRepository); + } + + /** + * 删除资源库对象 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + @Override + public int deleteXRepositoryByIds(String ids) + { + return xRepositoryMapper.updateIsDelBatch(Convert.toStrArray(ids)); + // return xRepositoryMapper.deleteXRepositoryByIds(Convert.toStrArray(ids)); + } + + /** + * 删除资源库信息 + * + * @param id 资源库ID + * @return 结果 + */ + @Override + public int deleteXRepositoryById(Long id) + { + return xRepositoryMapper.updateIsDel(id); + //return xRepositoryMapper.deleteXRepositoryById(id); + } + + @Override + public List selectRepoRoot(XRepository repository) { + List repositoryList = xRepositoryMapper.selectXRepositoryList(repository); + List ztrees = initZtree2(repositoryList); + return ztrees; + } + + @Override + public List selectRepoTree(Long id,String type) { + XRepository xrs = xRepositoryMapper.selectXRepositoryById(id); + List repositoryTrees = getRepoTress(xrs,type); + List subTrees = new ArrayList<>(); + String pId=String.valueOf(xrs.getId()); +// try +// { + +// repositoryTrees.forEach(item -> { +// if (item.getParent().equals(pId)) { +// if (item.isLasted()) { +// if (!StringUtils.isEmpty(type)) { +// if (item.getType().indexOf(type) != -1) { +// subTrees.add(item); +// } +// } else { +// subTrees.add(item); +// } +// +// } else { +// subTrees.add(item); +// } +// }}); +// }catch (Exception e) +// { +// StringWriter sw = new StringWriter(); +// e.printStackTrace(new PrintWriter(sw)); +// //throw new UserDefinedException(BaseResultConstant.UNKNOW_EXCEPTION, sw.toString().substring(0, 800)); +// } + + List ztrees = initZtree(repositoryTrees,String.valueOf(id)); + return ztrees; + } + + + public List initZtree(List repositoryList ,String parentId) + { + + List ztrees = new ArrayList(); + for (RepositoryTree rt : repositoryList) { + if(rt.getId().equals(parentId) || rt.getText().equals("/")){ + continue; + } + RepoTree ztree = new RepoTree(); + ztree.setId(rt.getId()); + ztree.setpId(rt.getParent()); + ztree.setName(rt.getText()); + ztree.setTitle(rt.getPath()); + ztrees.add(ztree); + } + return ztrees; + } + public List initZtree2(List repositoryList ) + { + + List ztrees = new ArrayList(); + for (XRepository rt : repositoryList) + { + RepoTree ztree = new RepoTree(); + ztree.setId(String.valueOf(rt.getId())); + ztree.setpId(" "); + ztree.setName(rt.getRepoName()); + ztree.setTitle(rt.getBaseDir()); + ztrees.add(ztree); + } + return ztrees; + } + private List getRepoTress(XRepository xr,String jobortrans) { + List repositoryTrees = new ArrayList<>(); + List xRepositoryList =xRepositoryMapper.selectXRepositoryList(xr); + + if (!CollectionUtils.isEmpty(xRepositoryList)) { + xRepositoryList.forEach(item -> { + List tmpRepositoryList = new ArrayList<>(); + String type = item.getType(); + + if (type.equalsIgnoreCase("File")) { + // 文件库 + String baseDir = item.getBaseDir(); + + try { + KettleFileRepository repository = (KettleFileRepository) KettleUtil_2. + conFileRep(String.valueOf(item.getId()), item.getRepoName(), baseDir); + XRepoManager.getAllDirectoryTreeList(String.valueOf(item.getId()), repository, "/", tmpRepositoryList,jobortrans); + if (tmpRepositoryList.size() > 0) { + RepositoryDirectoryInterface rDirectory = repository.loadRepositoryDirectoryTree().findDirectory("/"); + RepositoryTree repositoryTree = new RepositoryTree(); + repositoryTree.setParent(String.valueOf(item.getId())); + repositoryTree.setId(item.getRepoId() + "@" + rDirectory.getObjectId().toString()); + //repositoryTree.setId(String.valueOf(item.getId())); + + repositoryTree.setText(rDirectory.getName().equals("\\/") ? "基础路径" : rDirectory.getName()); + repositoryTree.setLasted(false); + repositoryTree.setType("tree"); + repositoryTree.setPath("file"); + tmpRepositoryList.add(repositoryTree); + } + + } catch (KettleException e) { + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + // throw new UserDefinedException(BaseResultConstant.UNKNOW_EXCEPTION, sw.toString().substring(0, 800)); + } + } + + +// RepositoryTree repositoryTree; +// repositoryTree = new RepositoryTree(); +// repositoryTree.setParent("99"); +// repositoryTree.setId(String.valueOf(item.getId())); +// repositoryTree.setText(item.getRepoName()); +// repositoryTree.setLasted(false); +// repositoryTree.setType(type); +// repositoryTree.setPath("repo"); +// tmpRepositoryList.add(repositoryTree); + repositoryTrees.addAll(tmpRepositoryList); + }); + } + + return repositoryTrees; + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/CommandLineRunnerImpl.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/CommandLineRunnerImpl.java new file mode 100644 index 000000000..4790e04a2 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/CommandLineRunnerImpl.java @@ -0,0 +1,27 @@ +package com.ruoyi.kettle.tools; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +@Component +public class CommandLineRunnerImpl implements CommandLineRunner { + + @Autowired + private RedisStreamUtil redisStreamUtil; + + + @Override + public void run(String... args) throws Exception { + + new Thread(){ + public void run() { + redisStreamUtil.readGroup(); + } + }.start(); + } + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/Constant.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/Constant.java new file mode 100644 index 000000000..cad202178 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/Constant.java @@ -0,0 +1,151 @@ +package com.ruoyi.kettle.tools; + + +import org.apache.commons.lang.StringUtils; +import org.pentaho.di.core.Const; +import org.pentaho.di.core.logging.LogLevel; + +import java.io.FileInputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +public class Constant extends Const { + public static final String VERSION = "7.1.0.0-12"; + public static final String DEFAULT_ENCODING = "UTF-8"; + public static final String DEFAULT_TIMEZONE = "GMT+8"; + public static final String UKETTLE = "resource/xtl.properties"; + public static final String VARIABLE_JOB_ID = "GLOBAL_JOB_ID"; + public static final String VARIABLE_TRANS_ID = "GLOBAL_TRANS_ID"; + public static final String VARIABLE_JOB_MONITOR_ID = "GLOBAL_JOB_MONITOR_ID"; + public static final String VARIABLE_TRANS_MONITOR_ID = "GLOBAL_TRANS_MONITOR_ID"; + public static final String DEFAULT_REPO_ID = "1326379690046259200"; + public static final String TYPE_JOB = "job"; + public static final String TYPE_TRANS = "transformation"; + public static final String JOB_FILE_TYPE = "file"; + public static final String JOB_REPO_TYPE = "db"; + public static final String TRANS_FILE_TYPE = "file"; + public static final String TRANS_REPO_TYPE = "db"; + public static final String TYPE_JOB_SUFFIX = ".kjb"; + public static final String TYPE_TRANS_SUFFIX = ".ktr"; + public static final String STARTS_WITH_USD = "$"; + public static final String STARTS_WITH_PARAM = "-param:"; + public static final String SPLIT_PARAM = "-param:"; + public static final String SPLIT_EQUAL = "="; + public static final String SPLIT_USD = "$"; + public static final String KETTLE_REPO = "repo"; + public static final String JOB_PREFIX = "JOB"; + public static final String JOB_GROUP_PREFIX = "JOB_GROUP"; + public static final String TRIGGER_PREFIX = "TRIGGER"; + public static final String TRIGGER_GROUP_PREFIX = "TRIGGER_GROUP"; + public static final String QUARTZ_SEPARATE = "@"; + public static final String RUNSTATUS_SEPARATE = "-"; + public static String KETTLE_HOME; + public static String KETTLE_PLUGIN; + public static String KETTLE_SCRIPT; + public static LogLevel KETTLE_LOGLEVEL; + public static Properties props; + + public Constant() { + } + + public static String get(String key) { + return props.getProperty(key); + } + + public static void set(Properties p) { + props = p; + } + + public static Properties readProperties() { + Properties p = new Properties(); + + try { + p.load(new FileInputStream(Constant.class.getResource("/").getPath().replace("%20", " ") + "resource/xtl.properties")); + } catch (Exception var2) { + var2.printStackTrace(); + } + + return p; + } + + public static LogLevel logger(int level) { + LogLevel logLevel = null; + if ("3".equals(level + "")) { + logLevel = LogLevel.BASIC; + } else if ("4".equals(level + "")) { + logLevel = LogLevel.DETAILED; + } else if ("1".equals(level + "")) { + logLevel = LogLevel.ERROR; + } else if ("5".equals(level + "")) { + logLevel = LogLevel.DEBUG; + } else if ("2".equals(level + "")) { + logLevel = LogLevel.MINIMAL; + } else if ("6".equals(level + "")) { + logLevel = LogLevel.ROWLEVEL; + } else if ("0".endsWith(level + "")) { + logLevel = LogLevel.NOTHING; + } else { + logLevel = KETTLE_LOGLEVEL; + } + + return logLevel; + } + + public static LogLevel logger(String code) { + LogLevel logLevel = null; + if ("Basic".equalsIgnoreCase(code)) { + logLevel = LogLevel.BASIC; + } else if ("Detail".equalsIgnoreCase(code)) { + logLevel = LogLevel.DETAILED; + } else if ("Error".equalsIgnoreCase(code)) { + logLevel = LogLevel.ERROR; + } else if ("Debug".equalsIgnoreCase(code)) { + logLevel = LogLevel.DEBUG; + } else if ("Minimal".equalsIgnoreCase(code)) { + logLevel = LogLevel.MINIMAL; + } else if ("Rowlevel".equalsIgnoreCase(code)) { + logLevel = LogLevel.ROWLEVEL; + } else if ("Nothing".equalsIgnoreCase(code)) { + logLevel = LogLevel.NOTHING; + } + + return logLevel; + } + + private static String uKettle() { + String classPath = Constant.class.getResource("/").getPath().replace("%20", " "); + String iQuartz = ""; + String index = "WEB-INF"; + if (classPath.indexOf("target") > 0) { + index = "target"; + } + + if ("\\".equals(FILE_SEPARATOR)) { + iQuartz = classPath.substring(1, classPath.indexOf(index)); + iQuartz = iQuartz.replace("/", "\\"); + } + + if ("/".equals(FILE_SEPARATOR)) { + iQuartz = classPath.substring(0, classPath.indexOf(index)); + iQuartz = iQuartz.replace("\\", "/"); + } + + return iQuartz; + } + + public static Map getQuartzBasic(String name, String path) { + Map quartzBasic = new HashMap(); + StringBuilder jobName = new StringBuilder(); + jobName.append("JOB").append("@").append(name).append("@").append(path); + StringBuilder jobGroupName = new StringBuilder(); + jobGroupName.append("JOB_GROUP").append("@").append("@").append(name).append("@").append(path); + String triggerName = StringUtils.replace(jobName.toString(), "JOB", "TRIGGER"); + String triggerGroupName = StringUtils.replace(jobGroupName.toString(), "JOB_GROUP", "TRIGGER_GROUP"); + quartzBasic.put("jobName", jobName.toString()); + quartzBasic.put("jobGroupName", jobGroupName.toString()); + quartzBasic.put("triggerName", triggerName); + quartzBasic.put("triggerGroupName", triggerGroupName); + return quartzBasic; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/DateHelper.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/DateHelper.java new file mode 100644 index 000000000..9ee790282 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/DateHelper.java @@ -0,0 +1,16 @@ +package com.ruoyi.kettle.tools; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DateHelper { + public DateHelper() { + } + + public static String format(Date date) { + String strDate = ""; + SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss.s"); + strDate = formatter.format(date); + return strDate; + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/GenCodeUtil.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/GenCodeUtil.java new file mode 100644 index 000000000..5fda8aab4 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/GenCodeUtil.java @@ -0,0 +1,108 @@ +package com.ruoyi.kettle.tools; + + +import java.lang.management.ManagementFactory; +import java.net.InetAddress; +import java.net.NetworkInterface; + +public class GenCodeUtil { + private static final long TWEPOCH = 1288834974657L; + private static final long WORKER_ID_BITS = 5L; + private static final long DATACENTER_ID_BITS = 5L; + private static final long MAX_WORKER_ID = 31L; + private static final long MAX_DATACENTER_ID = 31L; + private static final long SEQUENCE_BITS = 12L; + private static final long WORKER_ID_SHIFT = 12L; + private static final long DATACENTER_ID_SHIFT = 17L; + private static final long TIMESTAMP_LEFT_SHIFT = 22L; + private static final long SEQUENCE_MASK = 4095L; + private static long lastTimestamp = -1L; + private static long sequence = 0L; + private static long workerId; + private static long datacenterId; + + public GenCodeUtil() { + datacenterId = getDatacenterId(31L); + workerId = getMaxWorkerId(datacenterId, 31L); + } + + public GenCodeUtil(long workerId, long datacenterId) { + if (workerId <= 31L && workerId >= 0L) { + if (datacenterId <= 31L && datacenterId >= 0L) { + GenCodeUtil.workerId = workerId; + GenCodeUtil.datacenterId = datacenterId; + } else { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", 31L)); + } + } else { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", 31L)); + } + } + + public static synchronized String nextId() { + long timestamp = timeGen(); + if (timestamp < lastTimestamp) { + throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } else { + if (lastTimestamp == timestamp) { + sequence = sequence + 1L & 4095L; + if (sequence == 0L) { + timestamp = tilNextMillis(lastTimestamp); + } + } else { + sequence = 0L; + } + + lastTimestamp = timestamp; + long nextId = timestamp - 1288834974657L << 22 | datacenterId << 17 | workerId << 12 | sequence; + return String.valueOf(nextId); + } + } + + private static long tilNextMillis(long lastTimestamp) { + long timestamp; + for(timestamp = timeGen(); timestamp <= lastTimestamp; timestamp = timeGen()) { + } + + return timestamp; + } + + private static long timeGen() { + return System.currentTimeMillis(); + } + + protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) { + StringBuffer mpid = new StringBuffer(); + mpid.append(datacenterId); + String name = ManagementFactory.getRuntimeMXBean().getName(); + if (!name.isEmpty()) { + mpid.append(name.split("@")[0]); + } + + return (long)(mpid.toString().hashCode() & '\uffff') % (maxWorkerId + 1L); + } + + protected static long getDatacenterId(long maxDatacenterId) { + long id = 0L; + + try { + InetAddress ip = InetAddress.getLocalHost(); + NetworkInterface network = NetworkInterface.getByInetAddress(ip); + if (network == null) { + id = 1L; + } else { + byte[] mac = network.getHardwareAddress(); + id = (255L & (long)mac[mac.length - 1] | 65280L & (long)mac[mac.length - 2] << 8) >> 6; + id %= maxDatacenterId + 1L; + } + } catch (Exception var7) { + System.out.println(" getDatacenterId: " + var7.getMessage()); + } + + return id; + } + + public static void main(String[] args) { + System.out.println(nextId()); + } +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil.java new file mode 100644 index 000000000..08424a27f --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil.java @@ -0,0 +1,324 @@ +package com.ruoyi.kettle.tools; + +import com.ruoyi.common.config.datasource.DynamicDataSourceContextHolder; +import com.ruoyi.kettle.domain.KettleJob; +import com.ruoyi.kettle.domain.KettleTrans; +import com.ruoyi.kettle.domain.XRepository; +import org.pentaho.di.core.KettleEnvironment; +import org.pentaho.di.core.database.DatabaseMeta; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.logging.*; +import org.pentaho.di.core.util.EnvUtil; +import org.pentaho.di.core.variables.VariableSpace; +import org.pentaho.di.core.variables.Variables; +import org.pentaho.di.job.Job; +import org.pentaho.di.job.JobMeta; +import org.pentaho.di.repository.RepositoryDirectoryInterface; +import org.pentaho.di.repository.filerep.KettleFileRepository; +import org.pentaho.di.repository.filerep.KettleFileRepositoryMeta; +import org.pentaho.di.trans.Trans; +import org.pentaho.di.trans.TransMeta; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; + +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +@Component +public class KettleUtil { + public static final Logger log = LoggerFactory.getLogger(KettleUtil.class); + + + + + /** + * 执行文件资源库转换 + * @param namedParams 命名参数 + * @param clParams 命令行参数 + */ + public void callTrans(KettleTrans kettleTrans, XRepository xrepository, Map namedParams, String[] clParams) throws Exception { + KettleEnv.init(); + DatabaseMeta databaseMeta=new DatabaseMeta("kettle_trans_log", "mysql", "Native(JDBC)", + "192.168.2.18","bps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8", "3306", "root", "abc.123"); + + String msg; + KettleFileRepository repo = this.fileRepositoryCon(xrepository); + TransMeta transMeta = this.loadTrans(repo, kettleTrans.getTransPath(), kettleTrans.getTransName()); + + transMeta.addDatabase(databaseMeta); + VariableSpace space=new Variables(); + TransLogTable jobLogTable= TransLogTable.getDefault(space,transMeta,null); + jobLogTable.setTableName("kettle_trans_log"); + jobLogTable.setConnectionName("kettle_trans_log"); + transMeta.setTransLogTable(jobLogTable); + //transMeta.getTransLogTable().setTableName(repInitialization.transLog); + //转换 + Trans trans = new Trans(transMeta); + //设置命名参数 + if(null != namedParams) { + //namedParams.forEach(trans::setParameterValue); + /*for (Map.Entry entry : namedParams.entrySet()) { + trans.setParameterValue(entry.getKey(), entry.getValue()); + }*/ + for(Iterator> it = namedParams.entrySet().iterator(); it.hasNext();){ + Map.Entry entry = it.next(); + trans.setParameterValue(entry.getKey(), entry.getValue()); + } + } + trans.setLogLevel(this.getLogerLevel(kettleTrans.getTransLogLevel())); + //执行 + trans.execute(clParams); + trans.waitUntilFinished(); + + KettleLogStore.discardLines(trans.getLogChannelId(),true); + + //记录日志 + String logChannelId = trans.getLogChannelId(); + LoggingBuffer appender = KettleLogStore.getAppender(); + String logText = appender.getBuffer(logChannelId, true).toString(); + log.info("[logTextlogText:"+logText+":logTextlogText]"); + //抛出异常 + if (trans.getErrors() > 0) { + msg = "There are errors during transformation exception!(转换过程中发生异常)"; + log.error(msg); + throw new Exception(msg); + } + TimeUnit.SECONDS.sleep(10); + } + + /** + * 执行文件资源库job + * @throws Exception + */ + public boolean callJob(KettleJob kettleJob,XRepository xRepository, Map variables, String[] clParams) throws Exception { + KettleEnv.init(); + String msg; + DatabaseMeta databaseMeta=new DatabaseMeta("kettle_job_log", "mysql", "Native(JDBC)", + "192.168.2.18","bps?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8", "3306", "root", "abc.123"); + KettleFileRepository repo = this.fileRepositoryCon(xRepository); + JobMeta jobMeta = this.loadJob(repo, kettleJob.getJobPath(), kettleJob.getJobName()); + jobMeta.addDatabase(databaseMeta); + VariableSpace space=new Variables(); + space.setVariable("test","fromDbName"); + JobLogTable jobLogTable= JobLogTable.getDefault(space,jobMeta); + jobLogTable.setTableName("kettle_job_log"); + jobLogTable.setConnectionName("kettle_job_log"); + jobMeta.setJobLogTable(jobLogTable); + + Job job = new Job(repo, jobMeta); + //向Job 脚本传递参数,脚本中获取参数值:${参数名} + if(null != variables) { + for(Iterator> it = variables.entrySet().iterator(); it.hasNext();){ + Map.Entry entry = it.next(); + job.setVariable(entry.getKey(), entry.getValue()); + } + } + //设置日志级别 + job.setLogLevel(this.getLogerLevel(kettleJob.getJobLogLevel())); + job.setArguments(clParams); + job.start(); + job.waitUntilFinished(); + //记录日志 + String logChannelId = job.getLogChannelId(); + LoggingBuffer appender = KettleLogStore.getAppender(); + String logText = appender.getBuffer(logChannelId, true).toString(); + log.info(logText); + if (job.getErrors() > 0) { + msg = "There are errors during job exception!(执行job发生异常)"; + log.error(msg); + throw new Exception(msg); + } + return true; + } + + /** + * 加载转换 + * @param repo kettle文件资源库 + * @param transPath 相对路径 + * @param transName 转换名称 + */ + public TransMeta loadTrans(KettleFileRepository repo, String transPath, String transName) throws Exception{ + String msg; + RepositoryDirectoryInterface dir = repo.findDirectory(transPath);//根据指定的字符串路径找到目录 + if(null == dir){ + msg = "kettle资源库转换路径不存在【"+repo.getRepositoryMeta().getBaseDirectory()+transPath+"】!"; + throw new Exception(msg); + } + TransMeta transMeta = repo.loadTransformation(repo.getTransformationID(transName, dir), null); + if(null == transMeta){ + msg = "kettle资源库【"+dir.getPath()+"】不存在该转换【"+transName+"】!"; + throw new Exception(msg); + } + return transMeta; + } + /** + * 加载job + * @param repo kettle文件资源库 + * @param jobPath 相对路径 + * @param jobName job名称 + */ + private JobMeta loadJob(KettleFileRepository repo, String jobPath, String jobName) throws Exception{ + String msg; + RepositoryDirectoryInterface dir = repo.findDirectory(jobPath);//根据指定的字符串路径找到目录 + if(null == dir){ + msg = "kettle资源库Job路径不存在【"+repo.getRepositoryMeta().getBaseDirectory()+jobPath+"】!"; + throw new Exception(msg); + } + JobMeta jobMeta = repo.loadJob(repo.getJobId(jobName, dir), null); + if(null == jobMeta){ + msg = "kettle资源库【"+dir.getPath()+"】不存在该转换【"+jobName+"】!"; + throw new Exception(msg); + } + return jobMeta; + } + + /** + * 调用trans文件 带参数的 + */ + public void callNativeTransWithParams(String[] params, String transName) throws Exception { + // 初始化 + KettleEnvironment.init(); + EnvUtil.environmentInit(); + TransMeta transMeta = new TransMeta(transName); + //转换 + Trans trans = new Trans(transMeta); + //执行 + trans.execute(params); + //等待结束 + trans.waitUntilFinished(); + //抛出异常 + if (trans.getErrors() > 0) { + throw new Exception("There are errors during transformation exception!(传输过程中发生异常)"); + } + } + + + + + + /** + * 调用job文件 + * @param jobName + * @throws Exception + */ +/* public void callNativeJob(String jobName) throws Exception { + // 初始化 + *//*KettleEnvironment.init();*//* + + JobMeta jobMeta = new JobMeta(jobName, null); + Job job = new Job(null, jobMeta); + //向Job 脚本传递参数,脚本中获取参数值:${参数名} + //job.setVariable(paraname, paravalue); + //设置日志级别 + job.setLogLevel(this.getLogerLevel(KETTLE_LOG_LEVEL)); + job.start(); + job.waitUntilFinished(); + if (job.getErrors() > 0) { + throw new Exception("There are errors during job exception!(执行job发生异常)"); + } + }*/ + + /** + * 取得kettle的日志级别 + */ + private LogLevel getLogerLevel(String level) { + LogLevel logLevel; + if ("basic".equals(level)) { + logLevel = LogLevel.BASIC; + } else if ("detail".equals(level)) { + logLevel = LogLevel.DETAILED; + } else if ("error".equals(level)) { + logLevel = LogLevel.ERROR; + } else if ("debug".equals(level)) { + logLevel = LogLevel.DEBUG; + } else if ("minimal".equals(level)) { + logLevel = LogLevel.MINIMAL; + } else if ("rowlevel".equals(level)) { + logLevel = LogLevel.ROWLEVEL; + } else if ("nothing".endsWith(level)){ + logLevel = LogLevel.NOTHING; + }else { + logLevel = null; + } + return logLevel; + } + + /** + * 配置kettle文件库资源库环境 + **/ + public KettleFileRepository fileRepositoryCon(XRepository xRepository) throws KettleException { + String msg; + //初始化 + /*EnvUtil.environmentInit(); + KettleEnvironment.init();*/ + + //资源库元对象 + KettleFileRepositoryMeta fileRepositoryMeta = new KettleFileRepositoryMeta(String.valueOf(xRepository.getId()), xRepository.getRepoName(), xRepository.getRemark(), xRepository.getBaseDir()); + // 文件形式的资源库 + KettleFileRepository repo = new KettleFileRepository(); + repo.init(fileRepositoryMeta); + //连接到资源库 + repo.connect("", "");//默认的连接资源库的用户名和密码 + + if (repo.isConnected()) { + msg = "kettle文件库资源库【" + xRepository.getBaseDir() + "】连接成功"; + log.info(msg); + return repo; + } else { + msg = "kettle文件库资源库【" + xRepository.getBaseDir() + "】连接失败"; + log.error(msg); + throw new KettleException(msg); + } + } + + // 调用Transformation示例 + public static void runTrans(String filename) { + try { + KettleEnvironment.init(); + TransMeta transMeta = new TransMeta(filename); + Trans trans = new Trans(transMeta); + trans.execute(null);// 执行转换 + trans.waitUntilFinished(); // 等待转换执行结束 + if (trans.getErrors() != 0) { + System.out.println("Error"); + } + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + /** + * Kettle环境初始化. + */ + public static class KettleEnv { + public static void init(){ + try { + KettleEnvironment.init(); + EnvUtil.environmentInit(); + log.info("Kettle环境初始化成功"); + }catch (Exception e){ + e.printStackTrace(); + log.error("Kettle环境初始化失败"); + } + + } + } + + /** + * 初始化环境 + */ + public class StartInit implements InitializingBean { + + @Override + public void afterPropertiesSet() throws Exception { + KettleEnv.init(); + + } + + } + + +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil_2.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil_2.java new file mode 100644 index 000000000..5b1132613 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/KettleUtil_2.java @@ -0,0 +1,736 @@ +package com.ruoyi.kettle.tools; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.kettle.cons.XJobStatus; +import com.ruoyi.kettle.cons.XTransStatus; +import com.ruoyi.kettle.repo.XRepoManager; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.vfs2.FileObject; +import org.pentaho.di.core.Const; +import org.pentaho.di.core.ProgressMonitorListener; +import org.pentaho.di.core.database.Database; +import org.pentaho.di.core.database.DatabaseMeta; +import org.pentaho.di.core.exception.KettleException; +import org.pentaho.di.core.exception.KettleSecurityException; +import org.pentaho.di.core.gui.Point; +import org.pentaho.di.core.gui.Rectangle; +import org.pentaho.di.core.gui.ScrollBarInterface; +import org.pentaho.di.core.gui.SwingGC; +import org.pentaho.di.core.parameters.NamedParams; +import org.pentaho.di.core.variables.VariableSpace; +import org.pentaho.di.core.vfs.KettleVFS; +import org.pentaho.di.job.Job; +import org.pentaho.di.job.JobHopMeta; +import org.pentaho.di.job.JobMeta; +import org.pentaho.di.job.JobPainter; +import org.pentaho.di.job.entries.job.JobEntryJob; +import org.pentaho.di.job.entries.special.JobEntrySpecial; +import org.pentaho.di.job.entries.trans.JobEntryTrans; +import org.pentaho.di.job.entry.JobEntryBase; +import org.pentaho.di.job.entry.JobEntryCopy; +import org.pentaho.di.job.entry.JobEntryInterface; +import org.pentaho.di.repository.*; +import org.pentaho.di.repository.filerep.KettleFileRepositoryMeta; +import org.pentaho.di.repository.kdr.KettleDatabaseRepository; +import org.pentaho.di.trans.Trans; +import org.pentaho.di.trans.TransHopMeta; +import org.pentaho.di.trans.TransMeta; +import org.pentaho.di.trans.TransPainter; +import org.pentaho.di.trans.step.StepInterface; +import org.pentaho.di.trans.step.StepMeta; +import org.pentaho.di.trans.step.StepMetaInterface; +import org.pentaho.di.trans.steps.jobexecutor.JobExecutorMeta; +import org.pentaho.di.trans.steps.transexecutor.TransExecutorMeta; +import org.pentaho.metastore.api.IMetaStore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.image.BufferedImage; +import java.awt.image.ImageObserver; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class KettleUtil_2 { + private static final Logger log = LoggerFactory.getLogger(KettleUtil_2.class); + public static Map holder = new ConcurrentHashMap(); + + public static Map getHolder() { + return holder; + } + + public static void setHolder(Map holder) { + KettleUtil_2.holder = holder; + } + + public KettleUtil_2() { + } + public static Repository conFileRep(String repoId, String repoName, String baseDirectory) throws KettleException { + if (holder.containsKey(repoId)) { + return (Repository)holder.get(repoId); + } else { + XRepoManager.createFileRep(repoId, repoName, "文件资源库", baseDirectory); + Repository repository = connect(repoId); + if (null != repository) { + holder.put(repoId, repository); + return repository; + } else { + return null; + } + } + } + public static Repository connect(String repoId) throws KettleSecurityException, KettleException { + return connect(repoId, (String)null, (String)null); + } + + public static Repository connect(String repoId, String username, String password) throws KettleSecurityException, KettleException { + Repository repository = (Repository)XRepoManager.repositoryCache.get(repoId); + if (!repository.isConnected()) { + repository.connect(username, password); + log.info(repository.getName() + "资源库已连接!"); + } + + return repository; + } + /*public static Repository conByJndi(String repoId, String name, String db, String type, String kuser, String kpass) throws Exception { + testConnect(repoId); + if (holder.containsKey(repoId)) { + return (Repository)holder.get(repoId); + } else { + XRepoManager.createDBRepByJndi(repoId, name, type, db); + Repository repository = connect(repoId, kuser, kpass); + if (null != repository) { + holder.put(repoId, repository); + return repository; + } else { + return null; + } + } + } + + + + public static Repository conByNative(String repoId, String repoName, String name, String type, String host, String port, String db, String user, String pass, String kuser, String kpass) throws KettleException { + testConnect(repoId); + if (holder.containsKey(repoId)) { + return (Repository)holder.get(repoId); + } else { + XRepoManager.createDBRepByDesc(repoId, repoName, "资源库", name, type, "Native", host, db, port, user, pass); + Repository repository = connect(repoId, kuser, kpass); + if (null != repository) { + holder.put(repoId, repository); + return repository; + } else { + return null; + } + } + } + + public static DatabaseMeta createDatabaseMeta(String name, String type, String access, String host, String dbName, String port, String user, String pass, boolean replace, Repository repository) { + return XRepoManager.createDatabaseMeta(name, type, access, host, dbName, port, user, pass, (Map)null, replace, repository); + } + + public static DatabaseMeta createDatabaseMeta(String name, String type, String access, String host, String dbName, String port, String user, String pass, Map params, Repository repository) { + return XRepoManager.createDatabaseMeta(name, type, access, host, dbName, port, user, pass, params, false, repository); + } + + public static Repository conByParams(String repoId, String repoName, JSONObject params, String name, String type, String access, String host, String db, String port, String user, String pass, String kuser, String kpass) throws Exception { + testConnect(repoId); + if (holder.containsKey(repoId)) { + return (Repository)holder.get(repoId); + } else { + XRepoManager.createDBRepByParams(repoId, repoName, params, name, type, access, host, db, port, user, pass); + Repository repository = connect(repoId, kuser, kpass); + if (null != repository) { + holder.put(repoId, repository); + return repository; + } else { + return null; + } + } + } + + + + public static void setRepository(Repository repository) { + } + + public static void destroy(String id) { + XRepoManager.disConnectionRepository(id); + } + + public static void delJob(String repoId, long id_job) throws KettleException { + testConnect(repoId); + delJob(id_job, (Repository)holder.get(repoId)); + } + + private static String calcRelativeElementDirectory(RepositoryDirectoryInterface dir) { + return dir != null ? dir.getPath() : "/"; + } + + private static String calcDirectoryName(String repoId, RepositoryDirectoryInterface dir) { + testConnect(repoId); + StringBuilder directory = new StringBuilder(); + KettleFileRepositoryMeta repositoryMeta = (KettleFileRepositoryMeta)((Repository)holder.get(repoId)).getRepositoryMeta(); + String baseDir = repositoryMeta.getBaseDirectory(); + baseDir = Const.replace(baseDir, "\\", "/"); + directory.append(baseDir); + if (!baseDir.endsWith("/")) { + directory.append("/"); + } + + if (dir != null) { + String path = calcRelativeElementDirectory(dir); + if (path.startsWith("/")) { + directory.append(path.substring(1)); + } else { + directory.append(path); + } + + if (!path.endsWith("/")) { + directory.append("/"); + } + } + + return directory.toString(); + } + + public static void delFileJob(String repoId, String jobId) throws KettleException { + String path = jobId.substring(0, jobId.lastIndexOf("/")); + String jobName = jobId.substring(jobId.lastIndexOf("/") + 1); + RepositoryDirectoryInterface repositoryDirectoryInterface = ((Repository)holder.get(repoId)).findDirectory(path); + String filename = calcFilename(repoId, repositoryDirectoryInterface, jobName); + deleteFile(filename); + } + + private static String calcFilename(String repoId, RepositoryDirectoryInterface repositoryDirectoryInterface, String jobName) { + return calcDirectoryName(repoId, (RepositoryDirectoryInterface)null) + repositoryDirectoryInterface.getPath() + "/" + jobName; + } + + private static void deleteFile(String filename) throws KettleException { + try { + FileObject fileObject = KettleVFS.getFileObject(filename); + fileObject.delete(); + } catch (Exception var2) { + throw new KettleException("Unable to delete file with name [" + filename + "]", var2); + } + } + + public static void delJob(long id_job, Repository repository) throws KettleException { + repository.deleteJob(new LongObjectId(id_job)); + } + + public static void delTrans(String repoId, long id_job) throws KettleException { + testConnect(repoId); + delTrans(id_job, (Repository)holder.get(repoId)); + } + + public static void delTrans(long id_job, Repository repository) throws KettleException { + repository.deleteTransformation(new LongObjectId(id_job)); + } + + public static JobMeta loadJob(String repoId, long jobId) throws KettleException { + testConnect(repoId); + return ((Repository)holder.get(repoId)).loadJob(new LongObjectId(jobId), (String)null); + } + + public static JobMeta loadJob(String repoId, String jobId) throws KettleException { + testConnect(repoId); + return ((Repository)holder.get(repoId)).loadJob(new StringObjectId(jobId), (String)null); + } + + public static JobMeta loadJob(String repoId, String jobname, String directory) throws KettleException { + testConnect(repoId); + return loadJob(jobname, directory, (Repository)holder.get(repoId)); + } + + public static JobMeta loadJob(String jobname, String directory, Repository repository) throws KettleException { + RepositoryDirectoryInterface dir = repository.findDirectory(directory); + return repository.loadJob(jobname, dir, (ProgressMonitorListener)null, (String)null); + } + + public static JobMeta loadJob(String repoId, String jobname, long directory) throws KettleException { + testConnect(repoId); + return loadJob(jobname, directory, (Repository)holder.get(repoId)); + } + + public static JobMeta loadJob(String jobname, long directory, Repository repository) throws KettleException { + RepositoryDirectoryInterface dir = repository.findDirectory(new LongObjectId(directory)); + return repository.loadJob(jobname, dir, (ProgressMonitorListener)null, (String)null); + } + + public static TransMeta loadTrans(String repoId, long id) throws KettleException { + testConnect(repoId); + return ((Repository)holder.get(repoId)).loadTransformation(new LongObjectId(id), (String)null); + } + + public static TransMeta loadTrans(String repoId, String transname, String directory) throws KettleException { + testConnect(repoId); + return loadTrans(transname, directory, (Repository)holder.get(repoId)); + } + + public static TransMeta loadTrans(String transname, String directory, Repository repository) throws KettleException { + RepositoryDirectoryInterface dir = repository.findDirectory(directory); + return repository.loadTransformation(transname, dir, (ProgressMonitorListener)null, true, (String)null); + } + + public static TransMeta loadTrans(String repoId, JobMeta jobMeta, String teansName) throws KettleException { + JobEntryTrans trans = (JobEntryTrans)jobMeta.findJobEntry(teansName).getEntry(); + TransMeta transMeta = loadTrans(repoId, trans.getTransname(), trans.getDirectory()); + return transMeta; + } + + public static T loadJobEntry(String repoId, JobMeta jobMeta, String jobEntryName, T jobEntryMeta) throws KettleException { + return loadJobEntry(repoId, jobMeta.findJobEntry(jobEntryName).getEntry().getObjectId(), jobEntryMeta); + } + + public static T loadJobEntry(String repoId, ObjectId entryId, T jobEntryMeta) throws KettleException { + testConnect(repoId); + jobEntryMeta.loadRep((Repository)holder.get(repoId), (IMetaStore)null, entryId, (List)null, (List)null); + jobEntryMeta.setObjectId(entryId); + return jobEntryMeta; + } + + public static JobEntrySpecial findStart(JobMeta jobMeta) { + for(int i = 0; i < jobMeta.nrJobEntries(); ++i) { + JobEntryCopy jec = jobMeta.getJobEntry(i); + JobEntryInterface je = jec.getEntry(); + if (je.getPluginId().equals("SPECIAL")) { + return (JobEntrySpecial)je; + } + } + + return null; + } + + public static void saveRepositoryElement(String repoId, RepositoryElementInterface repositoryElement) throws KettleException { + testConnect(repoId); + saveRepositoryElement((Repository)holder.get(repoId), repositoryElement); + } + + public static void saveRepositoryElement(Repository repository, RepositoryElementInterface repositoryElement) throws KettleException { + repository.save(repositoryElement, (String)null, (ProgressMonitorListener)null, true); + } + + public static void saveTrans(String repoId, TransMeta transMeta) throws KettleException { + testConnect(repoId); + saveRepositoryElement((Repository)((Repository)holder.get(repoId)), transMeta); + } + + public static void saveTrans(Repository repository, TransMeta transMeta) throws KettleException { + saveRepositoryElement((Repository)repository, transMeta); + } + + public static void saveJob(String repoId, JobMeta jobMeta) throws KettleException { + testConnect(repoId); + saveRepositoryElement((Repository)((Repository)holder.get(repoId)), jobMeta); + } + + public static void saveJob(Repository repository, JobMeta jobMeta) throws KettleException { + saveRepositoryElement((Repository)repository, jobMeta); + } + + public static boolean isDirectoryExist(Repository repository, String directoryName) { + try { + RepositoryDirectoryInterface dir = repository.findDirectory(directoryName); + return dir != null; + } catch (KettleException var3) { + log.error("判断job目录是否存在失败!", var3); + return false; + } + } + + public static RepositoryDirectoryInterface getOrMakeDirectory(String repoId, String parentDirectory, String directoryName) throws KettleException { + testConnect(repoId); + RepositoryDirectoryInterface parent = ((Repository)holder.get(repoId)).findDirectory(parentDirectory); + if (StringUtils.isBlank(parentDirectory)) { + parent = ((Repository)holder.get(repoId)).findDirectory("/"); + } + + if (StringUtils.isNotBlank(directoryName)) { + RepositoryDirectoryInterface dir = ((Repository)holder.get(repoId)).findDirectory(parentDirectory + "/" + directoryName); + return dir == null ? ((Repository)holder.get(repoId)).createRepositoryDirectory(parent, directoryName) : dir; + } else { + return parent; + } + } + + public static RepositoryDirectoryInterface makeDirs(String repoId, String directoryName) throws KettleException { + if (!StringUtils.isNotBlank(directoryName)) { + return null; + } else { + String parentDirectory = ""; + String[] dirArr = directoryName.replace("\\", "/").split("/"); + String[] var6 = dirArr; + int var5 = dirArr.length; + + for(int var4 = 0; var4 < var5; ++var4) { + String dirStr = var6[var4]; + + try { + if (StringUtils.isNotBlank(dirStr)) { + RepositoryDirectoryInterface p = getOrMakeDirectory(repoId, parentDirectory, dirStr); + parentDirectory = p.getPath(); + } + } catch (Exception var9) { + log.error("创建目录失败:" + directoryName + "," + parentDirectory + "," + dirStr, var9); + } + } + + return getOrMakeDirectory(repoId, parentDirectory, (String)null); + } + } + + public static String getDirectory(String repoId, long dirId) throws KettleException { + return getDirectory(repoId, new LongObjectId(dirId)); + } + + public static String getDirectory(String repoId, ObjectId dirId) throws KettleException { + testConnect(repoId); + RepositoryDirectoryInterface dir = ((Repository)holder.get(repoId)).findDirectory(dirId); + return dir == null ? null : dir.getPath(); + } + + public static void setStepToTrans(TransMeta teans, String stepName, StepMetaInterface smi) { + StepMeta step = teans.findStep(stepName); + step.setStepMetaInterface(smi); + } + + public static void setStepToTransAndSave(String repoId, TransMeta teans, String stepName, StepMetaInterface smi) throws KettleException { + setStepToTrans(teans, stepName, smi); + saveTrans(repoId, teans); + } + + public static void jobStopAll(Job job) { + job.stopAll(); + JobMeta jobMeta = job.getJobMeta(); + Iterator var3 = jobMeta.getJobCopies().iterator(); + + while(var3.hasNext()) { + JobEntryCopy jec = (JobEntryCopy)var3.next(); + if (jec.isTransformation()) { + JobEntryTrans jet = (JobEntryTrans)jec.getEntry(); + if (jet.getTrans() != null) { + jet.getTrans().stopAll(); + } + } else if (jec.isJob()) { + JobEntryJob jej = (JobEntryJob)jec.getEntry(); + if (jej.getJob() != null) { + jobStopAll(jej.getJob()); + } + } + } + + } + + public static void jobKillAll(Job job) { + job.stopAll(); + JobMeta jobMeta = job.getJobMeta(); + Iterator var3 = jobMeta.getJobCopies().iterator(); + + while(var3.hasNext()) { + JobEntryCopy jec = (JobEntryCopy)var3.next(); + if (jec.isTransformation()) { + JobEntryTrans jet = (JobEntryTrans)jec.getEntry(); + if (jet.getTrans() != null) { + jet.getTrans().killAll(); + } + } else if (jec.isJob()) { + JobEntryJob jej = (JobEntryJob)jec.getEntry(); + if (jej.getJob() != null) { + jobKillAll(jej.getJob()); + } + } + } + + if (!job.getState().equals(Thread.State.BLOCKED) && !job.getState().equals(Thread.State.TIMED_WAITING)) { + job.interrupt(); + } else { + job.stop(); + } + + } + + public static void transCopy(Map param, Repository fromRepository, Repository toRepository) throws KettleException { + String jobName = param.get("transName") + ""; + String jobPath = param.get("transPath") + ""; + String newName = param.get("newName") + ""; + String newPath = param.get("newPath") + ""; + String des = param.get("transDescription") + ""; + TransMeta jobMeta = loadTrans(jobName, jobPath, fromRepository); + new TransMeta(); + jobMeta.setName(newName); + jobMeta.setDescription(des); + jobMeta.setRepositoryDirectory(makeDirs(param.get("repoId") + "", newPath)); + saveTrans(toRepository, jobMeta); + } + + public static void jobCopy(Map param, Repository fromRepository, Repository toRepository) throws KettleException { + String jobName = param.get("jobName") + ""; + String jobPath = param.get("jobPath") + ""; + String newName = param.get("newName") + ""; + String newPath = param.get("newPath") + ""; + String des = param.get("jobDescription") + ""; + JobMeta jobMeta = loadJob(jobName, jobPath, fromRepository); + jobMeta.setName(newName); + jobMeta.setDescription(des); + jobMeta.setRepositoryDirectory(makeDirs(param.get("repoId") + "", newPath)); + saveJob(toRepository, jobMeta); + } + + public static void jobCopy(String jobName, String jobPath, Repository fromRepository, Repository toRepository) throws KettleException { + JobMeta jobMeta = loadJob(jobName, jobPath, fromRepository); + Iterator var6 = jobMeta.getJobCopies().iterator(); + + while(var6.hasNext()) { + JobEntryCopy jec = (JobEntryCopy)var6.next(); + if (jec.isTransformation()) { + JobEntryTrans jet = (JobEntryTrans)jec.getEntry(); + transCopy(jet.getObjectName(), jet.getDirectory(), fromRepository, toRepository); + } else if (jec.isJob()) { + JobEntryJob jej = (JobEntryJob)jec.getEntry(); + jobCopy(jej.getObjectName(), jej.getDirectory(), fromRepository, toRepository); + } + } + + jobMeta.setRepository(toRepository); + jobMeta.setMetaStore(toRepository.getMetaStore()); + if (!isDirectoryExist(toRepository, jobPath)) { + toRepository.createRepositoryDirectory(toRepository.findDirectory("/"), jobPath); + } + + saveJob(toRepository, jobMeta); + } + + public static void transCopy(String transName, String transPath, Repository fromRepository, Repository toRepository) throws KettleException { + TransMeta tm = loadTrans(transName, transPath, fromRepository); + Iterator var6 = tm.getSteps().iterator(); + + while(var6.hasNext()) { + StepMeta sm = (StepMeta)var6.next(); + if (sm.isJobExecutor()) { + JobExecutorMeta jem = (JobExecutorMeta)sm.getStepMetaInterface(); + jobCopy(jem.getJobName(), jem.getDirectoryPath(), fromRepository, toRepository); + } else if (sm.getStepMetaInterface() instanceof TransExecutorMeta) { + TransExecutorMeta te = (TransExecutorMeta)sm.getStepMetaInterface(); + transCopy(te.getTransName(), te.getDirectoryPath(), fromRepository, toRepository); + } + } + + if (!isDirectoryExist(toRepository, transPath)) { + toRepository.createRepositoryDirectory(toRepository.findDirectory("/"), transPath); + } + + tm.setRepository(toRepository); + tm.setMetaStore(toRepository.getMetaStore()); + saveTrans(toRepository, tm); + } + + public static ObjectId getJobId(String repoId, JobMeta jm) { + return getJobId(repoId, jm.getName(), jm.getRepositoryDirectory()); + } + + public static ObjectId getJobId(String repoId, String name, RepositoryDirectoryInterface repositoryDirectory) { + try { + testConnect(repoId); + return ((Repository)holder.get(repoId)).getJobId(name, repositoryDirectory); + } catch (KettleException var4) { + log.info("获取作业id失败", var4); + return null; + } + } + + public static ObjectId getTransformationID(String repoId, TransMeta tm) { + return getTransformationID(repoId, tm.getName(), tm.getRepositoryDirectory()); + } + + public static ObjectId getTransformationID(String repoId, String name, RepositoryDirectoryInterface repositoryDirectory) { + try { + testConnect(repoId); + return ((Repository)holder.get(repoId)).getTransformationID(name, repositoryDirectory); + } catch (KettleException var4) { + log.info("获取转换id失败", var4); + return null; + } + } + + public static void repairTransHop(TransMeta tm) { + for(int i = 0; i < tm.nrTransHops(); ++i) { + TransHopMeta hop = tm.getTransHop(i); + hop.setFromStep(tm.findStep(hop.getFromStep().getName())); + hop.setToStep(tm.findStep(hop.getToStep().getName())); + } + + } + + public static void setParams(NamedParams target, NamedParams source, Map params) { + target.eraseParameters(); + + try { + String[] var6; + int var5 = (var6 = source.listParameters()).length; + + for(int var4 = 0; var4 < var5; ++var4) { + String key = var6[var4]; + String defaultVal = source.getParameterDefault(key); + if (params.containsKey(key)) { + defaultVal = (String)params.get(key); + } + + target.addParameterDefinition(key, defaultVal, source.getParameterDescription(key)); + } + } catch (Exception var8) { + log.error("保存JOB失败", var8); + } + + } + + public static void repairHop(JobMeta jm) { + Iterator var2 = jm.getJobhops().iterator(); + + while(var2.hasNext()) { + JobHopMeta hop = (JobHopMeta)var2.next(); + hop.setFromEntry(jm.findJobEntry(hop.getFromEntry().getName())); + hop.setToEntry(jm.findJobEntry(hop.getToEntry().getName())); + } + + } + + public static String getProp(VariableSpace vs, String key) { + String value = vs.environmentSubstitute("${" + key + "}"); + return value.startsWith("${") ? "" : value; + } + + public static JSONObject getPropJSONObject(VariableSpace vs, String key) { + String value = getProp(vs, key); + return StringUtils.isNotBlank(value) ? JSON.parseObject(value) : null; + } + + public static Job getRootJob(Job rootjob) { + while(rootjob != null && rootjob.getParentJob() != null) { + rootjob = rootjob.getParentJob(); + } + + return rootjob; + } + + public static Job getRootJob(JobEntryBase jee) { + Job rootjob = jee.getParentJob(); + return getRootJob(rootjob); + } + + public static Job getRootJob(StepInterface si) { + Job rootjob = si.getTrans().getParentJob(); + return getRootJob(rootjob); + } + + public static String getRootJobId(JobEntryBase jee) { + return getRootJob(jee).getObjectId().getId(); + } + + public static String getRootJobId(StepInterface si) { + Job rootjob = getRootJob(si); + return rootjob != null ? rootjob.getObjectId().getId() : null; + } + + public static String getRootJobName(StepInterface si) { + Job rootjob = getRootJob(si); + return rootjob != null ? rootjob.getObjectName() : null; + } + + public static BufferedImage generateTransformationImage(TransMeta transMeta) throws Exception { + float magnification = 1.0F; + Point maximum = transMeta.getMaximum(); + maximum.multiply(magnification); + SwingGC gc = new SwingGC((ImageObserver)null, maximum, 32, 0, 0); + TransPainter transPainter = new TransPainter(gc, transMeta, maximum, (ScrollBarInterface)null, (ScrollBarInterface)null, (TransHopMeta)null, (Point)null, (Rectangle)null, new ArrayList(), new ArrayList(), 32, 1, 0, 0, true, "Arial", 10); + transPainter.setMagnification(magnification); + transPainter.buildTransformationImage(); + BufferedImage image = (BufferedImage)gc.getImage(); + return image; + } + + public static BufferedImage generateJobImage(JobMeta jobMeta) throws Exception { + float magnification = 1.0F; + Point maximum = jobMeta.getMaximum(); + maximum.multiply(magnification); + SwingGC gc = new SwingGC((ImageObserver)null, maximum, 32, 0, 0); + JobPainter jobPainter = new JobPainter(gc, jobMeta, maximum, (ScrollBarInterface)null, (ScrollBarInterface)null, (JobHopMeta)null, (Point)null, (Rectangle)null, new ArrayList(), new ArrayList(), 32, 1, 0, 0, true, "Arial", 10); + jobPainter.setMagnification(magnification); + jobPainter.drawJob(); + BufferedImage image = (BufferedImage)gc.getImage(); + return image; + } + + public static XJobStatus getJobStatus(Job job) { + String status = job.getStatus(); + if (status.indexOf("errors") > -1) { + return XJobStatus.FAILED; + } else if (status.equals("Waiting")) { + return XJobStatus.PENDING; + } else if (status.equals("Halting")) { + return XJobStatus.HALTING; + } else if (status.equals("Running")) { + return XJobStatus.RUNNING; + } else if (status.equals("Stopped")) { + return XJobStatus.STOPPED; + } else { + return status.equalsIgnoreCase("Finished") ? XJobStatus.FINISHED : XJobStatus.UNKNOWN; + } + } + + public static XTransStatus getTransStatus(Trans trans) { + String status = trans.getStatus(); + if (status.indexOf("errors") > -1) { + return XTransStatus.FAILED; + } else if (status.equals("Waiting")) { + return XTransStatus.WAITING; + } else if (status.equals("Halting")) { + return XTransStatus.HALTING; + } else if (status.equals("Running")) { + return XTransStatus.RUNNING; + } else if (status.equals("Stopped")) { + return XTransStatus.STOPPED; + } else if (status.equals("Finished")) { + return XTransStatus.FINISHED; + } else if (status.equals("Paused")) { + return XTransStatus.PAUSED; + } else if (status.contains("Preparing")) { + return XTransStatus.PREPARING; + } else { + return status.equals("Initializing") ? XTransStatus.INITIALIZING : XTransStatus.UNKNOWN; + } + } + + private static void testConnect(String repoId) { + Repository repository = (Repository)holder.get(repoId); + if (repository instanceof KettleDatabaseRepository) { + KettleDatabaseRepository kettleDatabaseRepository = (KettleDatabaseRepository)repository; + Database database = kettleDatabaseRepository.getDatabase(); + Connection connection = database.getConnection(); + + try { + if (connection.isClosed()) { + holder.remove(repoId); + log.info("当前数据库连接已经关闭,准备重新连接......."); + DatabaseMeta databaseMeta = (DatabaseMeta)XRepoManager.databaseMeta.get("databaseMeta"); + XRepoManager.createDBRepByDesc(repoId, repository.getName(), "资源库", databaseMeta.getName(), databaseMeta.getDatabaseTypeDesc(), "Native", databaseMeta.getHostname(), databaseMeta.getDatabaseName(), databaseMeta.getDatabasePortNumberString(), databaseMeta.getUsername(), databaseMeta.getPassword()); + kettleDatabaseRepository = (KettleDatabaseRepository)connect(repoId, "admin", "admin"); + if (null != repository) { + holder.put(repoId, kettleDatabaseRepository); + } + } + } catch (SQLException var6) { + log.error("数据库重连出现异常,{}", var6); + } catch (KettleException var7) { + log.error("数据库重连出现异常,{}", var7); + } + } + + }*/ +} diff --git a/bps-kettle/src/main/java/com/ruoyi/kettle/tools/RedisStreamUtil.java b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/RedisStreamUtil.java new file mode 100644 index 000000000..a838fbe43 --- /dev/null +++ b/bps-kettle/src/main/java/com/ruoyi/kettle/tools/RedisStreamUtil.java @@ -0,0 +1,258 @@ +package com.ruoyi.kettle.tools; + +import com.ruoyi.common.utils.security.PermissionUtils; +import com.ruoyi.kettle.domain.KettleJob; +import com.ruoyi.kettle.domain.KettleTrans; +import com.ruoyi.kettle.service.IKettleJobService; +import com.ruoyi.kettle.service.IKettleTransService; +import com.ruoyi.system.service.ISysConfigService; +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.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.StreamEntry; +import redis.clients.jedis.StreamEntryID; + +import java.net.InetAddress; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +/** + * @Description: + * 现在redis安装后新建一个stream: XADD koneStream * user kang msg Hello + *再把他读掉:XREAD streams koneStream 0 + *最后创建一个这个steam的消费者:XGROUP CREATE koneStream koneGroup 0 + * @Author: Kone.wang + * @Date: 2021/8/10 13:19 + **/ +@Component +public class RedisStreamUtil { + private static final Logger log = LoggerFactory.getLogger(RedisStreamUtil.class); + + String koneConsumer="bpsemi_consumer"; +// +// @Value("${stream.key}") +// String koneStream ; +// @Value("${stream.group}") +// String koneGroup ; + @Value("${spring.redis.timeout}") + Long waitTIme; + + @Autowired + private JedisPool jedisPool; + + @Autowired + private IKettleTransService transService; + + @Autowired + private IKettleJobService jobService; + @Autowired + private ISysConfigService configService; + /** + * @Description: 往队列中插入trans + * @Author: Kone.wang + * @Date: 2021/8/6 13:50 + * @param trans: + * @return: com.ruoyi.common.core.domain.AjaxResult + **/ + public void addKettleTrans(KettleTrans trans) { + //获取主机ip + String localAddr = configService.selectConfigByKey("sys.local.addr"); + localAddr =localAddr!=null?localAddr:"192.168.2.84"; + String koneStream="bpsemi_test"; + try{ + InetAddress addr = InetAddress.getLocalHost(); + String address = addr.getHostAddress(); + if(address.equals(localAddr)){ + koneStream="bpsemi"; + } + }catch (Exception e){ + log.error("addKettleTrans()获取主机ip异常:"+e); + } + + String transName=trans.getTransName(); + Long trandId = trans.getId(); + + //定时任务跑的时候这个会报错,所以捕获一下然后设置默认的 + String userId =""; + try{ + userId = String.valueOf(PermissionUtils.getPrincipalProperty("userId")); + }catch (Exception e){ + log.warn("定时任务执行的,默认发送给天宁吧408"); + userId="408"; + } + + + log.info(userId+"开始增加:trans_"+trandId+"@"+userId+":::"+transName); + //这里可以添加更多的属性 + Map map = new HashMap(); + map.put("trans_"+trandId+"@"+userId, transName); + Jedis jedis = jedisPool.getResource(); + + try{ + StreamEntryID id =jedis.xadd(koneStream, new StreamEntryID().NEW_ENTRY, map); + log.info(userId+"成功增加:trans_"+trandId+"@"+userId+":::"+transName+"[StreamEntryID:"+id+"]"); + }catch (Exception e){ + log.error(userId+"失败增加:trans"+trandId+"@"+userId+":::"+transName+"]"); + }finally { + if (jedis != null) { + try { + jedis.close(); + } catch (Exception e) { + } + } + } + + } + + /** + * @Description: 往队列中插入job + * @Author: Kone.wang + * @Date: 2021/8/6 13:50 + * @param job: + * @return: com.ruoyi.common.core.domain.AjaxResult + **/ + public void addKettleJob(KettleJob job) { + //获取主机ip + String localAddr = configService.selectConfigByKey("sys.local.addr"); + localAddr =localAddr!=null?localAddr:"192.168.2.84"; + String koneStream="bpsemi_test"; + try{ + InetAddress addr = InetAddress.getLocalHost(); + String address = addr.getHostAddress(); + if(address.equals(localAddr)){ + koneStream="bpsemi"; + } + }catch (Exception e){ + log.error("addKettleJob()获取主机ip异常:"+e); + } + String jobName=job.getJobName(); + Long jobId = job.getId(); + String userId =""; + try{ + userId = String.valueOf(PermissionUtils.getPrincipalProperty("userId")); + }catch (Exception e){ + log.warn("定时任务执行的,默认发送给天宁吧408"); + userId="408"; + } + + log.info(userId+"开始增加:job_"+jobId+"@"+userId+":::"+jobName); + //这里可以添加更多的属性 + Map map = new HashMap(); + map.put("job_"+jobId+"@"+userId, jobName); + Jedis jedis = jedisPool.getResource(); + try{ + StreamEntryID id = jedis.xadd(koneStream, new StreamEntryID().NEW_ENTRY, map); + log.info(userId+"成功增加:job_"+jobId+"@"+userId+":::"+jobName+"[StreamEntryID:"+id+"]"); + }catch (Exception e){ + log.error(userId+"失败增加:job_"+jobId+"@"+userId+":::"+jobName+"]"); + }finally { + if (jedis != null) { + try { + jedis.close(); + } catch (Exception e) { + } + } + } + + + } + /** + * @Description: 循环重队列中读消息 + * @Author: Kone.wang + * @Date: 2021/8/6 13:50 + * @return: void + **/ + public void readGroup() { + //获取主机ip + String localAddr = configService.selectConfigByKey("sys.local.addr"); + localAddr =localAddr!=null?localAddr:"192.168.2.84"; + String koneStream="bpsemi_test"; + String koneGroup="bpsemi_group_test"; + String koneConsumer="bpsemi_consumer"; + try{ + InetAddress addr = InetAddress.getLocalHost(); + String address = addr.getHostAddress(); + if(address.equals(localAddr)){ + koneStream="bpsemi"; + koneGroup="bpsemi_group"; + } + }catch (Exception e){ + log.error("addKettleJob()获取主机ip异常:"+e); + } + while (true){ + + Jedis jedis = jedisPool.getResource(); + if(jedis ==null){ + return; + }else{ + try{ + Map t = new HashMap(); + List>> list = new ArrayList>>(); + t.put(koneStream, null);//null 则为 > 重头读起,也可以为$接受新消息,还可以是上一次未读完的消息id + Map.Entry e = null; + for(Map.Entry c:t.entrySet()){ + e=c; + } + //noAck为false的话需要手动ack,true则自动ack. commsumer新建的方式为xreadgroup。 + log.info("开始读消息"); + try{ + list = jedis.xreadGroup(koneGroup, koneConsumer, 1, 30000L, false, e); + + }catch (Exception ex){ + log.error("超时了!!!!!!!!"); + } + log.info("读消息结束!"); + if(list ==null){ + log.error("读到的list为空"); + }else{ + for (Map.Entry m : list) { + if (m.getValue() instanceof ArrayList) { + List l = (List) m.getValue(); + Map result = l.get(0).getFields(); + for (Map.Entry entry : result.entrySet()) { + System.out.println(entry.getKey() + "---" + entry.getValue()); + if(entry.getKey() != null){ + String key = String.valueOf(entry.getKey()); + String value =String.valueOf(entry.getValue()); + String id=key.substring(key.indexOf("_")+1,key.indexOf("@")); + String userId=key.substring(key.indexOf("@")+1); + if(key.startsWith("trans_")){ + log.info(value+"的trans:开始执行"); + transService.runTransRightNow(Long.valueOf(id),userId); + log.info(value+"的trans:结束执行"); + }else if(key.startsWith("job_")){ + log.info(value+"的job:开始执行"); + jobService.runJobRightNow(Long.valueOf(id),userId); + log.info(value+"的job:结束执行"); + } + } + } + long id = jedis.xack(koneStream, koneGroup, l.get(0).getID()); + log.info("消息消费成功:"+id); + } + } + } + }catch (Exception e){ + log.error(e.getMessage()); + }finally { + if (jedis != null) { + try { + jedis.close(); + } catch (Exception e) { + } + } + } + } + } + } + +} diff --git a/bps-kettle/src/main/resources/mapper/kettle/KettleJobMapper.xml b/bps-kettle/src/main/resources/mapper/kettle/KettleJobMapper.xml new file mode 100644 index 000000000..a9318e634 --- /dev/null +++ b/bps-kettle/src/main/resources/mapper/kettle/KettleJobMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + select id, created_time, update_time, created_by, update_by, job_name, job_description, job_type, job_path, job_repository_id, job_log_level, job_status, is_del, is_monitor_enabled, role_key, tpl_key,last_succeed_time from kettle_job + + + + + + + + + insert into kettle_job + + created_time, + update_time, + created_by, + update_by, + job_name, + job_description, + job_type, + job_path, + job_repository_id, + job_log_level, + job_status, + is_del, + is_monitor_enabled, + role_key, + tpl_key, + + + #{createdTime}, + #{updateTime}, + #{createdBy}, + #{updateBy}, + #{jobName}, + #{jobDescription}, + #{jobType}, + #{jobPath}, + #{jobRepositoryId}, + #{jobLogLevel}, + #{jobStatus}, + #{isDel}, + #{isMonitorEnabled}, + #{roleKey}, + #{tplKey}, + + + + + update kettle_job + + created_time = #{createdTime}, + update_time = #{updateTime}, + created_by = #{createdBy}, + update_by = #{updateBy}, + job_name = #{jobName}, + job_description = #{jobDescription}, + job_type = #{jobType}, + job_path = #{jobPath}, + job_repository_id = #{jobRepositoryId}, + job_log_level = #{jobLogLevel}, + job_status = #{jobStatus}, + is_del = #{isDel}, + is_monitor_enabled = #{isMonitorEnabled}, + role_key = #{roleKey}, + tpl_key = #{tplKey}, + last_succeed_time = #{lastSucceedTime}, + + where id = #{id} + + + + delete from kettle_job where id = #{id} + + + + delete from kettle_job where id in + + #{id} + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/mapper/kettle/KettleTransMapper.xml b/bps-kettle/src/main/resources/mapper/kettle/KettleTransMapper.xml new file mode 100644 index 000000000..d19254676 --- /dev/null +++ b/bps-kettle/src/main/resources/mapper/kettle/KettleTransMapper.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, trans_name, trans_description, created_time, update_time, created_by, update_by, trans_type, trans_path, trans_repository_id, trans_log_level, trans_status, is_del, is_monitor_enabled, tpl_key, role_key, remark,last_succeed_time from kettle_trans + + + + + + + + + + + insert into kettle_trans + + trans_name, + trans_description, + created_time, + update_time, + created_by, + update_by, + trans_type, + trans_path, + trans_repository_id, + trans_log_level, + trans_status, + is_del, + is_monitor_enabled, + tpl_key, + role_key, + remark, + + + #{transName}, + #{transDescription}, + #{createdTime}, + #{updateTime}, + #{createdBy}, + #{updateBy}, + #{transType}, + #{transPath}, + #{transRepositoryId}, + #{transLogLevel}, + #{transStatus}, + #{isDel}, + #{isMonitorEnabled}, + #{tplKey}, + #{roleKey}, + #{remark}, + + + + + update kettle_trans + + trans_name = #{transName}, + trans_description = #{transDescription}, + created_time = #{createdTime}, + update_time = #{updateTime}, + created_by = #{createdBy}, + update_by = #{updateBy}, + trans_type = #{transType}, + trans_path = #{transPath}, + trans_repository_id = #{transRepositoryId}, + trans_log_level = #{transLogLevel}, + trans_status = #{transStatus}, + is_del = #{isDel}, + is_monitor_enabled = #{isMonitorEnabled}, + tpl_key = #{tplKey}, + role_key = #{roleKey}, + remark = #{remark}, + last_succeed_time = #{lastSucceedTime}, + + where id = #{id} + + + + delete from kettle_trans where id = #{id} + + + + delete from kettle_trans where id in + + #{id} + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/mapper/kettle/XRepositoryMapper.xml b/bps-kettle/src/main/resources/mapper/kettle/XRepositoryMapper.xml new file mode 100644 index 000000000..3d82ca3c4 --- /dev/null +++ b/bps-kettle/src/main/resources/mapper/kettle/XRepositoryMapper.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + select id, repo_id, repo_name, repo_username, repo_password, repo_type, db_access, db_host, db_port, db_name, db_username, db_password, is_del, created_time, update_time, created_by, update_by, type, base_dir from kettle_repository + + + + + + + + insert into kettle_repository + + repo_id, + repo_name, + repo_username, + repo_password, + repo_type, + db_access, + db_host, + db_port, + db_name, + db_username, + db_password, + is_del, + created_time, + update_time, + created_by, + update_by, + type, + base_dir, + + + #{repoId}, + #{repoName}, + #{repoUsername}, + #{repoPassword}, + #{repoType}, + #{dbAccess}, + #{dbHost}, + #{dbPort}, + #{dbName}, + #{dbUsername}, + #{dbPassword}, + #{isDel}, + #{createdTime}, + #{updateTime}, + #{createdBy}, + #{updateBy}, + #{type}, + #{baseDir}, + + + + + update kettle_repository + + repo_id = #{repoId}, + repo_name = #{repoName}, + repo_username = #{repoUsername}, + repo_password = #{repoPassword}, + repo_type = #{repoType}, + db_access = #{dbAccess}, + db_host = #{dbHost}, + db_port = #{dbPort}, + db_name = #{dbName}, + db_username = #{dbUsername}, + db_password = #{dbPassword}, + is_del = #{isDel}, + created_time = #{createdTime}, + update_time = #{updateTime}, + created_by = #{createdBy}, + update_by = #{updateBy}, + type = #{type}, + base_dir = #{baseDir}, + + where id = #{id} + + + update kettle_repository set is_del=1 where id = #{id} + + + update kettle_repository set is_del=1 where id IN + + #{id} + + + + delete from kettle_repository where id = #{id} + + + + delete from kettle_repository where id in + + #{id} + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/common/repository_tree.html b/bps-kettle/src/main/resources/templates/kettle/common/repository_tree.html new file mode 100644 index 000000000..e0f63c3be --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/common/repository_tree.html @@ -0,0 +1,102 @@ + + + + + + + + + + + + +
    + + +
    + +
    + 展开 / + 折叠 +
    +
    +
    + + + + + diff --git a/bps-kettle/src/main/resources/templates/kettle/job/add.html b/bps-kettle/src/main/resources/templates/kettle/job/add.html new file mode 100644 index 000000000..13867d1b8 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/job/add.html @@ -0,0 +1,111 @@ + + + + + + + + + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/job/detail.html b/bps-kettle/src/main/resources/templates/kettle/job/detail.html new file mode 100644 index 000000000..1d95583f9 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/job/detail.html @@ -0,0 +1,36 @@ + + + + + + +
    +
    + +
    + + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/job/edit.html b/bps-kettle/src/main/resources/templates/kettle/job/edit.html new file mode 100644 index 000000000..5e55d8886 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/job/edit.html @@ -0,0 +1,90 @@ + + + + + + + + + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/job/job.html b/bps-kettle/src/main/resources/templates/kettle/job/job.html new file mode 100644 index 000000000..32c44d961 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/job/job.html @@ -0,0 +1,174 @@ + + + + + + +
    +
    +
    +
    +
    +
      +
    • + + +
    • + +
    • + + +
    • +
    • +  搜索 +  重置 +
    • +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/quartz/addquartz.html b/bps-kettle/src/main/resources/templates/kettle/quartz/addquartz.html new file mode 100644 index 000000000..f5dc8da71 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/quartz/addquartz.html @@ -0,0 +1,107 @@ + + + + + + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + + +
    +
    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    + +
    + + +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + diff --git a/bps-kettle/src/main/resources/templates/kettle/quartz/editquartz.html b/bps-kettle/src/main/resources/templates/kettle/quartz/editquartz.html new file mode 100644 index 000000000..46f421948 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/quartz/editquartz.html @@ -0,0 +1,108 @@ + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    + +
    + + +
    +
    +
    + +
    +
    + + +
    +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + diff --git a/bps-kettle/src/main/resources/templates/kettle/repository/add.html b/bps-kettle/src/main/resources/templates/kettle/repository/add.html new file mode 100644 index 000000000..e75ef192a --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/repository/add.html @@ -0,0 +1,135 @@ + + + + + + + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/repository/edit.html b/bps-kettle/src/main/resources/templates/kettle/repository/edit.html new file mode 100644 index 000000000..b175b3bf9 --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/repository/edit.html @@ -0,0 +1,132 @@ + + + + + + + +
    +
    + +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    +
    + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/repository/repository.html b/bps-kettle/src/main/resources/templates/kettle/repository/repository.html new file mode 100644 index 000000000..a2a66e6db --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/repository/repository.html @@ -0,0 +1,122 @@ + + + + + + +
    +
    +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/trans/add.html b/bps-kettle/src/main/resources/templates/kettle/trans/add.html new file mode 100644 index 000000000..dc470893b --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/trans/add.html @@ -0,0 +1,144 @@ + + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + + + + + + +
    + +
    + +
    +
    + + + + + + + + + +
    + +
    + +
    +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/trans/detail.html b/bps-kettle/src/main/resources/templates/kettle/trans/detail.html new file mode 100644 index 000000000..c77d55e2c --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/trans/detail.html @@ -0,0 +1,36 @@ + + + + + + +
    +
    + +
    + + +
    +
    +
    + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/trans/edit.html b/bps-kettle/src/main/resources/templates/kettle/trans/edit.html new file mode 100644 index 000000000..36c2f244d --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/trans/edit.html @@ -0,0 +1,105 @@ + + + + + + + + + +
    +
    + + +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    + +
    +
    + +
    + +
    + +
    +
    +
    + +
    + + + +
    +
    +
    + +
    + +
    +
    + + +
    +
    + + + + + + + \ No newline at end of file diff --git a/bps-kettle/src/main/resources/templates/kettle/trans/trans.html b/bps-kettle/src/main/resources/templates/kettle/trans/trans.html new file mode 100644 index 000000000..08b74430e --- /dev/null +++ b/bps-kettle/src/main/resources/templates/kettle/trans/trans.html @@ -0,0 +1,172 @@ + + + + + + +
    +
    +
    +
    +
    +
      +
    • + + +
    • + +
    • + + +
    • +
    • +  搜索 +  重置 +
    • +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + + \ No newline at end of file diff --git a/doc/若依环境使用手册.docx b/doc/若依环境使用手册.docx new file mode 100644 index 000000000..fa5b62d7d --- /dev/null +++ b/doc/若依环境使用手册.docx @@ -0,0 +1,89 @@ + 若依系统开发环境搭建手册 +1. 准备工作 +1.1 前置环境准备 -- -- 安装Maven + 若依基于Maven管理项目的构建,需要先安装好相应的版本。 +1.2 开发工具 + 若依系统采用Eclipse作为开发工具。但不局限于Eclipse。此处仅介绍在Eclipse搭建开发环境所需的操作。 +2. 开发环境搭建 +2.1 开发工具的配置 +2.1.1 配置Maven +进入Window->Preferences->Maven->Installations页面,设置已经安装好的Maven + +2.1.2 配置Maven仓库路径 +进入Window->Preferences->Maven->User Settings页面,配置仓库路径 + +2.1.4 关闭校验 +进入Window->Preferences->Validation页面,勾选"Suspend all validators",关闭校验 + +2.2 导入工程 +通过Eclipse导入工程,步骤如下: +(1)点击左侧项目区域 -- >Import... + +(2)选择RuoYi + +(3)点击Finish + +(4)RuoYi的代码就被导出到Eclipse中了,此时可以在工程视图中看到。 + +3. 运行若依系统 +3.1 必要的配置 +3.1.1 修改数据库连接 +编辑src/main/ resources目录下的application-druid.yml 文件,修改数据库地址账号信息。 +执行sql/ ry_20180423.sql,quartz.sql 两个文件 日期随版本变化 +3.1.2 开发环境配置 +编辑src/main/ resources目录下的application.yml 文件, +默认端口为80 + +3.1.3 代码生成配置 +编辑src/main/ resources目录下的application.yml 文件, + +默认为module,根据实际情况修改即可。生成的表要有注释 + +注:如对模板有特殊需求,可自行修改。编辑src/main/ resources/templates/vm目录下 + + +3.1.4 日志配置 +编辑src/main/ resources目录下的logback.yml 文件 + +改为自己需要的路径 + +3.2 启动及验证 +启动RuoYiApplication.java 出现如下图表示启动成功 + +打开浏览器,输入:http://localhost:80/ +若能正确展示登录页面,并能成功登录,登录后菜单及页面展示正常,则表明环境搭建成功。 +默认密码为 admin/admin123 + +演示地址:http://ruoyi.vip + + + +4. 部署若依系统 +4.1 war部署方式 +4.1.1 修改pom.xml文件。将jar修改为war + +如果是分模块需要修改ruoyi-admin + + +4.1.2 在spring-boot-starter依赖中移除tomcat模块 + + + + spring-boot-starter-tomcat + org.springframework.boot + + + +4.1.3 部署到tomcat的webapps目录下面 +默认为RuoYi.war + +4.1.4 启动及验证 +运行startup.bat 出现如下图即部署成功 + +4.2 Jar方式部署 +执行命令:java - jar RuoYi.jar +脚本执行:ry.sh start 启动stop 停止 + + +演示地址:ruoyi.vip +文档地址:doc.ruoyi.vip diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..be00b338d --- /dev/null +++ b/pom.xml @@ -0,0 +1,306 @@ + + + 4.0.0 + + com.ruoyi + ruoyi + 4.6.2 + + ruoyi + http://www.ruoyi.vip + 若依管理系统 + + + 4.6.2 + UTF-8 + UTF-8 + 1.8 + 3.1.1 + 1.7.1 + 2.0.0 + 1.2.6 + 1.21 + 2.3.2 + 3.0.0 + 2.1.4 + 1.3.1 + 1.2.76 + 5.8.0 + 5.8.0 + 2.11.0 + 1.4 + 4.1.2 + 1.7 + + + + + + + + + org.springframework.boot + spring-boot-dependencies + 2.2.13.RELEASE + pom + import + + + + + com.alibaba + druid-spring-boot-starter + ${druid.version} + + + + + com.github.penggle + kaptcha + ${kaptcha.version} + + + + + org.apache.shiro + shiro-core + ${shiro.version} + + + + + org.apache.shiro + shiro-spring + ${shiro.version} + + + + + org.apache.shiro + shiro-ehcache + ${shiro.version} + + + + + com.github.theborakompanioni + thymeleaf-extras-shiro + ${thymeleaf.extras.shiro.version} + + + + + eu.bitwalker + UserAgentUtils + ${bitwalker.version} + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot.version} + + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper.boot.version} + + + + + com.github.oshi + oshi-core + ${oshi.version} + + + + net.java.dev.jna + jna + ${jna.version} + + + + net.java.dev.jna + jna-platform + ${jna.version} + + + + + io.springfox + springfox-boot-starter + ${swagger.version} + + + io.swagger + swagger-models + + + + + + + commons-io + commons-io + ${commons.io.version} + + + + + commons-fileupload + commons-fileupload + ${commons.fileupload.version} + + + + + org.apache.poi + poi-ooxml + ${poi.version} + + + org.apache.xmlbeans + xmlbeans + 3.1.0 + + + + org.apache.velocity + velocity + ${velocity.version} + + + commons-collections + commons-collections + + + + + + + com.alibaba + fastjson + ${fastjson.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} + + + + + com.ruoyi + box-test + ${ruoyi.version} + + + + + com.ruoyi + box-bps + ${ruoyi.version} + + + + com.ruoyi + bps-kettle + ${ruoyi.version} + + + + + + ruoyi-admin + ruoyi-framework + ruoyi-system + ruoyi-quartz + ruoyi-generator + ruoyi-common + box-test + box-bps + bps-kettle + + pom + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + -Xlint:unchecked + + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + + + + + public + aliyun nexus + http://maven.aliyun.com/nexus/content/groups/public/ + + true + + + false + + + + + \ No newline at end of file diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml new file mode 100644 index 000000000..d865c3487 --- /dev/null +++ b/ruoyi-admin/pom.xml @@ -0,0 +1,167 @@ + + + + ruoyi + com.ruoyi + 4.6.2 + + 4.0.0 + war + ruoyi-admin + + + web服务入口 + + + + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + + org.springframework.boot + spring-boot-devtools + true + + + + + io.springfox + springfox-boot-starter + + + + + io.swagger + swagger-models + 1.6.2 + + + + + mysql + mysql-connector-java + + + + + + com.oracle.database.jdbc + ojdbc6 + 11.2.0.4 + + + + + com.microsoft.sqlserver + mssql-jdbc + 8.4.1.jre8 + + + + + com.ruoyi + ruoyi-framework + + + + + com.ruoyi + ruoyi-quartz + + + + + com.ruoyi + ruoyi-generator + + + + + com.ruoyi + box-test + + + + + com.ruoyi + box-bps + + + com.ruoyi + bps-kettle + + + + + + + org.springframework.boot + spring-boot-maven-plugin + 2.1.1.RELEASE + + true + + + + + repackage + + + + + + org.apache.maven.plugins + maven-war-plugin + 3.0.0 + + false + ${project.artifactId} + + + + + + it_war + + + \ No newline at end of file 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 000000000..0e890c109 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java @@ -0,0 +1,30 @@ +package com.ruoyi; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; + +/** + * 启动程序 + * + * @author ruoyi + */ +@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class }) +public class RuoYiApplication +{ + public static void main(String[] args) + { + // System.setProperty("spring.devtools.restart.enabled", "false"); + SpringApplication.run(RuoYiApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 若依启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} \ No newline at end of file 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 000000000..6de67dc76 --- /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/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java new file mode 100644 index 000000000..bb994f6db --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -0,0 +1,156 @@ +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.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.config.ServerConfig; +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; + +/** + * 通用请求处理 + * + * @author ruoyi + */ +@Controller +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("common/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("/common/upload") + @ResponseBody + 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("fileName", fileName); + ajax.put("url", url); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 通用上传请求(多个) + */ + @PostMapping("/common/uploads") + @ResponseBody + public AjaxResult uploadFiles(List files) throws Exception + { + try + { + // 上传文件路径 + String filePath = RuoYiConfig.getUploadPath(); + List fileNames = new ArrayList(); + List urls = new ArrayList(); + for (MultipartFile file : files) + { + // 上传并返回新文件名称 + String fileName = FileUploadUtils.upload(filePath, file); + String url = serverConfig.getUrl() + fileName; + fileNames.add(fileName); + urls.add(url); + } + AjaxResult ajax = AjaxResult.success(); + ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER)); + ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); + return ajax; + } + catch (Exception e) + { + return AjaxResult.error(e.getMessage()); + } + } + + /** + * 本地资源通用下载 + */ + @GetMapping("/common/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/demo/controller/DemoDialogController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoDialogController.java new file mode 100644 index 000000000..cf52245f0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoDialogController.java @@ -0,0 +1,80 @@ +package com.ruoyi.web.controller.demo.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * 模态窗口 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/modal") +public class DemoDialogController +{ + private String prefix = "demo/modal"; + + /** + * 模态窗口 + */ + @GetMapping("/dialog") + public String dialog() + { + return prefix + "/dialog"; + } + + /** + * 弹层组件 + */ + @GetMapping("/layer") + public String layer() + { + return prefix + "/layer"; + } + + /** + * 表单 + */ + @GetMapping("/form") + public String form() + { + return prefix + "/form"; + } + + /** + * 表格 + */ + @GetMapping("/table") + public String table() + { + return prefix + "/table"; + } + + /** + * 表格check + */ + @GetMapping("/check") + public String check() + { + return prefix + "/table/check"; + } + + /** + * 表格radio + */ + @GetMapping("/radio") + public String radio() + { + return prefix + "/table/radio"; + } + + /** + * 表格回传父窗体 + */ + @GetMapping("/parent") + public String parent() + { + return prefix + "/table/parent"; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoFormController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoFormController.java new file mode 100644 index 000000000..ec7d4c842 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoFormController.java @@ -0,0 +1,390 @@ +package com.ruoyi.web.controller.demo.controller; + +import java.util.ArrayList; +import java.util.List; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.alibaba.fastjson.JSON; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.domain.CxSelect; +import com.ruoyi.common.json.JSONObject; +import com.ruoyi.common.json.JSONObject.JSONArray; +import com.ruoyi.common.utils.StringUtils; + +/** + * 表单相关 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/form") +public class DemoFormController +{ + private String prefix = "demo/form"; + + private final static List users = new ArrayList(); + { + users.add(new UserFormModel(1, "1000001", "测试1", "15888888888")); + users.add(new UserFormModel(2, "1000002", "测试2", "15666666666")); + users.add(new UserFormModel(3, "1000003", "测试3", "15666666666")); + users.add(new UserFormModel(4, "1000004", "测试4", "15666666666")); + users.add(new UserFormModel(5, "1000005", "测试5", "15666666666")); + } + + /** + * 按钮页 + */ + @GetMapping("/button") + public String button() + { + return prefix + "/button"; + } + + /** + * 下拉框 + */ + @GetMapping("/select") + public String select() + { + return prefix + "/select"; + } + + /** + * 时间轴 + */ + @GetMapping("/timeline") + public String timeline() + { + return prefix + "/timeline"; + } + + /** + * 表单校验 + */ + @GetMapping("/validate") + public String validate() + { + return prefix + "/validate"; + } + + /** + * 功能扩展(包含文件上传) + */ + @GetMapping("/jasny") + public String jasny() + { + return prefix + "/jasny"; + } + + /** + * 拖动排序 + */ + @GetMapping("/sortable") + public String sortable() + { + return prefix + "/sortable"; + } + + /** + * 单据打印 + */ + @GetMapping("/invoice") + public String invoice() + { + return prefix + "/invoice"; + } + + /** + * 标签 & 提示 + */ + @GetMapping("/labels_tips") + public String labels_tips() + { + return prefix + "/labels_tips"; + } + + /** + * 选项卡 & 面板 + */ + @GetMapping("/tabs_panels") + public String tabs_panels() + { + return prefix + "/tabs_panels"; + } + + /** + * 栅格 + */ + @GetMapping("/grid") + public String grid() + { + return prefix + "/grid"; + } + + /** + * 表单向导 + */ + @GetMapping("/wizard") + public String wizard() + { + return prefix + "/wizard"; + } + + /** + * 文件上传 + */ + @GetMapping("/upload") + public String upload() + { + return prefix + "/upload"; + } + + /** + * 日期和时间页 + */ + @GetMapping("/datetime") + public String datetime() + { + return prefix + "/datetime"; + } + + /** + * 左右互选组件 + */ + @GetMapping("/duallistbox") + public String duallistbox() + { + return prefix + "/duallistbox"; + } + + /** + * 基本表单 + */ + @GetMapping("/basic") + public String basic() + { + return prefix + "/basic"; + } + + /** + * 卡片列表 + */ + @GetMapping("/cards") + public String cards() + { + return prefix + "/cards"; + } + + /** + * summernote 富文本编辑器 + */ + @GetMapping("/summernote") + public String summernote() + { + return prefix + "/summernote"; + } + + /** + * 搜索自动补全 + */ + @GetMapping("/autocomplete") + public String autocomplete() + { + return prefix + "/autocomplete"; + } + + /** + * 多级联动下拉 + */ + @GetMapping("/cxselect") + public String cxselect(ModelMap mmap) + { + CxSelect cxSelectTB = new CxSelect(); + cxSelectTB.setN("淘宝"); + cxSelectTB.setV("taobao"); + CxSelect cxSelectTm = new CxSelect(); + cxSelectTm.setN("天猫"); + cxSelectTm.setV("tm"); + CxSelect cxSelectJhs = new CxSelect(); + cxSelectJhs.setN("聚划算"); + cxSelectJhs.setV("jhs"); + List tmList = new ArrayList(); + tmList.add(cxSelectTm); + tmList.add(cxSelectJhs); + cxSelectTB.setS(tmList); + + CxSelect cxSelectJD = new CxSelect(); + cxSelectJD.setN("京东"); + cxSelectJD.setV("jd"); + CxSelect cxSelectCs = new CxSelect(); + cxSelectCs.setN("京东超市"); + cxSelectCs.setV("jdcs"); + CxSelect cxSelectSx = new CxSelect(); + cxSelectSx.setN("京东生鲜"); + cxSelectSx.setV("jdsx"); + List jdList = new ArrayList(); + jdList.add(cxSelectCs); + jdList.add(cxSelectSx); + cxSelectJD.setS(jdList); + + List cxList = new ArrayList(); + cxList.add(cxSelectTB); + cxList.add(cxSelectJD); + + mmap.put("data", JSON.toJSON(cxList)); + return prefix + "/cxselect"; + } + + /** + * 局部刷新 + */ + @GetMapping("/localrefresh") + public String localRefresh(ModelMap mmap) + { + JSONArray list = new JSONArray(); + JSONObject item = new JSONObject(); + item.put("name", "这条任务数据是由ModelMap传递到页面的,点击添加按钮后会将这条数据替换为新数据"); + item.put("type", "默认"); + item.put("date", "2020.06.10"); + list.add(item); + mmap.put("tasks", list); + mmap.put("min", 2); + mmap.put("max", 10); + return prefix + "/localrefresh"; + } + + /** + * 局部刷新-添加任务 + * + * @param fragment 页面中的模板名称 + * @param taskName 任务名称 + */ + @PostMapping("/localrefresh/task") + public String localRefreshTask(String fragment, String taskName, ModelMap mmap) + { + JSONArray list = new JSONArray(); + JSONObject item = new JSONObject(); + item.put("name", StringUtils.defaultIfBlank(taskName, "通过电话销售过程中了解各盛市的设备仪器使用、采购情况及相关重要追踪人")); + item.put("type", "新增"); + item.put("date", "2018.06.10"); + list.add(item); + item = new JSONObject(); + item.put("name", "提高自己电话营销技巧,灵活专业地与客户进行电话交流"); + item.put("type", "新增"); + item.put("date", "2018.06.12"); + list.add(item); + mmap.put("tasks", list); + return prefix + "/localrefresh::" + fragment; + } + + /** + * 模拟数据 + */ + @GetMapping("/cityData") + @ResponseBody + public String cityData() + { + String data = "[{\"n\":\"湖南省\",\"s\":[{\"n\":\"长沙市\",\"s\":[{\"n\":\"芙蓉区\"},{\"n\":\"天心区\"},{\"n\":\"岳麓区\"},{\"n\":\"开福区\"},{\"n\":\"雨花区\"},{\"n\":\"望城区\"},{\"n\":\"长沙县\"},{\"n\":\"宁乡县\"},{\"n\":\"浏阳市\"}]},{\"n\":\"株洲市\",\"s\":[{\"n\":\"荷塘区\"},{\"n\":\"芦淞区\"},{\"n\":\"石峰区\"},{\"n\":\"天元区\"},{\"n\":\"株洲县\"},{\"n\":\"攸县\"},{\"n\":\"茶陵县\"},{\"n\":\"炎陵县\"},{\"n\":\"醴陵市\"}]},{\"n\":\"湘潭市\",\"s\":[{\"n\":\"雨湖区\"},{\"n\":\"岳塘区\"},{\"n\":\"湘潭县\"},{\"n\":\"湘乡市\"},{\"n\":\"韶山市\"}]},{\"n\":\"衡阳市\",\"s\":[{\"n\":\"珠晖区\"},{\"n\":\"雁峰区\"},{\"n\":\"石鼓区\"},{\"n\":\"蒸湘区\"},{\"n\":\"南岳区\"},{\"n\":\"衡阳县\"},{\"n\":\"衡南县\"},{\"n\":\"衡山县\"},{\"n\":\"衡东县\"},{\"n\":\"祁东县\"},{\"n\":\"耒阳市\"},{\"n\":\"常宁市\"}]},{\"n\":\"邵阳市\",\"s\":[{\"n\":\"双清区\"},{\"n\":\"大祥区\"},{\"n\":\"北塔区\"},{\"n\":\"邵东县\"},{\"n\":\"新邵县\"},{\"n\":\"邵阳县\"},{\"n\":\"隆回县\"},{\"n\":\"洞口县\"},{\"n\":\"绥宁县\"},{\"n\":\"新宁县\"},{\"n\":\"城步苗族自治县\"},{\"n\":\"武冈市\"}]},{\"n\":\"岳阳市\",\"s\":[{\"n\":\"岳阳楼区\"},{\"n\":\"云溪区\"},{\"n\":\"君山区\"},{\"n\":\"岳阳县\"},{\"n\":\"华容县\"},{\"n\":\"湘阴县\"},{\"n\":\"平江县\"},{\"n\":\"汨罗市\"},{\"n\":\"临湘市\"}]},{\"n\":\"常德市\",\"s\":[{\"n\":\"武陵区\"},{\"n\":\"鼎城区\"},{\"n\":\"安乡县\"},{\"n\":\"汉寿县\"},{\"n\":\"澧县\"},{\"n\":\"临澧县\"},{\"n\":\"桃源县\"},{\"n\":\"石门县\"},{\"n\":\"津市市\"}]},{\"n\":\"张家界市\",\"s\":[{\"n\":\"永定区\"},{\"n\":\"武陵源区\"},{\"n\":\"慈利县\"},{\"n\":\"桑植县\"}]},{\"n\":\"益阳市\",\"s\":[{\"n\":\"资阳区\"},{\"n\":\"赫山区\"},{\"n\":\"南县\"},{\"n\":\"桃江县\"},{\"n\":\"安化县\"},{\"n\":\"沅江市\"}]},{\"n\":\"郴州市\",\"s\":[{\"n\":\"北湖区\"},{\"n\":\"苏仙区\"},{\"n\":\"桂阳县\"},{\"n\":\"宜章县\"},{\"n\":\"永兴县\"},{\"n\":\"嘉禾县\"},{\"n\":\"临武县\"},{\"n\":\"汝城县\"},{\"n\":\"桂东县\"},{\"n\":\"安仁县\"},{\"n\":\"资兴市\"}]},{\"n\":\"永州市\",\"s\":[{\"n\":\"零陵区\"},{\"n\":\"冷水滩区\"},{\"n\":\"祁阳县\"},{\"n\":\"东安县\"},{\"n\":\"双牌县\"},{\"n\":\"道县\"},{\"n\":\"江永县\"},{\"n\":\"宁远县\"},{\"n\":\"蓝山县\"},{\"n\":\"新田县\"},{\"n\":\"江华瑶族自治县\"}]},{\"n\":\"怀化市\",\"s\":[{\"n\":\"鹤城区\"},{\"n\":\"中方县\"},{\"n\":\"沅陵县\"},{\"n\":\"辰溪县\"},{\"n\":\"溆浦县\"},{\"n\":\"会同县\"},{\"n\":\"麻阳苗族自治县\"},{\"n\":\"新晃侗族自治县\"},{\"n\":\"芷江侗族自治县\"},{\"n\":\"靖州苗族侗族自治县\"},{\"n\":\"通道侗族自治县\"},{\"n\":\"洪江市\"}]},{\"n\":\"娄底市\",\"s\":[{\"n\":\"娄星区\"},{\"n\":\"双峰县\"},{\"n\":\"新化县\"},{\"n\":\"冷水江市\"},{\"n\":\"涟源市\"}]},{\"n\":\"湘西土家族苗族自治州\",\"s\":[{\"n\":\"吉首市\"},{\"n\":\"泸溪县\"},{\"n\":\"凤凰县\"},{\"n\":\"花垣县\"},{\"n\":\"保靖县\"},{\"n\":\"古丈县\"},{\"n\":\"永顺县\"},{\"n\":\"龙山县\"}]}]},{\"n\":\"广东省\",\"s\":[{\"n\":\"广州市\",\"s\":[{\"n\":\"荔湾区\"},{\"n\":\"越秀区\"},{\"n\":\"海珠区\"},{\"n\":\"天河区\"},{\"n\":\"白云区\"},{\"n\":\"黄埔区\"},{\"n\":\"番禺区\"},{\"n\":\"花都区\"},{\"n\":\"南沙区\"},{\"n\":\"萝岗区\"},{\"n\":\"增城市\"},{\"n\":\"从化市\"}]},{\"n\":\"韶关市\",\"s\":[{\"n\":\"武江区\"},{\"n\":\"浈江区\"},{\"n\":\"曲江区\"},{\"n\":\"始兴县\"},{\"n\":\"仁化县\"},{\"n\":\"翁源县\"},{\"n\":\"乳源瑶族自治县\"},{\"n\":\"新丰县\"},{\"n\":\"乐昌市\"},{\"n\":\"南雄市\"}]},{\"n\":\"深圳市\",\"s\":[{\"n\":\"罗湖区\"},{\"n\":\"福田区\"},{\"n\":\"南山区\"},{\"n\":\"宝安区\"},{\"n\":\"龙岗区\"},{\"n\":\"盐田区\"}]},{\"n\":\"珠海市\",\"s\":[{\"n\":\"香洲区\"},{\"n\":\"斗门区\"},{\"n\":\"金湾区\"}]},{\"n\":\"汕头市\",\"s\":[{\"n\":\"龙湖区\"},{\"n\":\"金平区\"},{\"n\":\"濠江区\"},{\"n\":\"潮阳区\"},{\"n\":\"潮南区\"},{\"n\":\"澄海区\"},{\"n\":\"南澳县\"}]},{\"n\":\"佛山市\",\"s\":[{\"n\":\"禅城区\"},{\"n\":\"南海区\"},{\"n\":\"顺德区\"},{\"n\":\"三水区\"},{\"n\":\"高明区\"}]},{\"n\":\"江门市\",\"s\":[{\"n\":\"蓬江区\"},{\"n\":\"江海区\"},{\"n\":\"新会区\"},{\"n\":\"台山市\"},{\"n\":\"开平市\"},{\"n\":\"鹤山市\"},{\"n\":\"恩平市\"}]},{\"n\":\"湛江市\",\"s\":[{\"n\":\"赤坎区\"},{\"n\":\"霞山区\"},{\"n\":\"坡头区\"},{\"n\":\"麻章区\"},{\"n\":\"遂溪县\"},{\"n\":\"徐闻县\"},{\"n\":\"廉江市\"},{\"n\":\"雷州市\"},{\"n\":\"吴川市\"}]},{\"n\":\"茂名市\",\"s\":[{\"n\":\"茂南区\"},{\"n\":\"茂港区\"},{\"n\":\"电白县\"},{\"n\":\"高州市\"},{\"n\":\"化州市\"},{\"n\":\"信宜市\"}]},{\"n\":\"肇庆市\",\"s\":[{\"n\":\"端州区\"},{\"n\":\"鼎湖区\"},{\"n\":\"广宁县\"},{\"n\":\"怀集县\"},{\"n\":\"封开县\"},{\"n\":\"德庆县\"},{\"n\":\"高要市\"},{\"n\":\"四会市\"}]},{\"n\":\"惠州市\",\"s\":[{\"n\":\"惠城区\"},{\"n\":\"惠阳区\"},{\"n\":\"博罗县\"},{\"n\":\"惠东县\"},{\"n\":\"龙门县\"}]},{\"n\":\"梅州市\",\"s\":[{\"n\":\"梅江区\"},{\"n\":\"梅县\"},{\"n\":\"大埔县\"},{\"n\":\"丰顺县\"},{\"n\":\"五华县\"},{\"n\":\"平远县\"},{\"n\":\"蕉岭县\"},{\"n\":\"兴宁市\"}]},{\"n\":\"汕尾市\",\"s\":[{\"n\":\"城区\"},{\"n\":\"海丰县\"},{\"n\":\"陆河县\"},{\"n\":\"陆丰市\"}]},{\"n\":\"河源市\",\"s\":[{\"n\":\"源城区\"},{\"n\":\"紫金县\"},{\"n\":\"龙川县\"},{\"n\":\"连平县\"},{\"n\":\"和平县\"},{\"n\":\"东源县\"}]},{\"n\":\"阳江市\",\"s\":[{\"n\":\"江城区\"},{\"n\":\"阳西县\"},{\"n\":\"阳东县\"},{\"n\":\"阳春市\"}]},{\"n\":\"清远市\",\"s\":[{\"n\":\"清城区\"},{\"n\":\"清新区\"},{\"n\":\"佛冈县\"},{\"n\":\"阳山县\"},{\"n\":\"连山壮族瑶族自治县\"},{\"n\":\"连南瑶族自治县\"},{\"n\":\"英德市\"},{\"n\":\"连州市\"}]},{\"n\":\"东莞市\"},{\"n\":\"中山市\"},{\"n\":\"潮州市\",\"s\":[{\"n\":\"湘桥区\"},{\"n\":\"潮安区\"},{\"n\":\"饶平县\"}]},{\"n\":\"揭阳市\",\"s\":[{\"n\":\"榕城区\"},{\"n\":\"揭东区\"},{\"n\":\"揭西县\"},{\"n\":\"惠来县\"},{\"n\":\"普宁市\"}]},{\"n\":\"云浮市\",\"s\":[{\"n\":\"云城区\"},{\"n\":\"新兴县\"},{\"n\":\"郁南县\"},{\"n\":\"云安县\"},{\"n\":\"罗定市\"}]}]}]"; + return data; + } + + /** + * 获取用户数据 + */ + @GetMapping("/userModel") + @ResponseBody + public AjaxResult userModel() + { + AjaxResult ajax = new AjaxResult(); + + ajax.put("code", 200); + ajax.put("value", users); + return ajax; + } + + /** + * 获取数据集合 + */ + @GetMapping("/collection") + @ResponseBody + public AjaxResult collection() + { + String[] array = { "ruoyi 1", "ruoyi 2", "ruoyi 3", "ruoyi 4", "ruoyi 5" }; + AjaxResult ajax = new AjaxResult(); + ajax.put("value", array); + return ajax; + } +} + +class UserFormModel +{ + /** 用户ID */ + private int userId; + + /** 用户编号 */ + private String userCode; + + /** 用户姓名 */ + private String userName; + + /** 用户手机 */ + private String userPhone; + + public UserFormModel() + { + + } + + public UserFormModel(int userId, String userCode, String userName, String userPhone) + { + this.userId = userId; + this.userCode = userCode; + this.userName = userName; + this.userPhone = userPhone; + } + + public int getUserId() + { + return userId; + } + + public void setUserId(int userId) + { + this.userId = userId; + } + + public String getUserCode() + { + return userCode; + } + + public void setUserCode(String userCode) + { + this.userCode = userCode; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getUserPhone() + { + return userPhone; + } + + public void setUserPhone(String userPhone) + { + this.userPhone = userPhone; + } + +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoIconController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoIconController.java new file mode 100644 index 000000000..b6884cc1e --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoIconController.java @@ -0,0 +1,35 @@ +package com.ruoyi.web.controller.demo.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * 图标相关 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/icon") +public class DemoIconController +{ + private String prefix = "demo/icon"; + + /** + * FontAwesome图标 + */ + @GetMapping("/fontawesome") + public String fontAwesome() + { + return prefix + "/fontawesome"; + } + + /** + * Glyphicons图标 + */ + @GetMapping("/glyphicons") + public String glyphicons() + { + return prefix + "/glyphicons"; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoOperateController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoOperateController.java new file mode 100644 index 000000000..18cb90ab6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoOperateController.java @@ -0,0 +1,326 @@ +package com.ruoyi.web.controller.demo.controller; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +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.core.text.Convert; +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.web.controller.demo.domain.CustomerModel; +import com.ruoyi.web.controller.demo.domain.UserOperateModel; + +/** + * 操作控制 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/operate") +public class DemoOperateController extends BaseController +{ + private String prefix = "demo/operate"; + + private final static Map users = new LinkedHashMap(); + { + users.put(1, new UserOperateModel(1, "1000001", "测试1", "0", "15888888888", "ry@qq.com", 150.0, "0")); + users.put(2, new UserOperateModel(2, "1000002", "测试2", "1", "15666666666", "ry@qq.com", 180.0, "1")); + users.put(3, new UserOperateModel(3, "1000003", "测试3", "0", "15666666666", "ry@qq.com", 110.0, "1")); + users.put(4, new UserOperateModel(4, "1000004", "测试4", "1", "15666666666", "ry@qq.com", 220.0, "1")); + users.put(5, new UserOperateModel(5, "1000005", "测试5", "0", "15666666666", "ry@qq.com", 140.0, "1")); + users.put(6, new UserOperateModel(6, "1000006", "测试6", "1", "15666666666", "ry@qq.com", 330.0, "1")); + users.put(7, new UserOperateModel(7, "1000007", "测试7", "0", "15666666666", "ry@qq.com", 160.0, "1")); + users.put(8, new UserOperateModel(8, "1000008", "测试8", "1", "15666666666", "ry@qq.com", 170.0, "1")); + users.put(9, new UserOperateModel(9, "1000009", "测试9", "0", "15666666666", "ry@qq.com", 180.0, "1")); + users.put(10, new UserOperateModel(10, "1000010", "测试10", "0", "15666666666", "ry@qq.com", 210.0, "1")); + users.put(11, new UserOperateModel(11, "1000011", "测试11", "1", "15666666666", "ry@qq.com", 110.0, "1")); + users.put(12, new UserOperateModel(12, "1000012", "测试12", "0", "15666666666", "ry@qq.com", 120.0, "1")); + users.put(13, new UserOperateModel(13, "1000013", "测试13", "1", "15666666666", "ry@qq.com", 380.0, "1")); + users.put(14, new UserOperateModel(14, "1000014", "测试14", "0", "15666666666", "ry@qq.com", 280.0, "1")); + users.put(15, new UserOperateModel(15, "1000015", "测试15", "0", "15666666666", "ry@qq.com", 570.0, "1")); + users.put(16, new UserOperateModel(16, "1000016", "测试16", "1", "15666666666", "ry@qq.com", 260.0, "1")); + users.put(17, new UserOperateModel(17, "1000017", "测试17", "1", "15666666666", "ry@qq.com", 210.0, "1")); + users.put(18, new UserOperateModel(18, "1000018", "测试18", "1", "15666666666", "ry@qq.com", 340.0, "1")); + users.put(19, new UserOperateModel(19, "1000019", "测试19", "1", "15666666666", "ry@qq.com", 160.0, "1")); + users.put(20, new UserOperateModel(20, "1000020", "测试20", "1", "15666666666", "ry@qq.com", 220.0, "1")); + users.put(21, new UserOperateModel(21, "1000021", "测试21", "1", "15666666666", "ry@qq.com", 120.0, "1")); + users.put(22, new UserOperateModel(22, "1000022", "测试22", "1", "15666666666", "ry@qq.com", 130.0, "1")); + users.put(23, new UserOperateModel(23, "1000023", "测试23", "1", "15666666666", "ry@qq.com", 490.0, "1")); + users.put(24, new UserOperateModel(24, "1000024", "测试24", "1", "15666666666", "ry@qq.com", 570.0, "1")); + users.put(25, new UserOperateModel(25, "1000025", "测试25", "1", "15666666666", "ry@qq.com", 250.0, "1")); + users.put(26, new UserOperateModel(26, "1000026", "测试26", "1", "15666666666", "ry@qq.com", 250.0, "1")); + } + + /** + * 表格 + */ + @GetMapping("/table") + public String table() + { + return prefix + "/table"; + } + + /** + * 其他 + */ + @GetMapping("/other") + public String other() + { + return prefix + "/other"; + } + + /** + * 查询数据 + */ + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(UserOperateModel userModel) + { + TableDataInfo rspData = new TableDataInfo(); + List userList = new ArrayList(users.values()); + // 查询条件过滤 + if (StringUtils.isNotEmpty(userModel.getSearchValue())) + { + userList.clear(); + for (Map.Entry entry : users.entrySet()) + { + if (entry.getValue().getUserName().equals(userModel.getSearchValue())) + { + userList.add(entry.getValue()); + } + } + } + else if (StringUtils.isNotEmpty(userModel.getUserName())) + { + userList.clear(); + for (Map.Entry entry : users.entrySet()) + { + if (entry.getValue().getUserName().equals(userModel.getUserName())) + { + userList.add(entry.getValue()); + } + } + } + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (null == pageDomain.getPageNum() || null == pageDomain.getPageSize()) + { + rspData.setRows(userList); + rspData.setTotal(userList.size()); + return rspData; + } + Integer pageNum = (pageDomain.getPageNum() - 1) * 10; + Integer pageSize = pageDomain.getPageNum() * 10; + if (pageSize > userList.size()) + { + pageSize = userList.size(); + } + rspData.setRows(userList.subList(pageNum, pageSize)); + rspData.setTotal(userList.size()); + return rspData; + } + + /** + * 新增用户 + */ + @GetMapping("/add") + public String add(ModelMap mmap) + { + return prefix + "/add"; + } + + /** + * 新增保存用户 + */ + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(UserOperateModel user) + { + Integer userId = users.size() + 1; + user.setUserId(userId); + return AjaxResult.success(users.put(userId, user)); + } + + /** + * 新增保存主子表信息 + */ + @PostMapping("/customer/add") + @ResponseBody + public AjaxResult addSave(CustomerModel customerModel) + { + System.out.println(customerModel.toString()); + return AjaxResult.success(); + } + + /** + * 修改用户 + */ + @GetMapping("/edit/{userId}") + public String edit(@PathVariable("userId") Integer userId, ModelMap mmap) + { + mmap.put("user", users.get(userId)); + return prefix + "/edit"; + } + + /** + * 修改保存用户 + */ + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(UserOperateModel user) + { + return AjaxResult.success(users.put(user.getUserId(), user)); + } + + /** + * 导出 + */ + @PostMapping("/export") + @ResponseBody + public AjaxResult export(UserOperateModel user) + { + List list = new ArrayList(users.values()); + ExcelUtil util = new ExcelUtil(UserOperateModel.class); + return util.exportExcel(list, "用户数据"); + } + + /** + * 下载模板 + */ + @GetMapping("/importTemplate") + @ResponseBody + public AjaxResult importTemplate() + { + ExcelUtil util = new ExcelUtil(UserOperateModel.class); + return util.importTemplateExcel("用户数据"); + } + + /** + * 导入数据 + */ + @PostMapping("/importData") + @ResponseBody + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(UserOperateModel.class); + List userList = util.importExcel(file.getInputStream()); + String message = importUser(userList, updateSupport); + return AjaxResult.success(message); + } + + /** + * 删除用户 + */ + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + Integer[] userIds = Convert.toIntArray(ids); + for (Integer userId : userIds) + { + users.remove(userId); + } + return AjaxResult.success(); + } + + /** + * 查看详细 + */ + @GetMapping("/detail/{userId}") + public String detail(@PathVariable("userId") Integer userId, ModelMap mmap) + { + mmap.put("user", users.get(userId)); + return prefix + "/detail"; + } + + @PostMapping("/clean") + @ResponseBody + public AjaxResult clean() + { + users.clear(); + return success(); + } + + /** + * 导入用户数据 + * + * @param userList 用户数据列表 + * @param isUpdateSupport 是否更新支持,如果已存在,则进行更新数据 + * @return 结果 + */ + public String importUser(List userList, Boolean isUpdateSupport) + { + 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 (UserOperateModel user : userList) + { + try + { + // 验证是否存在这个用户 + boolean userFlag = false; + for (Map.Entry entry : users.entrySet()) + { + if (entry.getValue().getUserName().equals(user.getUserName())) + { + userFlag = true; + break; + } + } + if (!userFlag) + { + Integer userId = users.size() + 1; + user.setUserId(userId); + users.put(userId, user); + successNum++; + successMsg.append("
    " + successNum + "、用户 " + user.getUserName() + " 导入成功"); + } + else if (isUpdateSupport) + { + users.put(user.getUserId(), 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()); + } + } + if (failureNum > 0) + { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new ServiceException(failureMsg.toString()); + } + else + { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条,数据如下:"); + } + return successMsg.toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoReportController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoReportController.java new file mode 100644 index 000000000..610100874 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoReportController.java @@ -0,0 +1,53 @@ +package com.ruoyi.web.controller.demo.controller; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * 报表 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/report") +public class DemoReportController +{ + private String prefix = "demo/report"; + + /** + * 百度ECharts + */ + @GetMapping("/echarts") + public String echarts() + { + return prefix + "/echarts"; + } + + /** + * 图表插件 + */ + @GetMapping("/peity") + public String peity() + { + return prefix + "/peity"; + } + + /** + * 线状图插件 + */ + @GetMapping("/sparkline") + public String sparkline() + { + return prefix + "/sparkline"; + } + + /** + * 图表组合 + */ + @GetMapping("/metrics") + public String metrics() + { + return prefix + "/metrics"; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java new file mode 100644 index 000000000..b1b77eb8d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/controller/DemoTableController.java @@ -0,0 +1,599 @@ +package com.ruoyi.web.controller.demo.controller; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.ColumnType; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +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.core.text.Convert; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; + +/** + * 表格相关 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/demo/table") +public class DemoTableController extends BaseController +{ + private String prefix = "demo/table"; + + private final static List users = new ArrayList(); + { + users.add(new UserTableModel(1, "1000001", "测试1", "0", "15888888888", "ry@qq.com", 150.0, "0")); + users.add(new UserTableModel(2, "1000002", "测试2", "1", "15666666666", "ry@qq.com", 180.0, "1")); + users.add(new UserTableModel(3, "1000003", "测试3", "0", "15666666666", "ry@qq.com", 110.0, "1")); + users.add(new UserTableModel(4, "1000004", "测试4", "1", "15666666666", "ry@qq.com", 220.0, "1")); + users.add(new UserTableModel(5, "1000005", "测试5", "0", "15666666666", "ry@qq.com", 140.0, "1")); + users.add(new UserTableModel(6, "1000006", "测试6", "1", "15666666666", "ry@qq.com", 330.0, "1")); + users.add(new UserTableModel(7, "1000007", "测试7", "0", "15666666666", "ry@qq.com", 160.0, "1")); + users.add(new UserTableModel(8, "1000008", "测试8", "1", "15666666666", "ry@qq.com", 170.0, "1")); + users.add(new UserTableModel(9, "1000009", "测试9", "0", "15666666666", "ry@qq.com", 180.0, "1")); + users.add(new UserTableModel(10, "1000010", "测试10", "0", "15666666666", "ry@qq.com", 210.0, "1")); + users.add(new UserTableModel(11, "1000011", "测试11", "1", "15666666666", "ry@qq.com", 110.0, "1")); + users.add(new UserTableModel(12, "1000012", "测试12", "0", "15666666666", "ry@qq.com", 120.0, "1")); + users.add(new UserTableModel(13, "1000013", "测试13", "1", "15666666666", "ry@qq.com", 380.0, "1")); + users.add(new UserTableModel(14, "1000014", "测试14", "0", "15666666666", "ry@qq.com", 280.0, "1")); + users.add(new UserTableModel(15, "1000015", "测试15", "0", "15666666666", "ry@qq.com", 570.0, "1")); + users.add(new UserTableModel(16, "1000016", "测试16", "1", "15666666666", "ry@qq.com", 260.0, "1")); + users.add(new UserTableModel(17, "1000017", "测试17", "1", "15666666666", "ry@qq.com", 210.0, "1")); + users.add(new UserTableModel(18, "1000018", "测试18", "1", "15666666666", "ry@qq.com", 340.0, "1")); + users.add(new UserTableModel(19, "1000019", "测试19", "1", "15666666666", "ry@qq.com", 160.0, "1")); + users.add(new UserTableModel(20, "1000020", "测试20", "1", "15666666666", "ry@qq.com", 220.0, "1")); + users.add(new UserTableModel(21, "1000021", "测试21", "1", "15666666666", "ry@qq.com", 120.0, "1")); + users.add(new UserTableModel(22, "1000022", "测试22", "1", "15666666666", "ry@qq.com", 130.0, "1")); + users.add(new UserTableModel(23, "1000023", "测试23", "1", "15666666666", "ry@qq.com", 490.0, "1")); + users.add(new UserTableModel(24, "1000024", "测试24", "1", "15666666666", "ry@qq.com", 570.0, "1")); + users.add(new UserTableModel(25, "1000025", "测试25", "1", "15666666666", "ry@qq.com", 250.0, "1")); + users.add(new UserTableModel(26, "1000026", "测试26", "1", "15666666666", "ry@qq.com", 250.0, "1")); + } + + private final static List columns = new ArrayList(); + { + columns.add(new UserTableColumn("用户ID", "userId")); + columns.add(new UserTableColumn("用户编号", "userCode")); + columns.add(new UserTableColumn("用户姓名", "userName")); + columns.add(new UserTableColumn("用户手机", "userPhone")); + columns.add(new UserTableColumn("用户邮箱", "userEmail")); + columns.add(new UserTableColumn("用户状态", "status")); + } + + /** + * 搜索相关 + */ + @GetMapping("/search") + public String search() + { + return prefix + "/search"; + } + + /** + * 数据汇总 + */ + @GetMapping("/footer") + public String footer() + { + return prefix + "/footer"; + } + + /** + * 组合表头 + */ + @GetMapping("/groupHeader") + public String groupHeader() + { + return prefix + "/groupHeader"; + } + + /** + * 表格导出 + */ + @GetMapping("/export") + public String export() + { + return prefix + "/export"; + } + + /** + * 表格导出选择列 + */ + @GetMapping("/exportSelected") + public String exportSelected() + { + return prefix + "/exportSelected"; + } + + /** + * 导出数据 + */ + @PostMapping("/exportData") + @ResponseBody + public AjaxResult exportSelected(UserTableModel userModel, String userIds) + { + List userList = new ArrayList(Arrays.asList(new UserTableModel[users.size()])); + Collections.copy(userList, users); + + // 条件过滤 + if (StringUtils.isNotEmpty(userIds)) + { + userList.clear(); + for (Long userId : Convert.toLongArray(userIds)) + { + for (UserTableModel user : users) + { + if (user.getUserId() == userId) + { + userList.add(user); + } + } + } + } + ExcelUtil util = new ExcelUtil(UserTableModel.class); + return util.exportExcel(userList, "用户数据"); + } + + /** + * 翻页记住选择 + */ + @GetMapping("/remember") + public String remember() + { + return prefix + "/remember"; + } + + /** + * 跳转至指定页 + */ + @GetMapping("/pageGo") + public String pageGo() + { + return prefix + "/pageGo"; + } + + /** + * 自定义查询参数 + */ + @GetMapping("/params") + public String params() + { + return prefix + "/params"; + } + + /** + * 多表格 + */ + @GetMapping("/multi") + public String multi() + { + return prefix + "/multi"; + } + + /** + * 点击按钮加载表格 + */ + @GetMapping("/button") + public String button() + { + return prefix + "/button"; + } + + /** + * 直接加载表格数据 + */ + @GetMapping("/data") + public String data(ModelMap mmap) + { + mmap.put("users", users); + return prefix + "/data"; + } + + /** + * 表格冻结列 + */ + @GetMapping("/fixedColumns") + public String fixedColumns() + { + return prefix + "/fixedColumns"; + } + + /** + * 自定义触发事件 + */ + @GetMapping("/event") + public String event() + { + return prefix + "/event"; + } + + /** + * 表格细节视图 + */ + @GetMapping("/detail") + public String detail() + { + return prefix + "/detail"; + } + + /** + * 表格父子视图 + */ + @GetMapping("/child") + public String child() + { + return prefix + "/child"; + } + + /** + * 表格图片预览 + */ + @GetMapping("/image") + public String image() + { + return prefix + "/image"; + } + + /** + * 动态增删改查 + */ + @GetMapping("/curd") + public String curd() + { + return prefix + "/curd"; + } + + /** + * 表格行拖拽操作 + */ + @GetMapping("/reorderRows") + public String reorderRows() + { + return prefix + "/reorderRows"; + } + + /** + * 表格列拖拽操作 + */ + @GetMapping("/reorderColumns") + public String reorderColumns() + { + return prefix + "/reorderColumns"; + } + + /** + * 表格列宽拖动 + */ + @GetMapping("/resizable") + public String resizable() + { + return prefix + "/resizable"; + } + + /** + * 表格行内编辑操作 + */ + @GetMapping("/editable") + public String editable() + { + return prefix + "/editable"; + } + + /** + * 主子表提交 + */ + @GetMapping("/subdata") + public String subdata() + { + return prefix + "/subdata"; + } + + /** + * 表格自动刷新 + */ + @GetMapping("/refresh") + public String refresh() + { + return prefix + "/refresh"; + } + + /** + * 表格打印配置 + */ + @GetMapping("/print") + public String print() + { + return prefix + "/print"; + } + + /** + * 表格标题格式化 + */ + @GetMapping("/headerStyle") + public String headerStyle() + { + return prefix + "/headerStyle"; + } + + /** + * 表格动态列 + */ + @GetMapping("/dynamicColumns") + public String dynamicColumns() + { + return prefix + "/dynamicColumns"; + } + + /** + * 自定义视图分页 + */ + @GetMapping("/customView") + public String customView() + { + return prefix + "/customView"; + } + + /** + * 表格其他操作 + */ + @GetMapping("/other") + public String other() + { + return prefix + "/other"; + } + + /** + * 动态获取列 + */ + @PostMapping("/ajaxColumns") + @ResponseBody + public AjaxResult ajaxColumns(UserTableColumn userColumn) + { + List columnList = new ArrayList(Arrays.asList(new UserTableColumn[columns.size()])); + Collections.copy(columnList, columns); + if (userColumn != null && "userBalance".equals(userColumn.getField())) + { + columnList.add(new UserTableColumn("用户余额", "userBalance")); + } + return AjaxResult.success(columnList); + } + + /** + * 查询数据 + */ + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(UserTableModel userModel) + { + TableDataInfo rspData = new TableDataInfo(); + List userList = new ArrayList(Arrays.asList(new UserTableModel[users.size()])); + Collections.copy(userList, users); + // 查询条件过滤 + if (StringUtils.isNotEmpty(userModel.getUserName())) + { + userList.clear(); + for (UserTableModel user : users) + { + if (user.getUserName().equals(userModel.getUserName())) + { + userList.add(user); + } + } + } + PageDomain pageDomain = TableSupport.buildPageRequest(); + if (null == pageDomain.getPageNum() || null == pageDomain.getPageSize()) + { + rspData.setRows(userList); + rspData.setTotal(userList.size()); + return rspData; + } + Integer pageNum = (pageDomain.getPageNum() - 1) * 10; + Integer pageSize = pageDomain.getPageNum() * 10; + if (pageSize > userList.size()) + { + pageSize = userList.size(); + } + rspData.setRows(userList.subList(pageNum, pageSize)); + rspData.setTotal(userList.size()); + return rspData; + } +} + +class UserTableColumn +{ + /** 表头 */ + private String title; + /** 字段 */ + private String field; + + public UserTableColumn() + { + + } + + public UserTableColumn(String title, String field) + { + this.title = title; + this.field = field; + } + + public String getTitle() + { + return title; + } + + public void setTitle(String title) + { + this.title = title; + } + + public String getField() + { + return field; + } + + public void setField(String field) + { + this.field = field; + } +} + +class UserTableModel +{ + /** 用户ID */ + private int userId; + + /** 用户编号 */ + @Excel(name = "用户编号", cellType = ColumnType.NUMERIC) + private String userCode; + + /** 用户姓名 */ + @Excel(name = "用户姓名") + private String userName; + + /** 用户性别 */ + private String userSex; + + /** 用户手机 */ + @Excel(name = "用户手机") + private String userPhone; + + /** 用户邮箱 */ + @Excel(name = "用户邮箱") + private String userEmail; + + /** 用户余额 */ + @Excel(name = "用户余额", cellType = ColumnType.NUMERIC) + private double userBalance; + + /** 用户状态(0正常 1停用) */ + private String status; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + public UserTableModel() + { + + } + + public UserTableModel(int userId, String userCode, String userName, String userSex, String userPhone, + String userEmail, double userBalance, String status) + { + this.userId = userId; + this.userCode = userCode; + this.userName = userName; + this.userSex = userSex; + this.userPhone = userPhone; + this.userEmail = userEmail; + this.userBalance = userBalance; + this.status = status; + this.createTime = DateUtils.getNowDate(); + } + + public int getUserId() + { + return userId; + } + + public void setUserId(int userId) + { + this.userId = userId; + } + + public String getUserCode() + { + return userCode; + } + + public void setUserCode(String userCode) + { + this.userCode = userCode; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getUserSex() + { + return userSex; + } + + public void setUserSex(String userSex) + { + this.userSex = userSex; + } + + public String getUserPhone() + { + return userPhone; + } + + public void setUserPhone(String userPhone) + { + this.userPhone = userPhone; + } + + public String getUserEmail() + { + return userEmail; + } + + public void setUserEmail(String userEmail) + { + this.userEmail = userEmail; + } + + public double getUserBalance() + { + return userBalance; + } + + public void setUserBalance(double userBalance) + { + this.userBalance = userBalance; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + public Date getCreateTime() + { + return createTime; + } + + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/CustomerModel.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/CustomerModel.java new file mode 100644 index 000000000..5e7d6e55d --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/CustomerModel.java @@ -0,0 +1,116 @@ +package com.ruoyi.web.controller.demo.domain; + +import java.util.List; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 客户测试信息 + * + * @author ruoyi + */ +public class CustomerModel +{ + /** + * 客户姓名 + */ + private String name; + + /** + * 客户手机 + */ + private String phonenumber; + + /** + * 客户性别 + */ + private String sex; + + /** + * 客户生日 + */ + private String birthday; + + /** + * 客户描述 + */ + private String remark; + + /** + * 商品信息 + */ + private List goods; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + 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 getBirthday() + { + return birthday; + } + + public void setBirthday(String birthday) + { + this.birthday = birthday; + } + + public String getRemark() + { + return remark; + } + + public void setRemark(String remark) + { + this.remark = remark; + } + + public List getGoods() + { + return goods; + } + + public void setGoods(List goods) + { + this.goods = goods; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("name", getName()) + .append("phonenumber", getPhonenumber()) + .append("sex", getSex()) + .append("birthday", getBirthday()) + .append("goods", getGoods()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/GoodsModel.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/GoodsModel.java new file mode 100644 index 000000000..b25542a08 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/GoodsModel.java @@ -0,0 +1,99 @@ +package com.ruoyi.web.controller.demo.domain; + +import java.util.Date; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 商品测试信息 + * + * @author ruoyi + */ +public class GoodsModel +{ + /** + * 商品名称 + */ + private String name; + + /** + * 商品重量 + */ + private Integer weight; + + /** + * 商品价格 + */ + private Double price; + + /** + * 商品日期 + */ + private Date date; + + /** + * 商品种类 + */ + private String type; + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Integer getWeight() + { + return weight; + } + + public void setWeight(Integer weight) + { + this.weight = weight; + } + + public Double getPrice() + { + return price; + } + + public void setPrice(Double price) + { + this.price = price; + } + + public Date getDate() + { + return date; + } + + public void setDate(Date date) + { + this.date = date; + } + + public String getType() + { + return type; + } + + public void setType(String type) + { + this.type = type; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("name", getName()) + .append("weight", getWeight()) + .append("price", getPrice()) + .append("date", getDate()) + .append("type", getType()) + .toString(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/UserOperateModel.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/UserOperateModel.java new file mode 100644 index 000000000..8b158aa10 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/demo/domain/UserOperateModel.java @@ -0,0 +1,149 @@ +package com.ruoyi.web.controller.demo.domain; + +import java.util.Date; +import com.ruoyi.common.annotation.Excel; +import com.ruoyi.common.annotation.Excel.Type; +import com.ruoyi.common.core.domain.BaseEntity; +import com.ruoyi.common.utils.DateUtils; + +public class UserOperateModel extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + private int userId; + + @Excel(name = "用户编号") + private String userCode; + + @Excel(name = "用户姓名") + private String userName; + + @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + private String userSex; + + @Excel(name = "用户手机") + private String userPhone; + + @Excel(name = "用户邮箱") + private String userEmail; + + @Excel(name = "用户余额") + private double userBalance; + + @Excel(name = "用户状态", readConverterExp = "0=正常,1=停用") + private String status; + + @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss", type = Type.EXPORT) + private Date createTime; + + public UserOperateModel() + { + + } + + public UserOperateModel(int userId, String userCode, String userName, String userSex, String userPhone, + String userEmail, double userBalance, String status) + { + this.userId = userId; + this.userCode = userCode; + this.userName = userName; + this.userSex = userSex; + this.userPhone = userPhone; + this.userEmail = userEmail; + this.userBalance = userBalance; + this.status = status; + this.createTime = DateUtils.getNowDate(); + } + + public int getUserId() + { + return userId; + } + + public void setUserId(int userId) + { + this.userId = userId; + } + + public String getUserCode() + { + return userCode; + } + + public void setUserCode(String userCode) + { + this.userCode = userCode; + } + + public String getUserName() + { + return userName; + } + + public void setUserName(String userName) + { + this.userName = userName; + } + + public String getUserSex() + { + return userSex; + } + + public void setUserSex(String userSex) + { + this.userSex = userSex; + } + + public String getUserPhone() + { + return userPhone; + } + + public void setUserPhone(String userPhone) + { + this.userPhone = userPhone; + } + + public String getUserEmail() + { + return userEmail; + } + + public void setUserEmail(String userEmail) + { + this.userEmail = userEmail; + } + + public double getUserBalance() + { + return userBalance; + } + + public void setUserBalance(double userBalance) + { + this.userBalance = userBalance; + } + + public String getStatus() + { + return status; + } + + public void setStatus(String status) + { + this.status = status; + } + + @Override + public Date getCreateTime() + { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) + { + this.createTime = createTime; + } +} \ No newline at end of file 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 000000000..f705b03ca --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/CacheController.java @@ -0,0 +1,82 @@ +package com.ruoyi.web.controller.monitor; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.framework.web.service.CacheService; + +/** + * 缓存监控 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/monitor/cache") +public class CacheController extends BaseController +{ + private String prefix = "monitor/cache"; + + @Autowired + private CacheService cacheService; + + @GetMapping() + public String cache(ModelMap mmap) + { + mmap.put("cacheNames", cacheService.getCacheNames()); + return prefix + "/cache"; + } + + @PostMapping("/getNames") + public String getCacheNames(String fragment, ModelMap mmap) + { + mmap.put("cacheNames", cacheService.getCacheNames()); + return prefix + "/cache::" + fragment; + } + + @PostMapping("/getKeys") + public String getCacheKeys(String fragment, String cacheName, ModelMap mmap) + { + mmap.put("cacheName", cacheName); + mmap.put("cacheKyes", cacheService.getCacheKeys(cacheName)); + return prefix + "/cache::" + fragment; + } + + @PostMapping("/getValue") + public String getCacheValue(String fragment, String cacheName, String cacheKey, ModelMap mmap) + { + mmap.put("cacheName", cacheName); + mmap.put("cacheKey", cacheKey); + mmap.put("cacheValue", cacheService.getCacheValue(cacheName, cacheKey)); + return prefix + "/cache::" + fragment; + } + + @PostMapping("/clearCacheName") + @ResponseBody + public AjaxResult clearCacheName(String cacheName, ModelMap mmap) + { + cacheService.clearCacheName(cacheName); + return AjaxResult.success(); + } + + @PostMapping("/clearCacheKey") + @ResponseBody + public AjaxResult clearCacheKey(String cacheName, String cacheKey, ModelMap mmap) + { + cacheService.clearCacheKey(cacheName, cacheKey); + return AjaxResult.success(); + } + + @GetMapping("/clearAll") + @ResponseBody + public AjaxResult clearAll(ModelMap mmap) + { + cacheService.clearAll(); + return AjaxResult.success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java new file mode 100644 index 000000000..a51bf2030 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/DruidController.java @@ -0,0 +1,26 @@ +package com.ruoyi.web.controller.monitor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import com.ruoyi.common.core.controller.BaseController; + +/** + * druid 监控 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/monitor/data") +public class DruidController extends BaseController +{ + private String prefix = "/druid"; + + @RequiresPermissions("monitor:data:view") + @GetMapping() + public String index() + { + return redirect(prefix + "/index.html"); + } +} 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 000000000..386b5c7da --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/ServerController.java @@ -0,0 +1,31 @@ +package com.ruoyi.web.controller.monitor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.framework.web.domain.Server; + +/** + * 服务器监控 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/monitor/server") +public class ServerController extends BaseController +{ + private String prefix = "monitor/server"; + + @RequiresPermissions("monitor:server:view") + @GetMapping() + public String server(ModelMap mmap) throws Exception + { + Server server = new Server(); + server.copyTo(); + mmap.put("server", server); + return prefix + "/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 000000000..0d30e427b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysLogininforController.java @@ -0,0 +1,94 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import com.ruoyi.framework.shiro.service.SysPasswordService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +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.ResponseBody; +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.SysLogininfor; +import com.ruoyi.system.service.ISysLogininforService; + +/** + * 系统访问记录 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/monitor/logininfor") +public class SysLogininforController extends BaseController +{ + private String prefix = "monitor/logininfor"; + + @Autowired + private ISysLogininforService logininforService; + + @Autowired + private SysPasswordService passwordService; + + @RequiresPermissions("monitor:logininfor:view") + @GetMapping() + public String logininfor() + { + return prefix + "/logininfor"; + } + + @RequiresPermissions("monitor:logininfor:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysLogininfor logininfor) + { + startPage(); + List list = logininforService.selectLogininforList(logininfor); + return getDataTable(list); + } + + @Log(title = "登录日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("monitor:logininfor:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysLogininfor logininfor) + { + List list = logininforService.selectLogininforList(logininfor); + ExcelUtil util = new ExcelUtil(SysLogininfor.class); + return util.exportExcel(list, "登录日志"); + } + + @RequiresPermissions("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(logininforService.deleteLogininforByIds(ids)); + } + + @RequiresPermissions("monitor:logininfor:remove") + @Log(title = "登录日志", businessType = BusinessType.CLEAN) + @PostMapping("/clean") + @ResponseBody + public AjaxResult clean() + { + logininforService.cleanLogininfor(); + return success(); + } + + @RequiresPermissions("monitor:logininfor:unlock") + @Log(title = "账户解锁", businessType = BusinessType.OTHER) + @PostMapping("/unlock") + @ResponseBody + public AjaxResult unlock(String loginName) + { + passwordService.clearLoginRecordCache(loginName); + 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 000000000..cddd972dd --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysOperlogController.java @@ -0,0 +1,90 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +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 + */ +@Controller +@RequestMapping("/monitor/operlog") +public class SysOperlogController extends BaseController +{ + private String prefix = "monitor/operlog"; + + @Autowired + private ISysOperLogService operLogService; + + @RequiresPermissions("monitor:operlog:view") + @GetMapping() + public String operlog() + { + return prefix + "/operlog"; + } + + @RequiresPermissions("monitor:operlog:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysOperLog operLog) + { + startPage(); + List list = operLogService.selectOperLogList(operLog); + return getDataTable(list); + } + + @Log(title = "操作日志", businessType = BusinessType.EXPORT) + @RequiresPermissions("monitor:operlog:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysOperLog operLog) + { + List list = operLogService.selectOperLogList(operLog); + ExcelUtil util = new ExcelUtil(SysOperLog.class); + return util.exportExcel(list, "操作日志"); + } + + @Log(title = "操作日志", businessType = BusinessType.DELETE) + @RequiresPermissions("monitor:operlog:remove") + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(operLogService.deleteOperLogByIds(ids)); + } + + @RequiresPermissions("monitor:operlog:detail") + @GetMapping("/detail/{operId}") + public String detail(@PathVariable("operId") Long operId, ModelMap mmap) + { + mmap.put("operLog", operLogService.selectOperLogById(operId)); + return prefix + "/detail"; + } + + @Log(title = "操作日志", businessType = BusinessType.CLEAN) + @RequiresPermissions("monitor:operlog:remove") + @PostMapping("/clean") + @ResponseBody + 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 000000000..19435e941 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/monitor/SysUserOnlineController.java @@ -0,0 +1,88 @@ +package com.ruoyi.web.controller.monitor; + +import java.util.List; +import org.apache.shiro.authz.annotation.Logical; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +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.ResponseBody; +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.enums.OnlineStatus; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.framework.shiro.session.OnlineSession; +import com.ruoyi.framework.shiro.session.OnlineSessionDAO; +import com.ruoyi.system.domain.SysUserOnline; +import com.ruoyi.system.service.ISysUserOnlineService; + +/** + * 在线用户监控 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/monitor/online") +public class SysUserOnlineController extends BaseController +{ + private String prefix = "monitor/online"; + + @Autowired + private ISysUserOnlineService userOnlineService; + + @Autowired + private OnlineSessionDAO onlineSessionDAO; + + @RequiresPermissions("monitor:online:view") + @GetMapping() + public String online() + { + return prefix + "/online"; + } + + @RequiresPermissions("monitor:online:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysUserOnline userOnline) + { + startPage(); + List list = userOnlineService.selectUserOnlineList(userOnline); + return getDataTable(list); + } + + @RequiresPermissions(value = { "monitor:online:batchForceLogout", "monitor:online:forceLogout" }, logical = Logical.OR) + @Log(title = "在线用户", businessType = BusinessType.FORCE) + @PostMapping("/batchForceLogout") + @ResponseBody + public AjaxResult batchForceLogout(String ids) + { + for (String sessionId : Convert.toStrArray(ids)) + { + SysUserOnline online = userOnlineService.selectOnlineById(sessionId); + if (online == null) + { + return error("用户已下线"); + } + OnlineSession onlineSession = (OnlineSession) onlineSessionDAO.readSession(online.getSessionId()); + if (onlineSession == null) + { + return error("用户已下线"); + } + if (sessionId.equals(ShiroUtils.getSessionId())) + { + return error("当前登录用户无法强退"); + } + onlineSessionDAO.delete(onlineSession); + online.setStatus(OnlineStatus.off_line); + userOnlineService.saveOnline(online); + userOnlineService.removeUserCache(online.getLoginName(), sessionId); + } + return success(); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java new file mode 100644 index 000000000..739c46934 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysCaptchaController.java @@ -0,0 +1,92 @@ +package com.ruoyi.web.controller.system; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.servlet.ModelAndView; +import com.google.code.kaptcha.Constants; +import com.google.code.kaptcha.Producer; +import com.ruoyi.common.core.controller.BaseController; + +/** + * 图片验证码(支持算术形式) + * + * @author ruoyi + */ +@Controller +@RequestMapping("/captcha") +public class SysCaptchaController extends BaseController +{ + @Resource(name = "captchaProducer") + private Producer captchaProducer; + + @Resource(name = "captchaProducerMath") + private Producer captchaProducerMath; + + /** + * 验证码生成 + */ + @GetMapping(value = "/captchaImage") + public ModelAndView getKaptchaImage(HttpServletRequest request, HttpServletResponse response) + { + ServletOutputStream out = null; + try + { + HttpSession session = request.getSession(); + response.setDateHeader("Expires", 0); + response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate"); + response.addHeader("Cache-Control", "post-check=0, pre-check=0"); + response.setHeader("Pragma", "no-cache"); + response.setContentType("image/jpeg"); + + String type = request.getParameter("type"); + String capStr = null; + String code = null; + BufferedImage bi = null; + if ("math".equals(type)) + { + String capText = captchaProducerMath.createText(); + capStr = capText.substring(0, capText.lastIndexOf("@")); + code = capText.substring(capText.lastIndexOf("@") + 1); + bi = captchaProducerMath.createImage(capStr); + } + else if ("char".equals(type)) + { + capStr = code = captchaProducer.createText(); + bi = captchaProducer.createImage(capStr); + } + session.setAttribute(Constants.KAPTCHA_SESSION_KEY, code); + out = response.getOutputStream(); + ImageIO.write(bi, "jpg", out); + out.flush(); + + } + catch (Exception e) + { + e.printStackTrace(); + } + finally + { + try + { + if (out != null) + { + out.close(); + } + } + catch (IOException e) + { + e.printStackTrace(); + } + } + return null; + } +} \ No newline at end of file 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 000000000..b329139ae --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysConfigController.java @@ -0,0 +1,157 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.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 + */ +@Controller +@RequestMapping("/system/config") +public class SysConfigController extends BaseController +{ + private String prefix = "system/config"; + + @Autowired + private ISysConfigService configService; + + @RequiresPermissions("system:config:view") + @GetMapping() + public String config() + { + return prefix + "/config"; + } + + /** + * 查询参数配置列表 + */ + @RequiresPermissions("system:config:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysConfig config) + { + startPage(); + List list = configService.selectConfigList(config); + return getDataTable(list); + } + + @Log(title = "参数管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:config:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysConfig config) + { + List list = configService.selectConfigList(config); + ExcelUtil util = new ExcelUtil(SysConfig.class); + return util.exportExcel(list, "参数数据"); + } + + /** + * 新增参数配置 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存参数配置 + */ + @RequiresPermissions("system:config:add") + @Log(title = "参数管理", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysConfig config) + { + if (UserConstants.CONFIG_KEY_NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return error("新增参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setCreateBy(getLoginName()); + return toAjax(configService.insertConfig(config)); + } + + /** + * 修改参数配置 + */ + @GetMapping("/edit/{configId}") + public String edit(@PathVariable("configId") Long configId, ModelMap mmap) + { + mmap.put("config", configService.selectConfigById(configId)); + return prefix + "/edit"; + } + + /** + * 修改保存参数配置 + */ + @RequiresPermissions("system:config:edit") + @Log(title = "参数管理", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysConfig config) + { + if (UserConstants.CONFIG_KEY_NOT_UNIQUE.equals(configService.checkConfigKeyUnique(config))) + { + return error("修改参数'" + config.getConfigName() + "'失败,参数键名已存在"); + } + config.setUpdateBy(getLoginName()); + return toAjax(configService.updateConfig(config)); + } + + /** + * 删除参数配置 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + configService.deleteConfigByIds(ids); + return success(); + } + + /** + * 刷新参数缓存 + */ + @RequiresPermissions("system:config:remove") + @Log(title = "参数管理", businessType = BusinessType.CLEAN) + @GetMapping("/refreshCache") + @ResponseBody + public AjaxResult refreshCache() + { + configService.resetConfigCache(); + return success(); + } + + /** + * 校验参数键名 + */ + @PostMapping("/checkConfigKeyUnique") + @ResponseBody + public String checkConfigKeyUnique(SysConfig config) + { + return configService.checkConfigKeyUnique(config); + } +} 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 000000000..345927d67 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDeptController.java @@ -0,0 +1,224 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; + +import com.ruoyi.system.service.ISysConfigService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.Ztree; +import com.ruoyi.common.core.domain.entity.SysDept; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.system.service.ISysDeptService; + +/** + * 部门信息 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/dept") +public class SysDeptController extends BaseController +{ + private String prefix = "system/dept"; + + @Autowired + private ISysDeptService deptService; + + @Autowired + ISysConfigService configService; + + @RequiresPermissions("system:dept:view") + @GetMapping() + public String dept() + { + return prefix + "/dept"; + } + + @RequiresPermissions("system:dept:list") + @PostMapping("/list") + @ResponseBody + public List list(SysDept dept) + { + List deptList = deptService.selectDeptList(dept); + return deptList; + } + + /** + * 新增部门 + */ + @GetMapping("/add/{parentId}") + public String add(@PathVariable("parentId") Long parentId, ModelMap mmap) + { + if (!getSysUser().isAdmin()) + { + parentId = getSysUser().getDeptId(); + } + mmap.put("dept", deptService.selectDeptById(parentId)); + return prefix + "/add"; + } + + /** + * 新增保存部门 + */ + @Log(title = "部门管理", businessType = BusinessType.INSERT) + @RequiresPermissions("system:dept:add") + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysDept dept) + { + if (UserConstants.DEPT_NAME_NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return error("新增部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + dept.setCreateBy(getLoginName()); + return toAjax(deptService.insertDept(dept)); + } + + /** + * 修改 + */ + @GetMapping("/edit/{deptId}") + public String edit(@PathVariable("deptId") Long deptId, ModelMap mmap) + { + SysDept dept = deptService.selectDeptById(deptId); + if (StringUtils.isNotNull(dept) && 100L == deptId) + { + dept.setParentName("无"); + } + mmap.put("dept", dept); + return prefix + "/edit"; + } + + /** + * 保存 + */ + @Log(title = "部门管理", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:dept:edit") + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysDept dept) + { + if (UserConstants.DEPT_NAME_NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept))) + { + return error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在"); + } + else if (dept.getParentId().equals(dept.getDeptId())) + { + return error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己"); + } + else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) + && deptService.selectNormalChildrenDeptById(dept.getDeptId()) > 0) + { + return AjaxResult.error("该部门包含未停用的子部门!"); + } + dept.setUpdateBy(getLoginName()); + return toAjax(deptService.updateDept(dept)); + } + + /** + * 删除 + */ + @Log(title = "部门管理", businessType = BusinessType.DELETE) + @RequiresPermissions("system:dept:remove") + @GetMapping("/remove/{deptId}") + @ResponseBody + public AjaxResult remove(@PathVariable("deptId") Long deptId) + { + if (deptService.selectDeptCount(deptId) > 0) + { + return AjaxResult.warn("存在下级部门,不允许删除"); + } + if (deptService.checkDeptExistUser(deptId)) + { + return AjaxResult.warn("部门存在用户,不允许删除"); + } + return toAjax(deptService.deleteDeptById(deptId)); + } + + /** + * 校验部门名称 + */ + @PostMapping("/checkDeptNameUnique") + @ResponseBody + public String checkDeptNameUnique(SysDept dept) + { + return deptService.checkDeptNameUnique(dept); + } + + /** + * 选择部门树 + * + * @param deptId 部门ID + * @param excludeId 排除ID + */ + @GetMapping(value = { "/selectDeptTree/{deptId}", "/selectDeptTree/{deptId}/{excludeId}" }) + public String selectDeptTree(@PathVariable("deptId") Long deptId, + @PathVariable(value = "excludeId", required = false) String excludeId, ModelMap mmap) + { + mmap.put("dept", deptService.selectDeptById(deptId)); + mmap.put("excludeId", excludeId); + return prefix + "/tree"; + } + + /** + * 加载部门列表树 + */ + @GetMapping("/treeData") + @ResponseBody + public List treeData() + { + List ztrees = deptService.selectDeptTree(new SysDept()); + return ztrees; + } + + /** + * 加载部门列表树(排除下级) + */ + @GetMapping("/treeData/{excludeId}") + @ResponseBody + public List treeDataExcludeChild(@PathVariable(value = "excludeId", required = false) Long excludeId) + { + SysDept dept = new SysDept(); + dept.setDeptId(excludeId); + List ztrees = deptService.selectDeptTreeExcludeChild(dept); + return ztrees; + } + + /** + * 加载角色部门(数据权限)列表树 + */ + @GetMapping("/roleDeptTreeData") + @ResponseBody + public List deptTreeData(SysRole role) + { + List ztrees = deptService.roleDeptTreeData(role); + return ztrees; + } + + /** + * Ecology部门信息同步 + */ + @Log(title = "部门同步", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:dept:sync") + @PostMapping("/syncDept") + @ResponseBody + public AjaxResult syncDept() { + String url = "http://192.168.2.85:90/api/hrm/resful/getHrmdepartmentWithPage"; + String params = "{\"params\":{\"pagesize\":999999}}"; + return deptService.syncEcologyDept(url, params); + } +} 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 000000000..27d243605 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictDataController.java @@ -0,0 +1,120 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.poi.ExcelUtil; +import com.ruoyi.system.service.ISysDictDataService; + +/** + * 数据字典信息 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/dict/data") +public class SysDictDataController extends BaseController +{ + private String prefix = "system/dict/data"; + + @Autowired + private ISysDictDataService dictDataService; + + @RequiresPermissions("system:dict:view") + @GetMapping() + public String dictData() + { + return prefix + "/data"; + } + + @PostMapping("/list") + @RequiresPermissions("system:dict:list") + @ResponseBody + public TableDataInfo list(SysDictData dictData) + { + startPage(); + List list = dictDataService.selectDictDataList(dictData); + return getDataTable(list); + } + + @Log(title = "字典数据", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysDictData dictData) + { + List list = dictDataService.selectDictDataList(dictData); + ExcelUtil util = new ExcelUtil(SysDictData.class); + return util.exportExcel(list, "字典数据"); + } + + /** + * 新增字典类型 + */ + @GetMapping("/add/{dictType}") + public String add(@PathVariable("dictType") String dictType, ModelMap mmap) + { + mmap.put("dictType", dictType); + return prefix + "/add"; + } + + /** + * 新增保存字典类型 + */ + @Log(title = "字典数据", businessType = BusinessType.INSERT) + @RequiresPermissions("system:dict:add") + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysDictData dict) + { + dict.setCreateBy(getLoginName()); + return toAjax(dictDataService.insertDictData(dict)); + } + + /** + * 修改字典类型 + */ + @GetMapping("/edit/{dictCode}") + public String edit(@PathVariable("dictCode") Long dictCode, ModelMap mmap) + { + mmap.put("dict", dictDataService.selectDictDataById(dictCode)); + return prefix + "/edit"; + } + + /** + * 修改保存字典类型 + */ + @Log(title = "字典数据", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:dict:edit") + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysDictData dict) + { + dict.setUpdateBy(getLoginName()); + return toAjax(dictDataService.updateDictData(dict)); + } + + @Log(title = "字典数据", businessType = BusinessType.DELETE) + @RequiresPermissions("system:dict:remove") + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + dictDataService.deleteDictDataByIds(ids); + 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 000000000..775d52a36 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysDictTypeController.java @@ -0,0 +1,188 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.Ztree; +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 + */ +@Controller +@RequestMapping("/system/dict") +public class SysDictTypeController extends BaseController +{ + private String prefix = "system/dict/type"; + + @Autowired + private ISysDictTypeService dictTypeService; + + @RequiresPermissions("system:dict:view") + @GetMapping() + public String dictType() + { + return prefix + "/type"; + } + + @PostMapping("/list") + @RequiresPermissions("system:dict:list") + @ResponseBody + public TableDataInfo list(SysDictType dictType) + { + startPage(); + List list = dictTypeService.selectDictTypeList(dictType); + return getDataTable(list); + } + + @Log(title = "字典类型", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:dict:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysDictType dictType) + { + + List list = dictTypeService.selectDictTypeList(dictType); + ExcelUtil util = new ExcelUtil(SysDictType.class); + return util.exportExcel(list, "字典类型"); + } + + /** + * 新增字典类型 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.INSERT) + @RequiresPermissions("system:dict:add") + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysDictType dict) + { + if (UserConstants.DICT_TYPE_NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return error("新增字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setCreateBy(getLoginName()); + return toAjax(dictTypeService.insertDictType(dict)); + } + + /** + * 修改字典类型 + */ + @GetMapping("/edit/{dictId}") + public String edit(@PathVariable("dictId") Long dictId, ModelMap mmap) + { + mmap.put("dict", dictTypeService.selectDictTypeById(dictId)); + return prefix + "/edit"; + } + + /** + * 修改保存字典类型 + */ + @Log(title = "字典类型", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:dict:edit") + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysDictType dict) + { + if (UserConstants.DICT_TYPE_NOT_UNIQUE.equals(dictTypeService.checkDictTypeUnique(dict))) + { + return error("修改字典'" + dict.getDictName() + "'失败,字典类型已存在"); + } + dict.setUpdateBy(getLoginName()); + return toAjax(dictTypeService.updateDictType(dict)); + } + + @Log(title = "字典类型", businessType = BusinessType.DELETE) + @RequiresPermissions("system:dict:remove") + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + dictTypeService.deleteDictTypeByIds(ids); + return success(); + } + + /** + * 刷新字典缓存 + */ + @RequiresPermissions("system:dict:remove") + @Log(title = "字典类型", businessType = BusinessType.CLEAN) + @GetMapping("/refreshCache") + @ResponseBody + public AjaxResult refreshCache() + { + dictTypeService.resetDictCache(); + return success(); + } + + /** + * 查询字典详细 + */ + @RequiresPermissions("system:dict:list") + @GetMapping("/detail/{dictId}") + public String detail(@PathVariable("dictId") Long dictId, ModelMap mmap) + { + mmap.put("dict", dictTypeService.selectDictTypeById(dictId)); + mmap.put("dictList", dictTypeService.selectDictTypeAll()); + return "system/dict/data/data"; + } + + /** + * 校验字典类型 + */ + @PostMapping("/checkDictTypeUnique") + @ResponseBody + public String checkDictTypeUnique(SysDictType dictType) + { + return dictTypeService.checkDictTypeUnique(dictType); + } + + /** + * 选择字典树 + */ + @GetMapping("/selectDictTree/{columnId}/{dictType}") + public String selectDeptTree(@PathVariable("columnId") Long columnId, @PathVariable("dictType") String dictType, + ModelMap mmap) + { + mmap.put("columnId", columnId); + mmap.put("dict", dictTypeService.selectDictTypeByType(dictType)); + return prefix + "/tree"; + } + + /** + * 加载字典列表树 + */ + @GetMapping("/treeData") + @ResponseBody + public List treeData() + { + List ztrees = dictTypeService.selectDictTree(new SysDictType()); + return ztrees; + } +} 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 000000000..71b5bdd56 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysIndexController.java @@ -0,0 +1,167 @@ +package com.ruoyi.web.controller.system; + +import java.util.Date; +import java.util.List; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.utils.http.HttpUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +import com.ruoyi.common.config.RuoYiConfig; +import com.ruoyi.common.constant.ShiroConstants; +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.core.domain.entity.SysUser; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.common.utils.CookieUtils; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.shiro.service.SysPasswordService; +import com.ruoyi.system.service.ISysConfigService; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 首页 业务处理 + * + * @author ruoyi + */ +@Controller +public class SysIndexController extends BaseController +{ + @Autowired + private ISysMenuService menuService; + + @Autowired + private ISysConfigService configService; + + @Autowired + private SysPasswordService passwordService; + + // 系统首页 + @GetMapping("/index") + public String index(ModelMap mmap) + { + // 取身份信息 + SysUser user = getSysUser(); + // 根据用户id取出菜单 + List menus = menuService.selectMenusByUser(user); + mmap.put("menus", menus); + mmap.put("user", user); + mmap.put("sideTheme", configService.selectConfigByKey("sys.index.sideTheme")); + mmap.put("skinName", configService.selectConfigByKey("sys.index.skinName")); + mmap.put("ignoreFooter", configService.selectConfigByKey("sys.index.ignoreFooter")); + mmap.put("copyrightYear", RuoYiConfig.getCopyrightYear()); + mmap.put("demoEnabled", RuoYiConfig.isDemoEnabled()); + mmap.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate())); + mmap.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate())); + mmap.put("isMobile", ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent"))); + + // 菜单导航显示风格 + String menuStyle = configService.selectConfigByKey("sys.index.menuStyle"); + // 移动端,默认使左侧导航菜单,否则取默认配置 + String indexStyle = ServletUtils.checkAgentIsMobile(ServletUtils.getRequest().getHeader("User-Agent")) ? "index" : menuStyle; + + // 优先Cookie配置导航菜单 + Cookie[] cookies = ServletUtils.getRequest().getCookies(); + for (Cookie cookie : cookies) + { + if (StringUtils.isNotEmpty(cookie.getName()) && "nav-style".equalsIgnoreCase(cookie.getName())) + { + indexStyle = cookie.getValue(); + break; + } + } + String webIndex = "topnav".equalsIgnoreCase(indexStyle) ? "index-topnav" : "index"; + return webIndex; + } + + // 锁定屏幕 + @GetMapping("/lockscreen") + public String lockscreen(ModelMap mmap) + { + mmap.put("user", getSysUser()); + ServletUtils.getSession().setAttribute(ShiroConstants.LOCK_SCREEN, true); + return "lock"; + } + + // 解锁屏幕 + @PostMapping("/unlockscreen") + @ResponseBody + public AjaxResult unlockscreen(String password) + { + SysUser user = getSysUser(); + if (StringUtils.isNull(user)) + { + return AjaxResult.error("服务器超时,请重新登陆"); + } + if (passwordService.matches(user, password)) + { + ServletUtils.getSession().removeAttribute(ShiroConstants.LOCK_SCREEN); + return AjaxResult.success(); + } + return AjaxResult.error("密码不正确,请重新输入。"); + } + + // 切换主题 + @GetMapping("/system/switchSkin") + public String switchSkin() + { + return "skin"; + } + + // 切换菜单 + @GetMapping("/system/menuStyle/{style}") + public void menuStyle(@PathVariable String style, HttpServletResponse response) + { + CookieUtils.setCookie(response, "nav-style", style); + } + + // 系统介绍 + @GetMapping("/system/main") + public String main(ModelMap mmap) + { + JSONObject wordsJson = JSONObject.parseObject(HttpUtils.sendGet("https://v1.jinrishici.com/all.json","")); + JSONObject oneWordJson = JSONObject.parseObject(HttpUtils.sendGet("https://api.xygeng.cn/one","")); + JSONObject oneWordDataJson = JSONObject.parseObject(oneWordJson.getString("data")); + mmap.put("wordsContent",wordsJson.get("content")); + mmap.put("wordsAuthor",wordsJson.get("author")); + mmap.put("wordsOrigin",wordsJson.getString("origin")); + mmap.put("oneWordOrigin",oneWordDataJson.get("origin")); + mmap.put("oneWordContent",oneWordDataJson.get("content")); + mmap.put("version", RuoYiConfig.getVersion()); + return "main"; + } + + // 检查初始密码是否提醒修改 + public boolean initPasswordIsModify(Date pwdUpdateDate) + { + Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify")); + return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null; + } + + // 检查密码是否过期 + public boolean passwordIsExpiration(Date pwdUpdateDate) + { + Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays")); + if (passwordValidateDays != null && passwordValidateDays > 0) + { + if (StringUtils.isNull(pwdUpdateDate)) + { + // 如果从未修改过初始密码,直接提醒过期 + return true; + } + Date nowDate = DateUtils.getNowDate(); + return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays; + } + return false; + } +} 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 000000000..51323a88a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java @@ -0,0 +1,145 @@ +package com.ruoyi.web.controller.system; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.alibaba.fastjson.JSONObject; +import com.ruoyi.common.utils.http.HttpUtils; +import com.ruoyi.framework.jwt.service.IJwtTokenService; +import com.ruoyi.framework.shiro.util.CustToken; +import com.ruoyi.system.service.IWechatApiService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiImplicitParams; +import io.swagger.annotations.ApiOperation; +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.UsernamePasswordToken; +import org.apache.shiro.subject.Subject; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +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.enums.UserStatus; +import com.ruoyi.common.utils.ServletUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.framework.web.service.ConfigService; +import com.ruoyi.framework.jwt.utils.JwtUtils; +import com.ruoyi.framework.shiro.service.SysPasswordService; +import com.ruoyi.system.service.ISysUserService; + +import java.util.Map; + +/** + * 登录验证 + * + * @author ruoyi + */ +@Api(tags = "生成AccessToken接口") +@Controller +public class SysLoginController extends BaseController +{ + /** + * 是否开启记住我功能 + */ + @Value("${shiro.rememberMe.enabled: false}") + private boolean rememberMe; + + @Autowired + private ConfigService configService; + + @Autowired + private IJwtTokenService jwtTokenService; + + @Autowired + private IWechatApiService wechatApiService; + + @GetMapping("/login") + public String login(HttpServletRequest request, HttpServletResponse response, ModelMap mmap) + { + String loginType= request.getParameter("loginType"); + if(StringUtils.isNotEmpty(loginType) && request.getParameter("loginType").equals("wechat")){ + String code= request.getParameter("code"); + //String state = request.getParameter("state"); + String username=wechatApiService.GetLoginNameWithWechatCode(code); + //如果没有获取到登录名,说明验证失败,跳转登录页 + if(StringUtils.isEmpty(username)){ + return "login"; + } + + String password=""; + mmap.put("loginType","wechat"); + mmap.put("username",username); + mmap.put("password",password); + return "loginwechat"; + + + } + + // 如果是Ajax请求,返回Json字符串。 + if (ServletUtils.isAjaxRequest(request)) + { + return ServletUtils.renderString(response, "{\"code\":\"1\",\"msg\":\"未登录或登录超时。请重新登录\"}"); + } + // 是否开启记住我 + mmap.put("isRemembered", rememberMe); + // 是否开启用户注册 + mmap.put("isAllowRegister", configService.getKey("sys.account.registerUser")); + return "login"; + } + + @PostMapping("/login") + @ResponseBody + public AjaxResult ajaxLogin(String username, String password, Boolean rememberMe,String loginType) + { + // UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe); + CustToken token=new CustToken(username,password,rememberMe,loginType); + Subject subject = SecurityUtils.getSubject(); + try + { + subject.login(token); + return success(); + } + catch (AuthenticationException e) + { + String msg = "用户或密码错误"; + if (StringUtils.isNotEmpty(e.getMessage())) + { + msg = e.getMessage(); + } + return error(msg); + } + } + + @ApiOperation("获取Json格式AccessToken") + @ApiImplicitParams({ + @ApiImplicitParam(name = "username", value = "用户名称", dataType = "String", dataTypeClass = String.class), + @ApiImplicitParam(name = "password", value = "用户密码", dataType = "String", dataTypeClass = String.class), + }) + @PostMapping("/jwt/login") + @ResponseBody + public AjaxResult jwtLogin(String username, String password) + { + return jwtTokenService.AjaxResultJwtToken(username,password); + } + + @ApiOperation("获取String格式AccessToken") + @PostMapping("/jwt/topgplogin") + @ResponseBody + public String topgpJwtLogin(String username, String password) + { + return JSONObject.toJSONString(jwtTokenService.AjaxResultJwtToken(username,password)); + } + + @GetMapping("/unauth") + public String unauth() + { + return "error/unauth"; + } +} 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 000000000..1a63fa0d6 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysMenuController.java @@ -0,0 +1,197 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.Ztree; +import com.ruoyi.common.core.domain.entity.SysMenu; +import com.ruoyi.common.core.domain.entity.SysRole; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.framework.shiro.util.AuthorizationUtils; +import com.ruoyi.system.service.ISysMenuService; + +/** + * 菜单信息 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/menu") +public class SysMenuController extends BaseController +{ + private String prefix = "system/menu"; + + @Autowired + private ISysMenuService menuService; + + @RequiresPermissions("system:menu:view") + @GetMapping() + public String menu() + { + return prefix + "/menu"; + } + + @RequiresPermissions("system:menu:list") + @PostMapping("/list") + @ResponseBody + public List list(SysMenu menu) + { + Long userId = ShiroUtils.getUserId(); + List menuList = menuService.selectMenuList(menu, userId); + return menuList; + } + + /** + * 删除菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.DELETE) + @RequiresPermissions("system:menu:remove") + @GetMapping("/remove/{menuId}") + @ResponseBody + public AjaxResult remove(@PathVariable("menuId") Long menuId) + { + if (menuService.selectCountMenuByParentId(menuId) > 0) + { + return AjaxResult.warn("存在子菜单,不允许删除"); + } + if (menuService.selectCountRoleMenuByMenuId(menuId) > 0) + { + return AjaxResult.warn("菜单已分配,不允许删除"); + } + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.deleteMenuById(menuId)); + } + + /** + * 新增 + */ + @GetMapping("/add/{parentId}") + public String add(@PathVariable("parentId") Long parentId, ModelMap mmap) + { + SysMenu menu = null; + if (0L != parentId) + { + menu = menuService.selectMenuById(parentId); + } + else + { + menu = new SysMenu(); + menu.setMenuId(0L); + menu.setMenuName("主目录"); + } + mmap.put("menu", menu); + return prefix + "/add"; + } + + /** + * 新增保存菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.INSERT) + @RequiresPermissions("system:menu:add") + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysMenu menu) + { + if (UserConstants.MENU_NAME_NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return error("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setCreateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.insertMenu(menu)); + } + + /** + * 修改菜单 + */ + @GetMapping("/edit/{menuId}") + public String edit(@PathVariable("menuId") Long menuId, ModelMap mmap) + { + mmap.put("menu", menuService.selectMenuById(menuId)); + return prefix + "/edit"; + } + + /** + * 修改保存菜单 + */ + @Log(title = "菜单管理", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:menu:edit") + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysMenu menu) + { + if (UserConstants.MENU_NAME_NOT_UNIQUE.equals(menuService.checkMenuNameUnique(menu))) + { + return error("修改菜单'" + menu.getMenuName() + "'失败,菜单名称已存在"); + } + menu.setUpdateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(menuService.updateMenu(menu)); + } + + /** + * 选择菜单图标 + */ + @GetMapping("/icon") + public String icon() + { + return prefix + "/icon"; + } + + /** + * 校验菜单名称 + */ + @PostMapping("/checkMenuNameUnique") + @ResponseBody + public String checkMenuNameUnique(SysMenu menu) + { + return menuService.checkMenuNameUnique(menu); + } + + /** + * 加载角色菜单列表树 + */ + @GetMapping("/roleMenuTreeData") + @ResponseBody + public List roleMenuTreeData(SysRole role) + { + Long userId = ShiroUtils.getUserId(); + List ztrees = menuService.roleMenuTreeData(role, userId); + return ztrees; + } + + /** + * 加载所有菜单列表树 + */ + @GetMapping("/menuTreeData") + @ResponseBody + public List menuTreeData() + { + Long userId = ShiroUtils.getUserId(); + List ztrees = menuService.menuTreeData(userId); + return ztrees; + } + + /** + * 选择菜单树 + */ + @GetMapping("/selectMenuTree/{menuId}") + public String selectMenuTree(@PathVariable("menuId") Long menuId, ModelMap mmap) + { + mmap.put("menu", menuService.selectMenuById(menuId)); + return prefix + "/tree"; + } +} \ 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 000000000..cace4adbb --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysNoticeController.java @@ -0,0 +1,111 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.ResponseBody; +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 + */ +@Controller +@RequestMapping("/system/notice") +public class SysNoticeController extends BaseController +{ + private String prefix = "system/notice"; + + @Autowired + private ISysNoticeService noticeService; + + @RequiresPermissions("system:notice:view") + @GetMapping() + public String notice() + { + return prefix + "/notice"; + } + + /** + * 查询公告列表 + */ + @RequiresPermissions("system:notice:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysNotice notice) + { + startPage(); + List list = noticeService.selectNoticeList(notice); + return getDataTable(list); + } + + /** + * 新增公告 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存公告 + */ + @RequiresPermissions("system:notice:add") + @Log(title = "通知公告", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(SysNotice notice) + { + notice.setCreateBy(getLoginName()); + return toAjax(noticeService.insertNotice(notice)); + } + + /** + * 修改公告 + */ + @GetMapping("/edit/{noticeId}") + public String edit(@PathVariable("noticeId") Long noticeId, ModelMap mmap) + { + mmap.put("notice", noticeService.selectNoticeById(noticeId)); + return prefix + "/edit"; + } + + /** + * 修改保存公告 + */ + @RequiresPermissions("system:notice:edit") + @Log(title = "通知公告", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(SysNotice notice) + { + notice.setUpdateBy(getLoginName()); + return toAjax(noticeService.updateNotice(notice)); + } + + /** + * 删除公告 + */ + @RequiresPermissions("system:notice:remove") + @Log(title = "通知公告", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(noticeService.deleteNoticeByIds(ids)); + } +} 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 000000000..b6db5a783 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysPostController.java @@ -0,0 +1,162 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.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 + */ +@Controller +@RequestMapping("/system/post") +public class SysPostController extends BaseController +{ + private String prefix = "system/post"; + + @Autowired + private ISysPostService postService; + + @RequiresPermissions("system:post:view") + @GetMapping() + public String operlog() + { + return prefix + "/post"; + } + + @RequiresPermissions("system:post:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysPost post) + { + startPage(); + List list = postService.selectPostList(post); + return getDataTable(list); + } + + @Log(title = "岗位管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:post:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysPost post) + { + List list = postService.selectPostList(post); + ExcelUtil util = new ExcelUtil(SysPost.class); + return util.exportExcel(list, "岗位数据"); + } + + @RequiresPermissions("system:post:remove") + @Log(title = "岗位管理", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + try + { + return toAjax(postService.deletePostByIds(ids)); + } + catch (Exception e) + { + return error(e.getMessage()); + } + } + + /** + * 新增岗位 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存岗位 + */ + @RequiresPermissions("system:post:add") + @Log(title = "岗位管理", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysPost post) + { + if (UserConstants.POST_NAME_NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.POST_CODE_NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return error("新增岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setCreateBy(getLoginName()); + return toAjax(postService.insertPost(post)); + } + + /** + * 修改岗位 + */ + @GetMapping("/edit/{postId}") + public String edit(@PathVariable("postId") Long postId, ModelMap mmap) + { + mmap.put("post", postService.selectPostById(postId)); + return prefix + "/edit"; + } + + /** + * 修改保存岗位 + */ + @RequiresPermissions("system:post:edit") + @Log(title = "岗位管理", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysPost post) + { + if (UserConstants.POST_NAME_NOT_UNIQUE.equals(postService.checkPostNameUnique(post))) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位名称已存在"); + } + else if (UserConstants.POST_CODE_NOT_UNIQUE.equals(postService.checkPostCodeUnique(post))) + { + return error("修改岗位'" + post.getPostName() + "'失败,岗位编码已存在"); + } + post.setUpdateBy(getLoginName()); + return toAjax(postService.updatePost(post)); + } + + /** + * 校验岗位名称 + */ + @PostMapping("/checkPostNameUnique") + @ResponseBody + public String checkPostNameUnique(SysPost post) + { + return postService.checkPostNameUnique(post); + } + + /** + * 校验岗位编码 + */ + @PostMapping("/checkPostCodeUnique") + @ResponseBody + public String checkPostCodeUnique(SysPost post) + { + return postService.checkPostCodeUnique(post); + } +} 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 000000000..052627a2b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysProfileController.java @@ -0,0 +1,187 @@ +package com.ruoyi.web.controller.system; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +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.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +import com.ruoyi.common.annotation.Log; +import com.ruoyi.common.config.RuoYiConfig; +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.SysUser; +import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.DateUtils; +import com.ruoyi.common.utils.ShiroUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.file.FileUploadUtils; +import com.ruoyi.framework.shiro.service.SysPasswordService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 个人信息 业务处理 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/user/profile") +public class SysProfileController extends BaseController +{ + private static final Logger log = LoggerFactory.getLogger(SysProfileController.class); + + private String prefix = "system/user/profile"; + + @Autowired + private ISysUserService userService; + + @Autowired + private SysPasswordService passwordService; + + /** + * 个人信息 + */ + @GetMapping() + public String profile(ModelMap mmap) + { + SysUser user = getSysUser(); + mmap.put("user", user); + mmap.put("roleGroup", userService.selectUserRoleGroup(user.getUserId())); + mmap.put("postGroup", userService.selectUserPostGroup(user.getUserId())); + return prefix + "/profile"; + } + + @GetMapping("/checkPassword") + @ResponseBody + public boolean checkPassword(String password) + { + SysUser user = getSysUser(); + if (passwordService.matches(user, password)) + { + return true; + } + return false; + } + + @GetMapping("/resetPwd") + public String resetPwd(ModelMap mmap) + { + SysUser user = getSysUser(); + mmap.put("user", userService.selectUserById(user.getUserId())); + return prefix + "/resetPwd"; + } + + @Log(title = "重置密码", businessType = BusinessType.UPDATE) + @PostMapping("/resetPwd") + @ResponseBody + public AjaxResult resetPwd(String oldPassword, String newPassword) + { + SysUser user = getSysUser(); + if (!passwordService.matches(user, oldPassword)) + { + return error("修改密码失败,旧密码错误"); + } + if (passwordService.matches(user, newPassword)) + { + return error("新密码不能与旧密码相同"); + } + user.setSalt(ShiroUtils.randomSalt()); + user.setPassword(passwordService.encryptPassword(user.getLoginName(), newPassword, user.getSalt())); + user.setPwdUpdateDate(DateUtils.getNowDate()); + if (userService.resetUserPwd(user) > 0) + { + setSysUser(userService.selectUserById(user.getUserId())); + return success(); + } + return error("修改密码异常,请联系管理员"); + } + + /** + * 修改用户 + */ + @GetMapping("/edit") + public String edit(ModelMap mmap) + { + SysUser user = getSysUser(); + mmap.put("user", userService.selectUserById(user.getUserId())); + return prefix + "/edit"; + } + + /** + * 修改头像 + */ + @GetMapping("/avatar") + public String avatar(ModelMap mmap) + { + SysUser user = getSysUser(); + mmap.put("user", userService.selectUserById(user.getUserId())); + return prefix + "/avatar"; + } + + /** + * 修改用户 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PostMapping("/update") + @ResponseBody + public AjaxResult update(SysUser user) + { + SysUser currentUser = getSysUser(); + currentUser.setUserName(user.getUserName()); + currentUser.setEmail(user.getEmail()); + currentUser.setPhonenumber(user.getPhonenumber()); + currentUser.setSex(user.getSex()); + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.USER_PHONE_NOT_UNIQUE.equals(userService.checkPhoneUnique(currentUser))) + { + return error("修改用户'" + currentUser.getLoginName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.USER_EMAIL_NOT_UNIQUE.equals(userService.checkEmailUnique(currentUser))) + { + return error("修改用户'" + currentUser.getLoginName() + "'失败,邮箱账号已存在"); + } + if (userService.updateUserInfo(currentUser) > 0) + { + setSysUser(userService.selectUserById(currentUser.getUserId())); + return success(); + } + return error(); + } + + /** + * 保存头像 + */ + @Log(title = "个人信息", businessType = BusinessType.UPDATE) + @PostMapping("/updateAvatar") + @ResponseBody + public AjaxResult updateAvatar(@RequestParam("avatarfile") MultipartFile file) + { + SysUser currentUser = getSysUser(); + try + { + if (!file.isEmpty()) + { + String avatar = FileUploadUtils.upload(RuoYiConfig.getAvatarPath(), file); + currentUser.setAvatar(avatar); + if (userService.updateUserInfo(currentUser) > 0) + { + setSysUser(userService.selectUserById(currentUser.getUserId())); + return success(); + } + } + return error(); + } + catch (Exception e) + { + log.error("修改头像失败!", e); + return error(e.getMessage()); + } + } +} 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 000000000..be71869c9 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRegisterController.java @@ -0,0 +1,46 @@ +package com.ruoyi.web.controller.system; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.ResponseBody; +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.framework.shiro.service.SysRegisterService; +import com.ruoyi.system.service.ISysConfigService; + +/** + * 注册验证 + * + * @author ruoyi + */ +@Controller +public class SysRegisterController extends BaseController +{ + @Autowired + private SysRegisterService registerService; + + @Autowired + private ISysConfigService configService; + + @GetMapping("/register") + public String register() + { + return "register"; + } + + @PostMapping("/register") + @ResponseBody + public AjaxResult ajaxRegister(SysUser 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 000000000..065125f5c --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java @@ -0,0 +1,300 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +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.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.poi.ExcelUtil; +import com.ruoyi.framework.shiro.util.AuthorizationUtils; +import com.ruoyi.system.domain.SysUserRole; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 角色信息 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/role") +public class SysRoleController extends BaseController +{ + private String prefix = "system/role"; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysUserService userService; + + @RequiresPermissions("system:role:view") + @GetMapping() + public String role() + { + return prefix + "/role"; + } + + @RequiresPermissions("system:role:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysRole role) + { + startPage(); + List list = roleService.selectRoleList(role); + return getDataTable(list); + } + + @Log(title = "角色管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:role:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysRole role) + { + List list = roleService.selectRoleList(role); + ExcelUtil util = new ExcelUtil(SysRole.class); + return util.exportExcel(list, "角色数据"); + } + + /** + * 新增角色 + */ + @GetMapping("/add") + public String add() + { + return prefix + "/add"; + } + + /** + * 新增保存角色 + */ + @RequiresPermissions("system:role:add") + @Log(title = "角色管理", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysRole role) + { + if (UserConstants.ROLE_NAME_NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.ROLE_KEY_NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return error("新增角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setCreateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(roleService.insertRole(role)); + + } + + /** + * 修改角色 + */ + @GetMapping("/edit/{roleId}") + public String edit(@PathVariable("roleId") Long roleId, ModelMap mmap) + { + mmap.put("role", roleService.selectRoleById(roleId)); + return prefix + "/edit"; + } + + /** + * 修改保存角色 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysRole role) + { + roleService.checkRoleAllowed(role); + if (UserConstants.ROLE_NAME_NOT_UNIQUE.equals(roleService.checkRoleNameUnique(role))) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色名称已存在"); + } + else if (UserConstants.ROLE_KEY_NOT_UNIQUE.equals(roleService.checkRoleKeyUnique(role))) + { + return error("修改角色'" + role.getRoleName() + "'失败,角色权限已存在"); + } + role.setUpdateBy(getLoginName()); + AuthorizationUtils.clearAllCachedAuthorizationInfo(); + return toAjax(roleService.updateRole(role)); + } + + /** + * 角色分配数据权限 + */ + @GetMapping("/authDataScope/{roleId}") + public String authDataScope(@PathVariable("roleId") Long roleId, ModelMap mmap) + { + mmap.put("role", roleService.selectRoleById(roleId)); + return prefix + "/dataScope"; + } + + /** + * 保存角色分配数据权限 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @PostMapping("/authDataScope") + @ResponseBody + public AjaxResult authDataScopeSave(SysRole role) + { + roleService.checkRoleAllowed(role); + role.setUpdateBy(getLoginName()); + if (roleService.authDataScope(role) > 0) + { + setSysUser(userService.selectUserById(getUserId())); + return success(); + } + return error(); + } + + @RequiresPermissions("system:role:remove") + @Log(title = "角色管理", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + return toAjax(roleService.deleteRoleByIds(ids)); + } + + /** + * 校验角色名称 + */ + @PostMapping("/checkRoleNameUnique") + @ResponseBody + public String checkRoleNameUnique(SysRole role) + { + return roleService.checkRoleNameUnique(role); + } + + /** + * 校验角色权限 + */ + @PostMapping("/checkRoleKeyUnique") + @ResponseBody + public String checkRoleKeyUnique(SysRole role) + { + return roleService.checkRoleKeyUnique(role); + } + + /** + * 选择菜单树 + */ + @GetMapping("/selectMenuTree") + public String selectMenuTree() + { + return prefix + "/tree"; + } + + /** + * 角色状态修改 + */ + @Log(title = "角色管理", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:role:edit") + @PostMapping("/changeStatus") + @ResponseBody + public AjaxResult changeStatus(SysRole role) + { + roleService.checkRoleAllowed(role); + return toAjax(roleService.changeStatus(role)); + } + + /** + * 分配用户 + */ + @RequiresPermissions("system:role:edit") + @GetMapping("/authUser/{roleId}") + public String authUser(@PathVariable("roleId") Long roleId, ModelMap mmap) + { + mmap.put("role", roleService.selectRoleById(roleId)); + return prefix + "/authUser"; + } + + /** + * 查询已分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @PostMapping("/authUser/allocatedList") + @ResponseBody + public TableDataInfo allocatedList(SysUser user) + { + startPage(); + List list = userService.selectAllocatedList(user); + return getDataTable(list); + } + + /** + * 取消授权 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PostMapping("/authUser/cancel") + @ResponseBody + public AjaxResult cancelAuthUser(SysUserRole userRole) + { + return toAjax(roleService.deleteAuthUser(userRole)); + } + + /** + * 批量取消授权 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PostMapping("/authUser/cancelAll") + @ResponseBody + public AjaxResult cancelAuthUserAll(Long roleId, String userIds) + { + return toAjax(roleService.deleteAuthUsers(roleId, userIds)); + } + + /** + * 选择用户 + */ + @GetMapping("/authUser/selectUser/{roleId}") + public String selectUser(@PathVariable("roleId") Long roleId, ModelMap mmap) + { + mmap.put("role", roleService.selectRoleById(roleId)); + return prefix + "/selectUser"; + } + + /** + * 查询未分配用户角色列表 + */ + @RequiresPermissions("system:role:list") + @PostMapping("/authUser/unallocatedList") + @ResponseBody + public TableDataInfo unallocatedList(SysUser user) + { + startPage(); + List list = userService.selectUnallocatedList(user); + return getDataTable(list); + } + + /** + * 批量选择用户授权 + */ + @RequiresPermissions("system:role:edit") + @Log(title = "角色管理", businessType = BusinessType.GRANT) + @PostMapping("/authUser/selectAll") + @ResponseBody + public AjaxResult selectAuthUserAll(Long roleId, String userIds) + { + return toAjax(roleService.insertAuthUsers(roleId, userIds)); + } +} \ No newline at end of file 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 000000000..0e3baf644 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysUserController.java @@ -0,0 +1,312 @@ +package com.ruoyi.web.controller.system; + +import java.util.List; +import java.util.stream.Collectors; +import org.apache.commons.lang3.ArrayUtils; + +import com.ruoyi.system.service.ISysConfigService; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.validation.annotation.Validated; +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.ResponseBody; +import org.springframework.web.multipart.MultipartFile; +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.SysRole; +import com.ruoyi.common.core.domain.entity.SysUser; +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.ShiroUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.common.utils.poi.ExcelUtil; +import com.ruoyi.framework.shiro.service.SysPasswordService; +import com.ruoyi.system.service.ISysPostService; +import com.ruoyi.system.service.ISysRoleService; +import com.ruoyi.system.service.ISysUserService; + +/** + * 用户信息 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/system/user") +public class SysUserController extends BaseController +{ + private String prefix = "system/user"; + + @Autowired + private ISysUserService userService; + + @Autowired + private ISysRoleService roleService; + + @Autowired + private ISysPostService postService; + + @Autowired + private SysPasswordService passwordService; + + @Autowired + private ISysConfigService configService; + + @RequiresPermissions("system:user:view") + @GetMapping() + public String user() + { + return prefix + "/user"; + } + + @RequiresPermissions("system:user:list") + @PostMapping("/list") + @ResponseBody + public TableDataInfo list(SysUser user) + { + startPage(); + List list = userService.selectUserList(user); + return getDataTable(list); + } + + @Log(title = "用户管理", businessType = BusinessType.EXPORT) + @RequiresPermissions("system:user:export") + @PostMapping("/export") + @ResponseBody + public AjaxResult export(SysUser user) + { + List list = userService.selectUserList(user); + ExcelUtil util = new ExcelUtil(SysUser.class); + return util.exportExcel(list, "用户数据"); + } + + @Log(title = "用户管理", businessType = BusinessType.IMPORT) + @RequiresPermissions("system:user:import") + @PostMapping("/importData") + @ResponseBody + public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception + { + ExcelUtil util = new ExcelUtil(SysUser.class); + List userList = util.importExcel(file.getInputStream()); + String message = userService.importUser(userList, updateSupport, getLoginName()); + return AjaxResult.success(message); + } + + @RequiresPermissions("system:user:view") + @GetMapping("/importTemplate") + @ResponseBody + public AjaxResult importTemplate() + { + ExcelUtil util = new ExcelUtil(SysUser.class); + return util.importTemplateExcel("用户数据"); + } + + /** + * 新增用户 + */ + @GetMapping("/add") + public String add(ModelMap mmap) + { + mmap.put("roles", roleService.selectRoleAll().stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + mmap.put("posts", postService.selectPostAll()); + return prefix + "/add"; + } + + /** + * 新增保存用户 + */ + @RequiresPermissions("system:user:add") + @Log(title = "用户管理", businessType = BusinessType.INSERT) + @PostMapping("/add") + @ResponseBody + public AjaxResult addSave(@Validated SysUser user) + { + if (UserConstants.USER_NAME_NOT_UNIQUE.equals(userService.checkLoginNameUnique(user.getLoginName()))) + { + return error("新增用户'" + user.getLoginName() + "'失败,登录账号已存在"); + } + else if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.USER_PHONE_NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return error("新增用户'" + user.getLoginName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.USER_EMAIL_NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return error("新增用户'" + user.getLoginName() + "'失败,邮箱账号已存在"); + } + user.setSalt(ShiroUtils.randomSalt()); + user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt())); + user.setCreateBy(getLoginName()); + return toAjax(userService.insertUser(user)); + } + + /** + * 修改用户 + */ + @GetMapping("/edit/{userId}") + public String edit(@PathVariable("userId") Long userId, ModelMap mmap) + { + List roles = roleService.selectRolesByUserId(userId); + mmap.put("user", userService.selectUserById(userId)); + mmap.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + mmap.put("posts", postService.selectPostsByUserId(userId)); + return prefix + "/edit"; + } + + /** + * 修改保存用户 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @PostMapping("/edit") + @ResponseBody + public AjaxResult editSave(@Validated SysUser user) + { + userService.checkUserAllowed(user); + if (StringUtils.isNotEmpty(user.getPhonenumber()) + && UserConstants.USER_PHONE_NOT_UNIQUE.equals(userService.checkPhoneUnique(user))) + { + return error("修改用户'" + user.getLoginName() + "'失败,手机号码已存在"); + } + else if (StringUtils.isNotEmpty(user.getEmail()) + && UserConstants.USER_EMAIL_NOT_UNIQUE.equals(userService.checkEmailUnique(user))) + { + return error("修改用户'" + user.getLoginName() + "'失败,邮箱账号已存在"); + } + user.setUpdateBy(getLoginName()); + return toAjax(userService.updateUser(user)); + } + + @RequiresPermissions("system:user:resetPwd") + @GetMapping("/resetPwd/{userId}") + public String resetPwd(@PathVariable("userId") Long userId, ModelMap mmap) + { + mmap.put("user", userService.selectUserById(userId)); + return prefix + "/resetPwd"; + } + + @RequiresPermissions("system:user:resetPwd") + @Log(title = "重置密码", businessType = BusinessType.UPDATE) + @PostMapping("/resetPwd") + @ResponseBody + public AjaxResult resetPwdSave(SysUser user) + { + userService.checkUserAllowed(user); + user.setSalt(ShiroUtils.randomSalt()); + user.setPassword(passwordService.encryptPassword(user.getLoginName(), user.getPassword(), user.getSalt())); + if (userService.resetUserPwd(user) > 0) + { + if (ShiroUtils.getUserId().longValue() == user.getUserId().longValue()) + { + setSysUser(userService.selectUserById(user.getUserId())); + } + return success(); + } + return error(); + } + + /** + * 进入授权角色页 + */ + @GetMapping("/authRole/{userId}") + public String authRole(@PathVariable("userId") Long userId, ModelMap mmap) + { + SysUser user = userService.selectUserById(userId); + // 获取用户所属的角色列表 + List roles = roleService.selectRolesByUserId(userId); + mmap.put("user", user); + mmap.put("roles", SysUser.isAdmin(userId) ? roles : roles.stream().filter(r -> !r.isAdmin()).collect(Collectors.toList())); + return prefix + "/authRole"; + } + + /** + * 用户授权角色 + */ + @RequiresPermissions("system:user:edit") + @Log(title = "用户管理", businessType = BusinessType.GRANT) + @PostMapping("/authRole/insertAuthRole") + @ResponseBody + public AjaxResult insertAuthRole(Long userId, Long[] roleIds) + { + userService.insertUserAuth(userId, roleIds); + return success(); + } + + @RequiresPermissions("system:user:remove") + @Log(title = "用户管理", businessType = BusinessType.DELETE) + @PostMapping("/remove") + @ResponseBody + public AjaxResult remove(String ids) + { + if (ArrayUtils.contains(Convert.toLongArray(ids), getUserId())) + { + return error("当前用户不能删除"); + } + return toAjax(userService.deleteUserByIds(ids)); + } + + /** + * 校验用户名 + */ + @PostMapping("/checkLoginNameUnique") + @ResponseBody + public String checkLoginNameUnique(SysUser user) + { + return userService.checkLoginNameUnique(user.getLoginName()); + } + + /** + * 校验手机号码 + */ + @PostMapping("/checkPhoneUnique") + @ResponseBody + public String checkPhoneUnique(SysUser user) + { + return userService.checkPhoneUnique(user); + } + + /** + * 校验email邮箱 + */ + @PostMapping("/checkEmailUnique") + @ResponseBody + public String checkEmailUnique(SysUser user) + { + return userService.checkEmailUnique(user); + } + + /** + * 用户状态修改 + */ + @Log(title = "用户管理", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:user:edit") + @PostMapping("/changeStatus") + @ResponseBody + public AjaxResult changeStatus(SysUser user) + { + userService.checkUserAllowed(user); + return toAjax(userService.changeStatus(user)); + } + + /** + * Ecology人员信息同步 + */ + @Log(title = "人员同步", businessType = BusinessType.UPDATE) + @RequiresPermissions("system:user:sync") + @PostMapping("/syncUser") + @ResponseBody + public AjaxResult syncUser() { + String url = "http://192.168.2.85:90/api/hrm/resful/getHrmUserInfoWithPage"; + String params = "{\"params\":{\"pagesize\":999999}}"; + return userService.syncEcologyUser(url, params); + } + +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BuildController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BuildController.java new file mode 100644 index 000000000..4d3c1287a --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/BuildController.java @@ -0,0 +1,26 @@ +package com.ruoyi.web.controller.tool; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import com.ruoyi.common.core.controller.BaseController; + +/** + * build 表单构建 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/tool/build") +public class BuildController extends BaseController +{ + private String prefix = "tool/build"; + + @RequiresPermissions("tool:build:view") + @GetMapping() + public String build() + { + return prefix + "/build"; + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java new file mode 100644 index 000000000..de2ae6c53 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/SwaggerController.java @@ -0,0 +1,24 @@ +package com.ruoyi.web.controller.tool; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import com.ruoyi.common.core.controller.BaseController; + +/** + * swagger 接口 + * + * @author ruoyi + */ +@Controller +@RequestMapping("/tool/swagger") +public class SwaggerController extends BaseController +{ + @RequiresPermissions("tool:swagger:view") + @GetMapping() + public String index() + { + return redirect("/swagger-ui/index.html"); + } +} 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 000000000..3429cfbe1 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/tool/TestController.java @@ -0,0 +1,181 @@ +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.AjaxResult; +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 AjaxResult userList() + { + List userList = new ArrayList(users.values()); + return AjaxResult.success(userList); + } + + @ApiOperation("获取用户详细") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path",dataTypeClass = Integer.class ) + @GetMapping("/{userId}") + public AjaxResult getUser(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + return AjaxResult.success(users.get(userId)); + } + else + { + return error("用户不存在"); + } + } + + @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 AjaxResult save(UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return error("用户ID不能为空"); + } + return AjaxResult.success(users.put(user.getUserId(), user)); + } + + @ApiOperation("更新用户") + @PutMapping("/update") + public AjaxResult update(@RequestBody UserEntity user) + { + if (StringUtils.isNull(user) || StringUtils.isNull(user.getUserId())) + { + return error("用户ID不能为空"); + } + if (users.isEmpty() || !users.containsKey(user.getUserId())) + { + return error("用户不存在"); + } + users.remove(user.getUserId()); + return AjaxResult.success(users.put(user.getUserId(), user)); + } + + @ApiOperation("删除用户信息") + @ApiImplicitParam(name = "userId", value = "用户ID", required = true, dataType = "int", paramType = "path", dataTypeClass = Integer.class) + @DeleteMapping("/{userId}") + public AjaxResult delete(@PathVariable Integer userId) + { + if (!users.isEmpty() && users.containsKey(userId)) + { + users.remove(userId); + return success(); + } + else + { + return error("用户不存在"); + } + } +} + +@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 000000000..81e4e16b4 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java @@ -0,0 +1,67 @@ +package com.ruoyi.web.core.config; + +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 springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; + +/** + * Swagger2的接口配置 + * + * @author ruoyi + */ +@Configuration +public class SwaggerConfig +{ + /** 是否开启swagger */ + @Value("${swagger.enabled}") + private boolean enabled; + + /** + * 创建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(); + } + + /** + * 添加摘要信息 + */ + private ApiInfo apiInfo() + { + // 用ApiInfoBuilder进行定制 + return new ApiInfoBuilder() + // 设置标题 + .title("BPS管理系统_接口文档") + // 描述 + .description("描述:用于管理系统的各类接口的文档说明。") + // 作者信息 + .contact(new Contact(RuoYiConfig.getName(), null, null)) + // 版本 + .version("版本号:" + RuoYiConfig.getVersion()) + .build(); + } +} diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml new file mode 100644 index 000000000..31e6f0439 --- /dev/null +++ b/ruoyi-admin/src/main/resources/application.yml @@ -0,0 +1,197 @@ +# 项目相关配置 +ruoyi: + # 名称 + name: RuoYi + # 版本 + version: 4.6.2 + # 版权年份 + copyrightYear: 2021 + # 实例演示开关 + demoEnabled: true + # 文件路径 示例( Windows配置D:/ruoyi/uploadPath,Linux配置 /home/ruoyi/uploadPath) + profile: C:/bps-it/uploadPath + # 获取ip地址开关 + addressEnabled: false +# 开发环境配置 +server: + # 服务器的HTTP端口,默认为80 + port: 80 + servlet: + # 应用的访问路径 + context-path: /it_war + tomcat: + # tomcat的URI编码 + uri-encoding: UTF-8 + # tomcat最大线程数,默认为200 + max-threads: 800 + # Tomcat启动初始化的线程数,默认值25 + min-spare-threads: 30 + +# 日志配置 +logging: + level: + com.ruoyi: debug + org.springframework: warn + +# 用户配置 +user: + password: + # 密码错误{maxRetryCount}次锁定10分钟 + maxRetryCount: 5 + +# Spring配置 +spring: + # 模板引擎 + thymeleaf: + mode: HTML + encoding: utf-8 + # 禁用缓存 + cache: false + # 资源信息 + messages: + # 国际化资源文件路径 + basename: static/i18n/messages + jackson: + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + profiles: + active: druid + # 文件上传 + servlet: + multipart: + # 单个文件大小 + max-file-size: 10MB + # 设置总上传的文件大小 + max-request-size: 20MB + # 服务模块 + devtools: + restart: + # 热部署开关 + enabled: true + #ldap + ldap: + urls: ldap://192.168.2.10:389 + base: OU=bp,DC=bpsemi,DC=com + username: administrator@bpsemi.com + password: Bps@2831! + + redis: + host: 192.168.2.88 + port: 6379 + password: "bpsemi2021" + timeout: 30000 + database: 2 + lettuce: + pool: + max-active: 100 + max-idle: 10 + min-idle: 0 + max-wait: 30000 + cluster: + refresh: + adaptive: true + #20秒自动刷新一次 + period: 20 + +# MyBatis + +mybatis: + # 搜索指定包别名 + typeAliasesPackage: com.ruoyi.**.domain + # 配置mapper的扫描,找到所有的mapper.xml映射文件 + mapperLocations: classpath*:mapper/**/*Mapper.xml + # 加载全局的配置文件 + configLocation: classpath:mybatis/mybatis-config.xml + +# PageHelper分页插件 +pagehelper: + helperDialect: mysql + supportMethodsArguments: true + params: count=countSql + +# Shiro +shiro: + user: + # 登录地址 + loginUrl: /login + # 权限认证失败地址 + unauthorizedUrl: /unauth + # 首页地址 + indexUrl: /index + # 验证码开关 + captchaEnabled: false + # 验证码类型 math 数组计算 char 字符 + captchaType: math + cookie: + # 设置Cookie的域名 默认空,即当前访问的域名 + domain: + # 设置cookie的有效访问路径 + path: / + # 设置HttpOnly属性 + httpOnly: true + # 设置Cookie的过期时间,天为单位 + maxAge: 30 + # 设置密钥,务必保持唯一性(生成方式,直接拷贝到main运行即可)Base64.encodeToString(CipherUtils.generateNewKey(128, "AES").getEncoded()) (默认启动生成随机秘钥,随机秘钥会导致之前客户端RememberMe Cookie无效,如设置固定秘钥RememberMe Cookie则有效) + cipherKey: + session: + # Session超时时间,-1代表永不过期(默认30分钟) + expireTime: 30 + # 同步session到数据库的周期(默认1分钟) + dbSyncPeriod: 1 + # 相隔多久检查一次session的有效性,默认就是10分钟 + validationInterval: 10 + # 同一个用户最大会话数,比如2的意思是同一个账号允许最多同时两个人登录(默认-1不限制) + maxSession: -1 + # 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户 + kickoutAfter: false + rememberMe: + # 是否开启记住我 + enabled: true + +# 防止XSS攻击 +xss: + # 过滤开关 + enabled: true + # 排除链接(多个用逗号分隔) + excludes: /system/notice/* + # 匹配链接 + urlPatterns: /system/*,/monitor/*,/tool/* + +# Swagger配置 +swagger: + # 是否开启swagger + enabled: true + +#企业微信 +wechat: + corpId: ww4ed3771457e5f463 + agentId: 1000082 + secret: PqTYlveYQc54T13QS-cDyuAesDaGgyMSgpZLXBNJ-Uc + token: 111 + aesKey: 111 + +#快递100 +express: + #快递100的基础账号信息,可以在这里获取 + # https://poll.kuaidi100.com/manager/page/myinfo/enterprise + key: kzuyKyAE3985 + customer: 6774D6F41D773B17027EEBE5CC902C9E + secret: 4fc7633a027c4fe1a68b68237c236d6e + userid: bfc0389a986f45c4b36e27d9b18b7bd3 + #电子面单快递公司账号信息(非必填) + partnerId: + partnerKey: + net: + siid: + #短信模板id(非必填) + tid: + #云平台相关(非必填) + #登录云平台 https://cloud.kuaidi100.com/buyer/user/info + secret_key: + secret_secret: + +#TOPGP Webservice +topgp: + webservice: + topprod: http://192.168.2.81:85/web/ws/r/aws_ttsrv2 + toptest: http://192.168.2.81:85/web/ws/r/aws_ttsrv2_toptest diff --git a/ruoyi-admin/src/main/resources/banner.txt b/ruoyi-admin/src/main/resources/banner.txt new file mode 100644 index 000000000..0931cb844 --- /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/ehcache/ehcache-shiro.xml b/ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml new file mode 100644 index 000000000..7bf080fc1 --- /dev/null +++ b/ruoyi-admin/src/main/resources/ehcache/ehcache-shiro.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/logback.xml b/ruoyi-admin/src/main/resources/logback.xml new file mode 100644 index 000000000..195fa0759 --- /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/mybatis/mybatis-config.xml b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml new file mode 100644 index 000000000..2b1811312 --- /dev/null +++ b/ruoyi-admin/src/main/resources/mybatis/mybatis-config.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/beautifyhtml/beautifyhtml.js b/ruoyi-admin/src/main/resources/static/ajax/libs/beautifyhtml/beautifyhtml.js new file mode 100644 index 000000000..ea69dece1 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/beautifyhtml/beautifyhtml.js @@ -0,0 +1,617 @@ +/*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */ +/* + + The MIT License (MIT) + + Copyright (c) 2007-2013 Einar Lielmanis and contributors. + + 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. + + + Style HTML +--------------- + + Written by Nochum Sossonko, (nsossonko@hotmail.com) + + Based on code initially developed by: Einar Lielmanis, + http://jsbeautifier.org/ + + Usage: + style_html(html_source); + + style_html(html_source, options); + + The options are: + indent_size (default 4) — indentation size, + indent_char (default space) — character to indent with, + max_char (default 250) - maximum amount of characters per line (0 = disable) + brace_style (default "collapse") - "collapse" | "expand" | "end-expand" + put braces on the same line as control statements (default), or put braces on own line (Allman / ANSI style), or just put end braces on own line. + unformatted (defaults to inline tags) - list of tags, that shouldn't be reformatted + indent_scripts (default normal) - "keep"|"separate"|"normal" + + e.g. + + style_html(html_source, { + 'indent_size': 2, + 'indent_char': ' ', + 'max_char': 78, + 'brace_style': 'expand', + 'unformatted': ['a', 'sub', 'sup', 'b', 'i', 'u'] + }); +*/ + +(function() { + + function style_html(html_source, options, js_beautify, css_beautify) { + //Wrapper function to invoke all the necessary constructors and deal with the output. + + var multi_parser, + indent_size, + indent_character, + max_char, + brace_style, + unformatted; + + options = options || {}; + indent_size = options.indent_size || 4; + indent_character = options.indent_char || ' '; + brace_style = options.brace_style || 'collapse'; + max_char = options.max_char === 0 ? Infinity : options.max_char || 250; + unformatted = options.unformatted || ['a', 'span', 'bdo', 'em', 'strong', 'dfn', 'code', 'samp', 'kbd', 'var', 'cite', 'abbr', 'acronym', 'q', 'sub', 'sup', 'tt', 'i', 'b', 'big', 'small', 'u', 's', 'strike', 'font', 'ins', 'del', 'pre', 'address', 'dt', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6']; + + function Parser() { + + this.pos = 0; //Parser position + this.token = ''; + this.current_mode = 'CONTENT'; //reflects the current Parser mode: TAG/CONTENT + this.tags = { //An object to hold tags, their position, and their parent-tags, initiated with default values + parent: 'parent1', + parentcount: 1, + parent1: '' + }; + this.tag_type = ''; + this.token_text = this.last_token = this.last_text = this.token_type = ''; + + this.Utils = { //Uilities made available to the various functions + whitespace: "\n\r\t ".split(''), + single_token: 'br,input,link,meta,!doctype,basefont,base,area,hr,wbr,param,img,isindex,?xml,embed,?php,?,?='.split(','), //all the single tags for HTML + extra_liners: 'head,body,/html'.split(','), //for tags that need a line of whitespace before them + in_array: function (what, arr) { + for (var i=0; i= this.input.length) { + return content.length?content.join(''):['', 'TK_EOF']; + } + + input_char = this.input.charAt(this.pos); + this.pos++; + this.line_char_count++; + + if (this.Utils.in_array(input_char, this.Utils.whitespace)) { + if (content.length) { + space = true; + } + this.line_char_count--; + continue; //don't want to insert unnecessary space + } + else if (space) { + if (this.line_char_count >= this.max_char) { //insert a line when the max_char is reached + content.push('\n'); + for (var i=0; i', 'igm'); + reg_match.lastIndex = this.pos; + var reg_array = reg_match.exec(this.input); + var end_script = reg_array?reg_array.index:this.input.length; //absolute end of script + if(this.pos < end_script) { //get everything in between the script tags + content = this.input.substring(this.pos, end_script); + this.pos = end_script; + } + return content; + }; + + this.record_tag = function (tag){ //function to record a tag and its parent in this.tags Object + if (this.tags[tag + 'count']) { //check for the existence of this tag type + this.tags[tag + 'count']++; + this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level + } + else { //otherwise initialize this tag type + this.tags[tag + 'count'] = 1; + this.tags[tag + this.tags[tag + 'count']] = this.indent_level; //and record the present indent level + } + this.tags[tag + this.tags[tag + 'count'] + 'parent'] = this.tags.parent; //set the parent (i.e. in the case of a div this.tags.div1parent) + this.tags.parent = tag + this.tags[tag + 'count']; //and make this the current parent (i.e. in the case of a div 'div1') + }; + + this.retrieve_tag = function (tag) { //function to retrieve the opening tag to the corresponding closer + if (this.tags[tag + 'count']) { //if the openener is not in the Object we ignore it + var temp_parent = this.tags.parent; //check to see if it's a closable tag. + while (temp_parent) { //till we reach '' (the initial value); + if (tag + this.tags[tag + 'count'] === temp_parent) { //if this is it use it + break; + } + temp_parent = this.tags[temp_parent + 'parent']; //otherwise keep on climbing up the DOM Tree + } + if (temp_parent) { //if we caught something + this.indent_level = this.tags[tag + this.tags[tag + 'count']]; //set the indent_level accordingly + this.tags.parent = this.tags[temp_parent + 'parent']; //and set the current parent + } + delete this.tags[tag + this.tags[tag + 'count'] + 'parent']; //delete the closed tags parent reference... + delete this.tags[tag + this.tags[tag + 'count']]; //...and the tag itself + if (this.tags[tag + 'count'] === 1) { + delete this.tags[tag + 'count']; + } + else { + this.tags[tag + 'count']--; + } + } + }; + + this.get_tag = function (peek) { //function to get a full tag and parse its type + var input_char = '', + content = [], + comment = '', + space = false, + tag_start, tag_end, + orig_pos = this.pos, + orig_line_char_count = this.line_char_count; + + peek = peek !== undefined ? peek : false; + + do { + if (this.pos >= this.input.length) { + if (peek) { + this.pos = orig_pos; + this.line_char_count = orig_line_char_count; + } + return content.length?content.join(''):['', 'TK_EOF']; + } + + input_char = this.input.charAt(this.pos); + this.pos++; + this.line_char_count++; + + if (this.Utils.in_array(input_char, this.Utils.whitespace)) { //don't want to insert unnecessary space + space = true; + this.line_char_count--; + continue; + } + + if (input_char === "'" || input_char === '"') { + if (!content[1] || content[1] !== '!') { //if we're in a comment strings don't get treated specially + input_char += this.get_unformatted(input_char); + space = true; + } + } + + if (input_char === '=') { //no space before = + space = false; + } + + if (content.length && content[content.length-1] !== '=' && input_char !== '>' && space) { + //no space after = or before > + if (this.line_char_count >= this.max_char) { + this.print_newline(false, content); + this.line_char_count = 0; + } + else { + content.push(' '); + this.line_char_count++; + } + space = false; + } + if (input_char === '<') { + tag_start = this.pos - 1; + } + content.push(input_char); //inserts character at-a-time (or string) + } while (input_char !== '>'); + + var tag_complete = content.join(''); + var tag_index; + if (tag_complete.indexOf(' ') !== -1) { //if there's whitespace, thats where the tag name ends + tag_index = tag_complete.indexOf(' '); + } + else { //otherwise go with the tag ending + tag_index = tag_complete.indexOf('>'); + } + var tag_check = tag_complete.substring(1, tag_index).toLowerCase(); + if (tag_complete.charAt(tag_complete.length-2) === '/' || + this.Utils.in_array(tag_check, this.Utils.single_token)) { //if this tag name is a single tag type (either in the list or has a closing /) + if ( ! peek) { + this.tag_type = 'SINGLE'; + } + } + else if (tag_check === 'script') { //for later script handling + if ( ! peek) { + this.record_tag(tag_check); + this.tag_type = 'SCRIPT'; + } + } + else if (tag_check === 'style') { //for future style handling (for now it justs uses get_content) + if ( ! peek) { + this.record_tag(tag_check); + this.tag_type = 'STYLE'; + } + } + else if (this.is_unformatted(tag_check, unformatted)) { // do not reformat the "unformatted" tags + comment = this.get_unformatted('', tag_complete); //...delegate to get_unformatted function + content.push(comment); + // Preserve collapsed whitespace either before or after this tag. + if (tag_start > 0 && this.Utils.in_array(this.input.charAt(tag_start - 1), this.Utils.whitespace)){ + content.splice(0, 0, this.input.charAt(tag_start - 1)); + } + tag_end = this.pos - 1; + if (this.Utils.in_array(this.input.charAt(tag_end + 1), this.Utils.whitespace)){ + content.push(this.input.charAt(tag_end + 1)); + } + this.tag_type = 'SINGLE'; + } + else if (tag_check.charAt(0) === '!') { //peek for so... + comment = this.get_unformatted('-->', tag_complete); //...delegate to get_unformatted + content.push(comment); + } + if ( ! peek) { + this.tag_type = 'START'; + } + } + else if (tag_check.indexOf('[endif') !== -1) {//peek for ', tag_complete); + content.push(comment); + this.tag_type = 'SINGLE'; + } + } + else if ( ! peek) { + if (tag_check.charAt(0) === '/') { //this tag is a double tag so check for tag-ending + this.retrieve_tag(tag_check.substring(1)); //remove it and all ancestors + this.tag_type = 'END'; + } + else { //otherwise it's a start-tag + this.record_tag(tag_check); //push it on the tag stack + this.tag_type = 'START'; + } + if (this.Utils.in_array(tag_check, this.Utils.extra_liners)) { //check if this double needs an extra line + this.print_newline(true, this.output); + } + } + + if (peek) { + this.pos = orig_pos; + this.line_char_count = orig_line_char_count; + } + + return content.join(''); //returns fully formatted tag + }; + + this.get_unformatted = function (delimiter, orig_tag) { //function to return unformatted content in its entirety + + if (orig_tag && orig_tag.toLowerCase().indexOf(delimiter) !== -1) { + return ''; + } + var input_char = ''; + var content = ''; + var space = true; + do { + + if (this.pos >= this.input.length) { + return content; + } + + input_char = this.input.charAt(this.pos); + this.pos++; + + if (this.Utils.in_array(input_char, this.Utils.whitespace)) { + if (!space) { + this.line_char_count--; + continue; + } + if (input_char === '\n' || input_char === '\r') { + content += '\n'; + /* Don't change tab indention for unformatted blocks. If using code for html editing, this will greatly affect
     tags if they are specified in the 'unformatted array'
    +                for (var i=0; i]*>\s*$/);
    +
    +            // if next_tag comes back but is not an isolated tag, then
    +            // let's treat the 'a' tag as having content
    +            // and respect the unformatted option
    +            if (!tag || this.Utils.in_array(tag, unformatted)){
    +                return true;
    +            } else {
    +                return false;
    +            }
    +        };
    +
    +        this.printer = function (js_source, indent_character, indent_size, max_char, brace_style) { //handles input/output and some other printing functions
    +
    +          this.input = js_source || ''; //gets the input for the Parser
    +          this.output = [];
    +          this.indent_character = indent_character;
    +          this.indent_string = '';
    +          this.indent_size = indent_size;
    +          this.brace_style = brace_style;
    +          this.indent_level = 0;
    +          this.max_char = max_char;
    +          this.line_char_count = 0; //count to see if max_char was exceeded
    +
    +          for (var i=0; i 0) {
    +              this.indent_level--;
    +            }
    +          };
    +        };
    +        return this;
    +      }
    +
    +      /*_____________________--------------------_____________________*/
    +
    +      multi_parser = new Parser(); //wrapping functions Parser
    +      multi_parser.printer(html_source, indent_character, indent_size, max_char, brace_style); //initialize starting values
    +
    +      while (true) {
    +          var t = multi_parser.get_token();
    +          multi_parser.token_text = t[0];
    +          multi_parser.token_type = t[1];
    +
    +        if (multi_parser.token_type === 'TK_EOF') {
    +          break;
    +        }
    +
    +        switch (multi_parser.token_type) {
    +          case 'TK_TAG_START':
    +            multi_parser.print_newline(false, multi_parser.output);
    +            multi_parser.print_token(multi_parser.token_text);
    +            multi_parser.indent();
    +            multi_parser.current_mode = 'CONTENT';
    +            break;
    +          case 'TK_TAG_STYLE':
    +          case 'TK_TAG_SCRIPT':
    +            multi_parser.print_newline(false, multi_parser.output);
    +            multi_parser.print_token(multi_parser.token_text);
    +            multi_parser.current_mode = 'CONTENT';
    +            break;
    +          case 'TK_TAG_END':
    +            //Print new line only if the tag has no content and has child
    +            if (multi_parser.last_token === 'TK_CONTENT' && multi_parser.last_text === '') {
    +                var tag_name = multi_parser.token_text.match(/\w+/)[0];
    +                var tag_extracted_from_last_output = multi_parser.output[multi_parser.output.length -1].match(/<\s*(\w+)/);
    +                if (tag_extracted_from_last_output === null || tag_extracted_from_last_output[1] !== tag_name) {
    +                    multi_parser.print_newline(true, multi_parser.output);
    +                }
    +            }
    +            multi_parser.print_token(multi_parser.token_text);
    +            multi_parser.current_mode = 'CONTENT';
    +            break;
    +          case 'TK_TAG_SINGLE':
    +            // Don't add a newline before elements that should remain unformatted.
    +            var tag_check = multi_parser.token_text.match(/^\s*<([a-z]+)/i);
    +            if (!tag_check || !multi_parser.Utils.in_array(tag_check[1], unformatted)){
    +                multi_parser.print_newline(false, multi_parser.output);
    +            }
    +            multi_parser.print_token(multi_parser.token_text);
    +            multi_parser.current_mode = 'CONTENT';
    +            break;
    +          case 'TK_CONTENT':
    +            if (multi_parser.token_text !== '') {
    +              multi_parser.print_token(multi_parser.token_text);
    +            }
    +            multi_parser.current_mode = 'TAG';
    +            break;
    +          case 'TK_STYLE':
    +          case 'TK_SCRIPT':
    +            if (multi_parser.token_text !== '') {
    +              multi_parser.output.push('\n');
    +              var text = multi_parser.token_text,
    +                  _beautifier,
    +                  script_indent_level = 1;
    +              if (multi_parser.token_type === 'TK_SCRIPT') {
    +                _beautifier = typeof js_beautify === 'function' && js_beautify;
    +              } else if (multi_parser.token_type === 'TK_STYLE') {
    +                _beautifier = typeof css_beautify === 'function' && css_beautify;
    +              }
    +
    +              if (options.indent_scripts === "keep") {
    +                script_indent_level = 0;
    +              } else if (options.indent_scripts === "separate") {
    +                script_indent_level = -multi_parser.indent_level;
    +              }
    +
    +              var indentation = multi_parser.get_full_indent(script_indent_level);
    +              if (_beautifier) {
    +                // call the Beautifier if avaliable
    +                text = _beautifier(text.replace(/^\s*/, indentation), options);
    +              } else {
    +                // simply indent the string otherwise
    +                var white = text.match(/^\s*/)[0];
    +                var _level = white.match(/[^\n\r]*$/)[0].split(multi_parser.indent_string).length - 1;
    +                var reindent = multi_parser.get_full_indent(script_indent_level -_level);
    +                text = text.replace(/^\s*/, indentation)
    +                       .replace(/\r\n|\r|\n/g, '\n' + reindent)
    +                       .replace(/\s*$/, '');
    +              }
    +              if (text) {
    +                multi_parser.print_token(text);
    +                multi_parser.print_newline(true, multi_parser.output);
    +              }
    +            }
    +            multi_parser.current_mode = 'TAG';
    +            break;
    +        }
    +        multi_parser.last_token = multi_parser.token_type;
    +        multi_parser.last_text = multi_parser.token_text;
    +      }
    +      return multi_parser.output.join('');
    +    }
    +
    +    // If we're running a web page and don't have either of the above, add our one global
    +    window.html_beautify = function(html_source, options) {
    +        return style_html(html_source, options, window.js_beautify, window.css_beautify);
    +    };
    +
    +}());
    diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/blockUI/jquery.blockUI.js b/ruoyi-admin/src/main/resources/static/ajax/libs/blockUI/jquery.blockUI.js
    new file mode 100644
    index 000000000..552613d96
    --- /dev/null
    +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/blockUI/jquery.blockUI.js
    @@ -0,0 +1,620 @@
    +/*!
    + * jQuery blockUI plugin
    + * Version 2.70.0-2014.11.23
    + * Requires jQuery v1.7 or later
    + *
    + * Examples at: http://malsup.com/jquery/block/
    + * Copyright (c) 2007-2013 M. Alsup
    + * Dual licensed under the MIT and GPL licenses:
    + * http://www.opensource.org/licenses/mit-license.php
    + * http://www.gnu.org/licenses/gpl.html
    + *
    + * Thanks to Amir-Hossein Sobhi for some excellent contributions!
    + */
    +
    +;(function() {
    +/*jshint eqeqeq:false curly:false latedef:false */
    +"use strict";
    +
    +	function setup($) {
    +		$.fn._fadeIn = $.fn.fadeIn;
    +
    +		var noOp = $.noop || function() {};
    +
    +		// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
    +		// confusing userAgent strings on Vista)
    +		var msie = /MSIE/.test(navigator.userAgent);
    +		var ie6  = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
    +		var mode = document.documentMode || 0;
    +		var setExpr = $.isFunction( document.createElement('div').style.setExpression );
    +
    +		// global $ methods for blocking/unblocking the entire page
    +		$.blockUI   = function(opts) { install(window, opts); };
    +		$.unblockUI = function(opts) { remove(window, opts); };
    +
    +		// convenience method for quick growl-like notifications  (http://www.google.com/search?q=growl)
    +		$.growlUI = function(title, message, timeout, onClose) {
    +			var $m = $('
    '); + if (title) $m.append('

    '+title+'

    '); + if (message) $m.append('

    '+message+'

    '); + if (timeout === undefined) timeout = 3000; + + // Added by konapun: Set timeout to 30 seconds if this growl is moused over, like normal toast notifications + var callBlock = function(opts) { + opts = opts || {}; + + $.blockUI({ + message: $m, + fadeIn : typeof opts.fadeIn !== 'undefined' ? opts.fadeIn : 700, + fadeOut: typeof opts.fadeOut !== 'undefined' ? opts.fadeOut : 1000, + timeout: typeof opts.timeout !== 'undefined' ? opts.timeout : timeout, + centerY: false, + showOverlay: false, + onUnblock: onClose, + css: $.blockUI.defaults.growlCSS + }); + }; + + callBlock(); + var nonmousedOpacity = $m.css('opacity'); + $m.mouseover(function() { + callBlock({ + fadeIn: 0, + timeout: 30000 + }); + + var displayBlock = $('.blockMsg'); + displayBlock.stop(); // cancel fadeout if it has started + displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency + }).mouseout(function() { + $('.blockMsg').fadeOut(1000); + }); + // End konapun additions + }; + + // plugin method for blocking element content + $.fn.block = function(opts) { + if ( this[0] === window ) { + $.blockUI( opts ); + return this; + } + var fullOpts = $.extend({}, $.blockUI.defaults, opts || {}); + this.each(function() { + var $el = $(this); + if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked')) + return; + $el.unblock({ fadeOut: 0 }); + }); + + return this.each(function() { + if ($.css(this,'position') == 'static') { + this.style.position = 'relative'; + $(this).data('blockUI.static', true); + } + this.style.zoom = 1; // force 'hasLayout' in ie + install(this, opts); + }); + }; + + // plugin method for unblocking element content + $.fn.unblock = function(opts) { + if ( this[0] === window ) { + $.unblockUI( opts ); + return this; + } + return this.each(function() { + remove(this, opts); + }); + }; + + $.blockUI.version = 2.70; // 2nd generation blocking at no extra cost! + + // override these in your code to change the default behavior and style + $.blockUI.defaults = { + // message displayed when blocking (use null for no message) + message: '
    加载中......
    ', + + title: null, // title string; only used when theme == true + draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded) + + theme: false, // set to true to use with jQuery UI themes + + // styles for the message when blocking; if you wish to disable + // these and use an external stylesheet then do this in your code: + // $.blockUI.defaults.css = {}; + css: { + padding: 0, + margin: 0, + width: '30%', + top: '40%', + left: '35%', + textAlign: 'center', + color: '#000', + border: '0px', + backgroundColor:'transparent', + cursor: 'wait' + }, + + // minimal style set used when themes are used + themedCSS: { + width: '30%', + top: '40%', + left: '35%' + }, + + // styles for the overlay + overlayCSS: { + backgroundColor: '#000', + opacity: 0.6, + cursor: 'wait' + }, + + // style to replace wait cursor before unblocking to correct issue + // of lingering wait cursor + cursorReset: 'default', + + // styles applied when using $.growlUI + growlCSS: { + width: '350px', + top: '10px', + left: '', + right: '10px', + border: 'none', + padding: '5px', + opacity: 0.6, + cursor: 'default', + color: '#fff', + backgroundColor: '#000', + '-webkit-border-radius':'10px', + '-moz-border-radius': '10px', + 'border-radius': '10px' + }, + + // IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w + // (hat tip to Jorge H. N. de Vasconcelos) + /*jshint scripturl:true */ + iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank', + + // force usage of iframe in non-IE browsers (handy for blocking applets) + forceIframe: false, + + // z-index for the blocking overlay + baseZ: 1000, + + // set these to true to have the message automatically centered + centerX: true, // <-- only effects element blocking (page block controlled via css above) + centerY: true, + + // allow body element to be stetched in ie6; this makes blocking look better + // on "short" pages. disable if you wish to prevent changes to the body height + allowBodyStretch: true, + + // enable if you want key and mouse events to be disabled for content that is blocked + bindEvents: true, + + // be default blockUI will supress tab navigation from leaving blocking content + // (if bindEvents is true) + constrainTabKey: true, + + // fadeIn time in millis; set to 0 to disable fadeIn on block + fadeIn: 200, + + // fadeOut time in millis; set to 0 to disable fadeOut on unblock + fadeOut: 400, + + // time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock + timeout: 0, + + // disable if you don't want to show the overlay + showOverlay: true, + + // if true, focus will be placed in the first available input field when + // page blocking + focusInput: true, + + // elements that can receive focus + focusableElements: ':input:enabled:visible', + + // suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity) + // no longer needed in 2012 + // applyPlatformOpacityRules: true, + + // callback method invoked when fadeIn has completed and blocking message is visible + onBlock: null, + + // callback method invoked when unblocking has completed; the callback is + // passed the element that has been unblocked (which is the window object for page + // blocks) and the options that were passed to the unblock call: + // onUnblock(element, options) + onUnblock: null, + + // callback method invoked when the overlay area is clicked. + // setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used. + onOverlayClick: null, + + // don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493 + quirksmodeOffsetHack: 4, + + // class name of the message block + blockMsgClass: 'blockMsg', + + // if it is already blocked, then ignore it (don't unblock and reblock) + ignoreIfBlocked: false + }; + + // private data and functions follow... + + var pageBlock = null; + var pageBlockEls = []; + + function install(el, opts) { + var css, themedCSS; + var full = (el == window); + var msg = (opts && opts.message !== undefined ? opts.message : undefined); + opts = $.extend({}, $.blockUI.defaults, opts || {}); + + if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked')) + return; + + opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {}); + css = $.extend({}, $.blockUI.defaults.css, opts.css || {}); + if (opts.onOverlayClick) + opts.overlayCSS.cursor = 'pointer'; + + themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {}); + msg = msg === undefined ? opts.message : msg; + + // remove the current block (if there is one) + if (full && pageBlock) + remove(window, {fadeOut:0}); + + // if an existing element is being used as the blocking content then we capture + // its current place in the DOM (and current display style) so we can restore + // it when we unblock + if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) { + var node = msg.jquery ? msg[0] : msg; + var data = {}; + $(el).data('blockUI.history', data); + data.el = node; + data.parent = node.parentNode; + data.display = node.style.display; + data.position = node.style.position; + if (data.parent) + data.parent.removeChild(node); + } + + $(el).data('blockUI.onUnblock', opts.onUnblock); + var z = opts.baseZ; + + // blockUI uses 3 layers for blocking, for simplicity they are all used on every platform; + // layer1 is the iframe layer which is used to supress bleed through of underlying content + // layer2 is the overlay layer which has opacity and a wait cursor (by default) + // layer3 is the message content that is displayed while blocking + var lyr1, lyr2, lyr3, s; + if (msie || opts.forceIframe) + lyr1 = $(''); + else + lyr1 = $(''); + + if (opts.theme) + lyr2 = $(''); + else + lyr2 = $(''); + + if (opts.theme && full) { + s = ''; + } + else if (opts.theme) { + s = ''; + } + else if (full) { + s = ''; + } + else { + s = ''; + } + lyr3 = $(s); + + // if we have a message, style it + if (msg) { + if (opts.theme) { + lyr3.css(themedCSS); + lyr3.addClass('ui-widget-content'); + } + else + lyr3.css(css); + } + + // style the overlay + if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/) + lyr2.css(opts.overlayCSS); + lyr2.css('position', full ? 'fixed' : 'absolute'); + + // make iframe layer transparent in IE + if (msie || opts.forceIframe) + lyr1.css('opacity',0.0); + + //$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el); + var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el); + $.each(layers, function() { + this.appendTo($par); + }); + + if (opts.theme && opts.draggable && $.fn.draggable) { + lyr3.draggable({ + handle: '.ui-dialog-titlebar', + cancel: 'li' + }); + } + + // ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling) + var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0); + if (ie6 || expr) { + // give body 100% height + if (full && opts.allowBodyStretch && $.support.boxModel) + $('html,body').css('height','100%'); + + // fix ie6 issue when blocked element has a border width + if ((ie6 || !$.support.boxModel) && !full) { + var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth'); + var fixT = t ? '(0 - '+t+')' : 0; + var fixL = l ? '(0 - '+l+')' : 0; + } + + // simulate fixed position + $.each(layers, function(i,o) { + var s = o[0].style; + s.position = 'absolute'; + if (i < 2) { + if (full) + s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"'); + else + s.setExpression('height','this.parentNode.offsetHeight + "px"'); + if (full) + s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'); + else + s.setExpression('width','this.parentNode.offsetWidth + "px"'); + if (fixL) s.setExpression('left', fixL); + if (fixT) s.setExpression('top', fixT); + } + else if (opts.centerY) { + if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'); + s.marginTop = 0; + } + else if (!opts.centerY && full) { + var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0; + var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"'; + s.setExpression('top',expression); + } + }); + } + + // show the message + if (msg) { + if (opts.theme) + lyr3.find('.ui-widget-content').append(msg); + else + lyr3.append(msg); + if (msg.jquery || msg.nodeType) + $(msg).show(); + } + + if ((msie || opts.forceIframe) && opts.showOverlay) + lyr1.show(); // opacity is zero + if (opts.fadeIn) { + var cb = opts.onBlock ? opts.onBlock : noOp; + var cb1 = (opts.showOverlay && !msg) ? cb : noOp; + var cb2 = msg ? cb : noOp; + if (opts.showOverlay) + lyr2._fadeIn(opts.fadeIn, cb1); + if (msg) + lyr3._fadeIn(opts.fadeIn, cb2); + } + else { + if (opts.showOverlay) + lyr2.show(); + if (msg) + lyr3.show(); + if (opts.onBlock) + opts.onBlock.bind(lyr3)(); + } + + // bind key and mouse events + bind(1, el, opts); + + if (full) { + pageBlock = lyr3[0]; + pageBlockEls = $(opts.focusableElements,pageBlock); + if (opts.focusInput) + setTimeout(focus, 20); + } + else + center(lyr3[0], opts.centerX, opts.centerY); + + if (opts.timeout) { + // auto-unblock + var to = setTimeout(function() { + if (full) + $.unblockUI(opts); + else + $(el).unblock(opts); + }, opts.timeout); + $(el).data('blockUI.timeout', to); + } + } + + // remove the block + function remove(el, opts) { + var count; + var full = (el == window); + var $el = $(el); + var data = $el.data('blockUI.history'); + var to = $el.data('blockUI.timeout'); + if (to) { + clearTimeout(to); + $el.removeData('blockUI.timeout'); + } + opts = $.extend({}, $.blockUI.defaults, opts || {}); + bind(0, el, opts); // unbind events + + if (opts.onUnblock === null) { + opts.onUnblock = $el.data('blockUI.onUnblock'); + $el.removeData('blockUI.onUnblock'); + } + + var els; + if (full) // crazy selector to handle odd field errors in ie6/7 + els = $('body').children().filter('.blockUI').add('body > .blockUI'); + else + els = $el.find('>.blockUI'); + + // fix cursor issue + if ( opts.cursorReset ) { + if ( els.length > 1 ) + els[1].style.cursor = opts.cursorReset; + if ( els.length > 2 ) + els[2].style.cursor = opts.cursorReset; + } + + if (full) + pageBlock = pageBlockEls = null; + + if (opts.fadeOut) { + count = els.length; + els.stop().fadeOut(opts.fadeOut, function() { + if ( --count === 0) + reset(els,data,opts,el); + }); + } + else + reset(els, data, opts, el); + } + + // move blocking element back into the DOM where it started + function reset(els,data,opts,el) { + var $el = $(el); + if ( $el.data('blockUI.isBlocked') ) + return; + + els.each(function(i,o) { + // remove via DOM calls so we don't lose event handlers + if (this.parentNode) + this.parentNode.removeChild(this); + }); + + if (data && data.el) { + data.el.style.display = data.display; + data.el.style.position = data.position; + data.el.style.cursor = 'default'; // #59 + if (data.parent) + data.parent.appendChild(data.el); + $el.removeData('blockUI.history'); + } + + if ($el.data('blockUI.static')) { + $el.css('position', 'static'); // #22 + } + + if (typeof opts.onUnblock == 'function') + opts.onUnblock(el,opts); + + // fix issue in Safari 6 where block artifacts remain until reflow + var body = $(document.body), w = body.width(), cssW = body[0].style.width; + body.width(w-1).width(w); + body[0].style.width = cssW; + } + + // bind/unbind the handler + function bind(b, el, opts) { + var full = el == window, $el = $(el); + + // don't bother unbinding if there is nothing to unbind + if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked'))) + return; + + $el.data('blockUI.isBlocked', b); + + // don't bind events when overlay is not in use or if bindEvents is false + if (!full || !opts.bindEvents || (b && !opts.showOverlay)) + return; + + // bind anchors and inputs for mouse and key events + var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove'; + if (b) + $(document).bind(events, opts, handler); + else + $(document).unbind(events, handler); + + // former impl... + // var $e = $('a,:input'); + // b ? $e.bind(events, opts, handler) : $e.unbind(events, handler); + } + + // event handler to suppress keyboard/mouse events when blocking + function handler(e) { + // allow tab navigation (conditionally) + if (e.type === 'keydown' && e.keyCode && e.keyCode == 9) { + if (pageBlock && e.data.constrainTabKey) { + var els = pageBlockEls; + var fwd = !e.shiftKey && e.target === els[els.length-1]; + var back = e.shiftKey && e.target === els[0]; + if (fwd || back) { + setTimeout(function(){focus(back);},10); + return false; + } + } + } + var opts = e.data; + var target = $(e.target); + if (target.hasClass('blockOverlay') && opts.onOverlayClick) + opts.onOverlayClick(e); + + // allow events within the message content + if (target.parents('div.' + opts.blockMsgClass).length > 0) + return true; + + // allow events for content that is not being blocked + return target.parents().children().filter('div.blockUI').length === 0; + } + + function focus(back) { + if (!pageBlockEls) + return; + var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0]; + if (e) + e.focus(); + } + + function center(el, x, y) { + var p = el.parentNode, s = el.style; + var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth'); + var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth'); + if (x) s.left = l > 0 ? (l+'px') : '0'; + if (y) s.top = t > 0 ? (t+'px') : '0'; + } + + function sz(el, p) { + return parseInt($.css(el,p),10)||0; + } + + } + + + /*global define:true */ + if (typeof define === 'function' && define.amd && define.amd.jQuery) { + define(['jquery'], setup); + } else { + setup(jQuery); + } + +})(); \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.css new file mode 100644 index 000000000..3cfefd722 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.css @@ -0,0 +1,671 @@ +/*! + * bootstrap-fileinput v5.2.3 + * http://plugins.krajee.com/file-input + * + * Krajee default styling for bootstrap-fileinput. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2021, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ + +.file-loading input[type=file], +input[type=file].file-loading { + width: 0; + height: 0; +} + +.file-no-browse { + position: absolute; + left: 50%; + bottom: 20%; + width: 1px; + height: 1px; + font-size: 0; + opacity: 0; + border: none; + background: none; + outline: none; + box-shadow: none; +} + +.kv-hidden, +.file-caption-icon, +.file-zoom-dialog .modal-header:before, +.file-zoom-dialog .modal-header:after, +.file-input-new .file-preview, +.file-input-new .close, +.file-input-new .glyphicon-file, +.file-input-new .fileinput-remove-button, +.file-input-new .fileinput-upload-button, +.file-input-new .no-browse .input-group-btn, +.file-input-ajax-new .fileinput-remove-button, +.file-input-ajax-new .fileinput-upload-button, +.file-input-ajax-new .no-browse .input-group-btn, +.hide-content .kv-file-content, +.is-locked .fileinput-upload-button, +.is-locked .fileinput-remove-button { + display: none; +} + +.btn-file input[type=file], +.file-caption-icon, +.file-preview .fileinput-remove, +.krajee-default .file-thumb-progress, +.file-zoom-dialog .btn-navigate, +.file-zoom-dialog .floating-buttons { + position: absolute; +} + +.file-caption-icon .kv-caption-icon { + line-height: inherit; +} + +.file-input, +.file-loading:before, +.btn-file, +.file-caption, +.file-preview, +.krajee-default.file-preview-frame, +.krajee-default .file-thumbnail-footer, +.file-zoom-dialog .modal-dialog { + position: relative; +} + +.file-error-message pre, +.file-error-message ul, +.krajee-default .file-actions, +.krajee-default .file-other-error { + text-align: left; +} + +.file-error-message pre, +.file-error-message ul { + margin: 0; +} + +.krajee-default .file-drag-handle, +.krajee-default .file-upload-indicator { + float: left; + margin-top: 10px; + width: 16px; + height: 16px; +} + +.file-thumb-progress .progress, +.file-thumb-progress .progress-bar { + font-family: Verdana, Helvetica, sans-serif; + font-size: 0.7rem; +} + +.krajee-default .file-thumb-progress .progress, +.kv-upload-progress .progress { + background-color: #ccc; +} + +.krajee-default .file-caption-info, +.krajee-default .file-size-info { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 160px; + height: 15px; + margin: auto; +} + +.file-zoom-content > .file-object.type-video, +.file-zoom-content > .file-object.type-flash, +.file-zoom-content > .file-object.type-image { + max-width: 100%; + max-height: 100%; + width: auto; +} + +.file-zoom-content > .file-object.type-video, +.file-zoom-content > .file-object.type-flash { + height: 100%; +} + +.file-zoom-content > .file-object.type-pdf, +.file-zoom-content > .file-object.type-html, +.file-zoom-content > .file-object.type-text, +.file-zoom-content > .file-object.type-default { + width: 100%; +} + +.file-loading:before { + content: " Loading..."; + display: inline-block; + padding-left: 20px; + line-height: 16px; + font-size: 13px; + font-variant: small-caps; + color: #999; + background: transparent url(loading.gif) top left no-repeat; +} + +.file-object { + margin: 0 0 -5px 0; + padding: 0; +} + +.btn-file { + overflow: hidden; +} + +.btn-file input[type=file] { + top: 0; + left: 0; + min-width: 100%; + min-height: 100%; + text-align: right; + opacity: 0; + background: none repeat scroll 0 0 transparent; + cursor: inherit; + display: block; +} + +.btn-file ::-ms-browse { + font-size: 10000px; + width: 100%; + height: 100%; +} + +.file-caption.icon-visible .file-caption-icon { + display: inline-block; +} + +.file-caption.icon-visible .file-caption-name { + padding-left: 1.875rem; +} + +.file-caption.icon-visible > .input-group-lg .file-caption-name { + padding-left: 2.1rem; +} + +.file-caption.icon-visible > .input-group-sm .file-caption-name { + padding-left: 1.5rem; +} + +.file-caption-name:not(.file-caption-disabled) { + background-color: transparent; +} + +.file-caption-name.file-processing { + font-style: italic; + border-color: #bbb; + opacity: 0.5; +} + +.file-caption-icon { + padding: 0.5rem; + left: 4px; +} + +.input-group-lg .file-caption-icon { + font-size: 1.25rem; +} + +.input-group-sm .file-caption-icon { + font-size: 0.875rem; + padding: 0.25rem; +} + +.file-error-message { + color: #a94442; + background-color: #f2dede; + margin: 5px; + border: 1px solid #ebccd1; + border-radius: 4px; + padding: 15px; +} + +.file-error-message pre { + margin: 5px 0; +} + +.file-caption-disabled { + background-color: #eee; + cursor: not-allowed; + opacity: 1; +} + +.file-preview { + border-radius: 5px; + border: 1px solid #ddd; + padding: 8px; + width: 100%; + margin-bottom: 5px; +} + +.file-preview .btn-xs { + padding: 1px 5px; + font-size: 12px; + line-height: 1.5; + border-radius: 3px; +} + +.file-preview .fileinput-remove { + top: 1px; + right: 1px; + line-height: 10px; +} + +.file-preview .clickable { + cursor: pointer; +} + +.file-preview-image { + font: 40px Impact, Charcoal, sans-serif; + color: #008000; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; +} + +.krajee-default.file-preview-frame { + margin: 8px; + border: 1px solid rgba(0, 0, 0, 0.2); + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2); + padding: 6px; + float: left; + text-align: center; +} + +.krajee-default.file-preview-frame .kv-file-content { + width: 213px; + height: 160px; +} + +.krajee-default .file-preview-other-frame { + display: flex; + align-items: center; + justify-content: center; +} + +.krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered { + width: 400px; +} + +.krajee-default.file-preview-frame[data-template="audio"] .kv-file-content { + width: 240px; + height: 55px; +} + +.krajee-default.file-preview-frame .file-thumbnail-footer { + height: 70px; +} + +.krajee-default.file-preview-frame:not(.file-preview-error):hover { + border: 1px solid rgba(0, 0, 0, 0.3); + box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.4); +} + +.krajee-default .file-preview-text { + color: #428bca; + border: 1px solid #ddd; + outline: none; + resize: none; +} + +.krajee-default .file-preview-html { + border: 1px solid #ddd; +} + +.krajee-default .file-other-icon { + font-size: 6em; + line-height: 1; +} + +.krajee-default .file-footer-buttons { + float: right; +} + +.krajee-default .file-footer-caption { + display: block; + text-align: center; + padding-top: 4px; + font-size: 11px; + color: #777; + margin-bottom: 30px; +} + +.file-upload-stats { + font-size: 10px; + text-align: center; + width: 100%; +} + +.kv-upload-progress .file-upload-stats { + font-size: 12px; + margin: -10px 0 5px; +} + +.krajee-default .file-preview-error { + opacity: 0.65; + box-shadow: none; +} + +.krajee-default .file-thumb-progress { + top: 37px; + left: 0; + right: 0; +} + +.krajee-default.kvsortable-ghost { + background: #e1edf7; + border: 2px solid #a1abff; +} + +.krajee-default .file-preview-other:hover { + opacity: 0.8; +} + +.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover { + color: #000; +} + +.kv-upload-progress .progress { + height: 20px; + margin: 10px 0; + overflow: hidden; +} + +.kv-upload-progress .progress-bar { + height: 20px; + font-family: Verdana, Helvetica, sans-serif; +} + + +/*noinspection CssOverwrittenProperties*/ + +.file-zoom-dialog .file-other-icon { + font-size: 22em; + font-size: 50vmin; +} + +.file-zoom-dialog .modal-dialog { + width: auto; +} + +.file-zoom-dialog .modal-header { + display: flex; + align-items: center; + justify-content: space-between; +} + +.file-zoom-dialog .btn-navigate { + margin: 0 0.1rem; + padding: 0; + font-size: 1.2rem; + width: 2.4rem; + height: 2.4rem; + top: 50%; + border-radius: 50%; + text-align:center; +} + +.btn-navigate * { + width: auto; +} + +.file-zoom-dialog .floating-buttons { + top: 5px; + right: 10px; +} + +.file-zoom-dialog .btn-kv-prev { + left: 0; +} + +.file-zoom-dialog .btn-kv-next { + right: 0; +} + +.file-zoom-dialog .kv-zoom-caption { + max-width: 50%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + +.file-zoom-dialog .kv-zoom-header { + padding: 0.5rem; +} + +.file-zoom-dialog .kv-zoom-body { + padding: 0.25rem 0.5rem 0.25rem 0; +} + +.file-zoom-dialog .kv-zoom-description { + position: absolute; + opacity: 0.8; + font-size: 0.8rem; + background-color: #1a1a1a; + padding: 1rem; + text-align: center; + border-radius: 0.5rem; + color: #fff; + left: 15%; + right: 15%; + bottom: 15%; +} + +.file-zoom-dialog .kv-desc-hide { + float: right; + color: #fff; + padding: 0 0.1rem; + background: none; + border: none; +} + +.file-zoom-dialog .kv-desc-hide:hover { + opacity: 0.7; +} + +.file-zoom-dialog .kv-desc-hide:focus { + opacity: 0.9; +} + +.file-input-new .no-browse .form-control { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.file-input-ajax-new .no-browse .form-control { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.file-caption { + width: 100%; + position: relative; +} + +.file-thumb-loading { + background: transparent url(loading.gif) no-repeat scroll center center content-box !important; +} + +.file-drop-zone { + border: 1px dashed #aaa; + min-height: 260px; + border-radius: 4px; + text-align: center; + vertical-align: middle; + margin: 12px 15px 12px 12px; + padding: 5px; +} + +.file-drop-zone.clickable:hover { + border: 2px dashed #999; +} + +.file-drop-zone.clickable:focus { + border: 2px solid #5acde2; +} + +.file-drop-zone .file-preview-thumbnails { + cursor: default; +} + +.file-drop-zone-title { + color: #aaa; + font-size: 1.6em; + text-align: center; + padding: 85px 10px; + cursor: default; +} + +.file-highlighted { + border: 2px dashed #999 !important; + background-color: #eee; +} + +.file-uploading { + background: url(loading-sm.gif) no-repeat center bottom 10px; + opacity: 0.65; +} + +.file-zoom-fullscreen .modal-dialog { + min-width: 100%; + margin: 0; +} + +.file-zoom-fullscreen .modal-content { + border-radius: 0; + box-shadow: none; + min-height: 100vh; +} + +.file-zoom-fullscreen .kv-zoom-body { + overflow-y: auto; +} + +.floating-buttons { + z-index: 3000; +} + +.floating-buttons .btn-kv { + margin-left: 3px; + z-index: 3000; +} + +.kv-zoom-actions .btn-kv { + margin-left: 3px; +} + +.file-zoom-content { + text-align: center; + white-space: nowrap; + min-height: 300px; +} + +.file-zoom-content:hover { + background: transparent; +} + +.file-zoom-content > * { + display: inline-block; + vertical-align: middle; +} + +.file-zoom-content .kv-spacer { + height: 100%; +} + +.file-zoom-content .file-preview-image { + max-height: 100%; +} + +.file-zoom-content .file-preview-video { + max-height: 100%; +} + +.file-zoom-content > .file-object.type-image { + height: auto; + min-height: inherit; +} + +.file-zoom-content > .file-object.type-audio { + width: auto; + height: 30px; +} + +@media (min-width: 576px) { + .file-zoom-dialog .modal-dialog { + max-width: 500px; + } +} + +@media (min-width: 992px) { + .file-zoom-dialog .modal-lg { + max-width: 800px; + } +} + +@media (max-width: 767px) { + .file-preview-thumbnails { + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + } + + .file-zoom-dialog .modal-header { + flex-direction: column; + } +} + +@media (max-width: 350px) { + .krajee-default.file-preview-frame:not([data-template="audio"]) .kv-file-content { + width: 160px; + } +} + +@media (max-width: 420px) { + .krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered { + width: 100%; + } +} + +.file-loading[dir=rtl]:before { + background: transparent url(loading.gif) top right no-repeat; + padding-left: 0; + padding-right: 20px; +} + +.clickable .file-drop-zone-title { + cursor: pointer; +} + +.file-sortable .file-drag-handle:hover { + opacity: 0.7; +} + +.file-sortable .file-drag-handle { + cursor: grab; + opacity: 1; +} + +.file-grabbing, +.file-grabbing * { + cursor: not-allowed !important; +} + +.file-grabbing .file-preview-thumbnails * { + cursor: grabbing !important; +} + +.file-preview-frame.sortable-chosen { + background-color: #d9edf7; + border-color: #17a2b8; + box-shadow: none !important; +} + +.file-preview .kv-zoom-cache { + display: none; +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.js new file mode 100644 index 000000000..35f0d48f2 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.js @@ -0,0 +1,6392 @@ +/*! + * bootstrap-fileinput v5.2.3 + * http://plugins.krajee.com/file-input + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2021, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + define(['jquery'], factory); + } else { + if (typeof module === 'object' && module.exports) { + //noinspection NpmUsedModulesInstalled + module.exports = factory(require('jquery')); + } else { + factory(window.jQuery); + } + } +}(function ($) { + 'use strict'; + $.fn.fileinputLocales = {}; + $.fn.fileinputThemes = {}; + if (!$.fn.fileinputBsVersion) { + $.fn.fileinputBsVersion = (window.Alert && window.Alert.VERSION) || + (window.bootstrap && window.bootstrap.Alert && bootstrap.Alert.VERSION) || '3.x.x'; + } + String.prototype.setTokens = function (replacePairs) { + var str = this.toString(), key, re; + for (key in replacePairs) { + if (replacePairs.hasOwnProperty(key)) { + re = new RegExp('\{' + key + '\}', 'g'); + str = str.replace(re, replacePairs[key]); + } + } + return str; + }; + + if (!Array.prototype.flatMap) { // polyfill flatMap + Array.prototype.flatMap = function (lambda) { + return [].concat(this.map(lambda)); + }; + } + + var $h, FileInput; + + // fileinput helper object for all global variables and internal helper methods + $h = { + FRAMES: '.kv-preview-thumb', + SORT_CSS: 'file-sortable', + INIT_FLAG: 'init-', + OBJECT_PARAMS: '\n' + + '\n' + + '\n' + + '\n' + + '\n' + + '\n', + DEFAULT_PREVIEW: '
    \n' + + '{previewFileIcon}\n' + + '
    ', + MODAL_ID: 'kvFileinputModal', + MODAL_EVENTS: ['show', 'shown', 'hide', 'hidden', 'loaded'], + logMessages: { + ajaxError: '{status}: {error}. Error Details: {text}.', + badDroppedFiles: 'Error scanning dropped files!', + badExifParser: 'Error loading the piexif.js library. {details}', + badInputType: 'The input "type" must be set to "file" for initializing the "bootstrap-fileinput" plugin.', + exifWarning: 'To avoid this warning, either set "autoOrientImage" to "false" OR ensure you have loaded ' + + 'the "piexif.js" library correctly on your page before the "fileinput.js" script.', + invalidChunkSize: 'Invalid upload chunk size: "{chunkSize}". Resumable uploads are disabled.', + invalidThumb: 'Invalid thumb frame with id: "{id}".', + noResumableSupport: 'The browser does not support resumable or chunk uploads.', + noUploadUrl: 'The "uploadUrl" is not set. Ajax uploads and resumable uploads have been disabled.', + retryStatus: 'Retrying upload for chunk # {chunk} for {filename}... retry # {retry}.', + chunkQueueError: 'Could not push task to ajax pool for chunk index # {index}.', + resumableMaxRetriesReached: 'Maximum resumable ajax retries ({n}) reached.', + resumableRetryError: 'Could not retry the resumable request (try # {n})... aborting.', + resumableAborting: 'Aborting / cancelling the resumable request.', + resumableRequestError: 'Error processing resumable request. {msg}' + + }, + objUrl: window.URL || window.webkitURL, + isBs: function (ver) { + var chk = $.trim(($.fn.fileinputBsVersion || '') + ''); + ver = parseInt(ver, 10); + if (!chk) { + return ver === 4; + } + return ver === parseInt(chk.charAt(0), 10); + + }, + defaultButtonCss: function (fill) { + return 'btn-default btn-' + (fill ? '' : 'outline-') + 'secondary'; + }, + now: function () { + return new Date().getTime(); + }, + round: function (num) { + num = parseFloat(num); + return isNaN(num) ? 0 : Math.floor(Math.round(num)); + }, + getArray: function (obj) { + var i, arr = [], len = obj && obj.length || 0; + for (i = 0; i < len; i++) { + arr.push(obj[i]); + } + return arr; + }, + getFileRelativePath: function (file) { + /** @namespace file.relativePath */ + /** @namespace file.webkitRelativePath */ + return String(file.newPath || file.relativePath || file.webkitRelativePath || $h.getFileName(file) || null); + + }, + getFileId: function (file, generateFileId) { + var relativePath = $h.getFileRelativePath(file); + if (typeof generateFileId === 'function') { + return generateFileId(file); + } + if (!file) { + return null; + } + if (!relativePath) { + return null; + } + return (file.size + '_' + encodeURIComponent(relativePath).replace(/%/g, '_')); + }, + getFrameSelector: function (id, selector) { + selector = selector || ''; + return '[id="' + id + '"]' + selector; + }, + getZoomSelector: function (id, selector) { + return $h.getFrameSelector('zoom-' + id, selector); + }, + getFrameElement: function ($element, id, selector) { + return $element.find($h.getFrameSelector(id, selector)); + }, + getZoomElement: function ($element, id, selector) { + return $element.find($h.getZoomSelector(id, selector)); + }, + getElapsed: function (seconds) { + var delta = seconds, out = '', result = {}, structure = { + year: 31536000, + month: 2592000, + week: 604800, // uncomment row to ignore + day: 86400, // feel free to add your own row + hour: 3600, + minute: 60, + second: 1 + }; + $h.getObjectKeys(structure).forEach(function (key) { + result[key] = Math.floor(delta / structure[key]); + delta -= result[key] * structure[key]; + }); + $.each(result, function (key, value) { + if (value > 0) { + out += (out ? ' ' : '') + value + key.substring(0, 1); + } + }); + return out; + }, + debounce: function (func, delay) { + var inDebounce; + return function () { + var args = arguments, context = this; + clearTimeout(inDebounce); + inDebounce = setTimeout(function () { + func.apply(context, args); + }, delay); + }; + }, + stopEvent: function (e) { + e.stopPropagation(); + e.preventDefault(); + }, + getFileName: function (file) { + /** @namespace file.fileName */ + return file ? (file.fileName || file.name || '') : ''; // some confusion in different versions of Firefox + }, + createObjectURL: function (data) { + if ($h.objUrl && $h.objUrl.createObjectURL && data) { + return $h.objUrl.createObjectURL(data); + } + return ''; + }, + revokeObjectURL: function (data) { + if ($h.objUrl && $h.objUrl.revokeObjectURL && data) { + $h.objUrl.revokeObjectURL(data); + } + }, + compare: function (input, str, exact) { + return input !== undefined && (exact ? input === str : input.match(str)); + }, + isIE: function (ver) { + var div, status; + // check for IE versions < 11 + if (navigator.appName !== 'Microsoft Internet Explorer') { + return false; + } + if (ver === 10) { + return new RegExp('msie\\s' + ver, 'i').test(navigator.userAgent); + } + div = document.createElement('div'); + div.innerHTML = ''; + status = div.getElementsByTagName('i').length; + document.body.appendChild(div); + div.parentNode.removeChild(div); + return status; + }, + canOrientImage: function ($el) { + var $img = $(document.createElement('img')).css({width: '1px', height: '1px'}).insertAfter($el), + flag = $img.css('image-orientation'); + $img.remove(); + return !!flag; + }, + canAssignFilesToInput: function () { + var input = document.createElement('input'); + try { + input.type = 'file'; + input.files = null; + return true; + } catch (err) { + return false; + } + }, + getDragDropFolders: function (items) { + var i, item, len = items ? items.length : 0, folders = 0; + if (len > 0 && items[0].webkitGetAsEntry()) { + for (i = 0; i < len; i++) { + item = items[i].webkitGetAsEntry(); + if (item && item.isDirectory) { + folders++; + } + } + } + return folders; + }, + initModal: function ($modal) { + var $body = $('body'); + if ($body.length) { + $modal.appendTo($body); + } + }, + isFunction: function (v) { + return typeof v === 'function'; + }, + isEmpty: function (value, trim) { + if (value === undefined || value === null || value === '') { + return true; + } + if ($h.isString(value) && trim) { + return $.trim(value) === ''; + } + if ($h.isArray(value)) { + return value.length === 0; + } + if ($.isPlainObject(value) && $.isEmptyObject(value)) { + return true + } + return false; + }, + isArray: function (a) { + return Array.isArray(a) || Object.prototype.toString.call(a) === '[object Array]'; + }, + isString: function (a) { + return Object.prototype.toString.call(a) === '[object String]'; + }, + ifSet: function (needle, haystack, def) { + def = def || ''; + return (haystack && typeof haystack === 'object' && needle in haystack) ? haystack[needle] : def; + }, + cleanArray: function (arr) { + if (!(arr instanceof Array)) { + arr = []; + } + return arr.filter(function (e) { + return (e !== undefined && e !== null); + }); + }, + spliceArray: function (arr, index, reverseOrder) { + var i, j = 0, out = [], newArr; + if (!(arr instanceof Array)) { + return []; + } + newArr = $.extend(true, [], arr); + if (reverseOrder) { + newArr.reverse(); + } + for (i = 0; i < newArr.length; i++) { + if (i !== index) { + out[j] = newArr[i]; + j++; + } + } + if (reverseOrder) { + out.reverse(); + } + return out; + }, + getNum: function (num, def) { + def = def || 0; + if (typeof num === 'number') { + return num; + } + if (typeof num === 'string') { + num = parseFloat(num); + } + return isNaN(num) ? def : num; + }, + hasFileAPISupport: function () { + return !!(window.File && window.FileReader); + }, + hasDragDropSupport: function () { + var div = document.createElement('div'); + /** @namespace div.draggable */ + /** @namespace div.ondragstart */ + /** @namespace div.ondrop */ + return !$h.isIE(9) && + (div.draggable !== undefined || (div.ondragstart !== undefined && div.ondrop !== undefined)); + }, + hasFileUploadSupport: function () { + return $h.hasFileAPISupport() && window.FormData; + }, + hasBlobSupport: function () { + try { + return !!window.Blob && Boolean(new Blob()); + } catch (e) { + return false; + } + }, + hasArrayBufferViewSupport: function () { + try { + return new Blob([new Uint8Array(100)]).size === 100; + } catch (e) { + return false; + } + }, + hasResumableUploadSupport: function () { + /** @namespace Blob.prototype.webkitSlice */ + /** @namespace Blob.prototype.mozSlice */ + return $h.hasFileUploadSupport() && $h.hasBlobSupport() && $h.hasArrayBufferViewSupport() && + (!!Blob.prototype.webkitSlice || !!Blob.prototype.mozSlice || !!Blob.prototype.slice || false); + }, + dataURI2Blob: function (dataURI) { + var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || + window.MSBlobBuilder, canBlob = $h.hasBlobSupport(), byteStr, arrayBuffer, intArray, i, mimeStr, bb, + canProceed = (canBlob || BlobBuilder) && window.atob && window.ArrayBuffer && window.Uint8Array; + if (!canProceed) { + return null; + } + if (dataURI.split(',')[0].indexOf('base64') >= 0) { + byteStr = atob(dataURI.split(',')[1]); + } else { + byteStr = decodeURIComponent(dataURI.split(',')[1]); + } + arrayBuffer = new ArrayBuffer(byteStr.length); + intArray = new Uint8Array(arrayBuffer); + for (i = 0; i < byteStr.length; i += 1) { + intArray[i] = byteStr.charCodeAt(i); + } + mimeStr = dataURI.split(',')[0].split(':')[1].split(';')[0]; + if (canBlob) { + return new Blob([$h.hasArrayBufferViewSupport() ? intArray : arrayBuffer], {type: mimeStr}); + } + bb = new BlobBuilder(); + bb.append(arrayBuffer); + return bb.getBlob(mimeStr); + }, + arrayBuffer2String: function (buffer) { + if (window.TextDecoder) { + return new TextDecoder('utf-8').decode(buffer); + } + var array = Array.prototype.slice.apply(new Uint8Array(buffer)), out = '', i = 0, len, c, char2, char3; + len = array.length; + while (i < len) { + c = array[i++]; + switch (c >> 4) { // jshint ignore:line + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); // jshint ignore:line + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 0x0F) << 12) | // jshint ignore:line + ((char2 & 0x3F) << 6) | // jshint ignore:line + ((char3 & 0x3F) << 0)); // jshint ignore:line + break; + } + } + return out; + }, + isHtml: function (str) { + var a = document.createElement('div'); + a.innerHTML = str; + for (var c = a.childNodes, i = c.length; i--;) { + if (c[i].nodeType === 1) { + return true; + } + } + return false; + }, + isSvg: function (str) { + return str.match(/^\s*<\?xml/i) && (str.match(/' + str + '')); + }, + uniqId: function () { + return (new Date().getTime() + Math.floor(Math.random() * Math.pow(10, 15))).toString(36); + }, + cspBuffer: { + CSP_ATTRIB: 'data-csp-01928735', // a randomly named temporary attribute to store the CSP elem id + domElementsStyles: {}, + stash: function (htmlString) { + var self = this, outerDom = $.parseHTML('
    ' + htmlString + '
    '), $el = $(outerDom); + $el.find('[style]').each(function (key, elem) { + var $elem = $(elem), styleDeclaration = $elem[0].style, id = $h.uniqId(), styles = {}; + if (styleDeclaration && styleDeclaration.length) { + $(styleDeclaration).each(function () { + styles[this] = styleDeclaration[this]; + }); + self.domElementsStyles[id] = styles; + $elem.removeAttr('style').attr(self.CSP_ATTRIB, id); + } + }); + $el.filter('*').removeAttr('style'); // make sure all style attr are removed + var values = Object.values ? Object.values(outerDom) : Object.keys(outerDom).map(function (itm) { + return outerDom[itm]; + }); + return values.flatMap(function (elem) { + return elem.innerHTML; + }).join(''); + }, + apply: function (domElement) { + var self = this, $el = $(domElement); + $el.find('[' + self.CSP_ATTRIB + ']').each(function (key, elem) { + var $elem = $(elem), id = $elem.attr(self.CSP_ATTRIB), styles = self.domElementsStyles[id]; + if (styles) { + $elem.css(styles); + } + $elem.removeAttr(self.CSP_ATTRIB); + }); + self.domElementsStyles = {}; + } + }, + setHtml: function ($elem, htmlString) { + var buf = $h.cspBuffer; + $elem.html(buf.stash(htmlString)); + buf.apply($elem); + return $elem; + }, + htmlEncode: function (str, undefVal) { + if (str === undefined) { + return undefVal || null; + } + return str.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + }, + replaceTags: function (str, tags) { + var out = str; + if (!tags) { + return out; + } + $.each(tags, function (key, value) { + if (typeof value === 'function') { + value = value(); + } + out = out.split(key).join(value); + }); + return out; + }, + cleanMemory: function ($thumb) { + var data = $thumb.is('img') ? $thumb.attr('src') : $thumb.find('source').attr('src'); + $h.revokeObjectURL(data); + }, + findFileName: function (filePath) { + var sepIndex = filePath.lastIndexOf('/'); + if (sepIndex === -1) { + sepIndex = filePath.lastIndexOf('\\'); + } + return filePath.split(filePath.substring(sepIndex, sepIndex + 1)).pop(); + }, + checkFullScreen: function () { + return document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || + document.msFullscreenElement; + }, + toggleFullScreen: function (maximize) { + var doc = document, de = doc.documentElement, isFullScreen = $h.checkFullScreen(); + if (de && maximize && !isFullScreen) { + if (de.requestFullscreen) { + de.requestFullscreen(); + } else { + if (de.msRequestFullscreen) { + de.msRequestFullscreen(); + } else { + if (de.mozRequestFullScreen) { + de.mozRequestFullScreen(); + } else { + if (de.webkitRequestFullscreen) { + de.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT); + } + } + } + } + } else { + if (isFullScreen) { + if (doc.exitFullscreen) { + doc.exitFullscreen(); + } else { + if (doc.msExitFullscreen) { + doc.msExitFullscreen(); + } else { + if (doc.mozCancelFullScreen) { + doc.mozCancelFullScreen(); + } else { + if (doc.webkitExitFullscreen) { + doc.webkitExitFullscreen(); + } + } + } + } + } + } + }, + moveArray: function (arr, oldIndex, newIndex, reverseOrder) { + var newArr = $.extend(true, [], arr); + if (reverseOrder) { + newArr.reverse(); + } + if (newIndex >= newArr.length) { + var k = newIndex - newArr.length; + while ((k--) + 1) { + newArr.push(undefined); + } + } + newArr.splice(newIndex, 0, newArr.splice(oldIndex, 1)[0]); + if (reverseOrder) { + newArr.reverse(); + } + return newArr; + }, + closeButton: function (css) { + css = ($h.isBs(5) ? 'btn-close' : 'close') + (css ? ' ' + css : ''); + return ''; + }, + getRotation: function (value) { + switch (value) { + case 2: + return 'rotateY(180deg)'; + case 3: + return 'rotate(180deg)'; + case 4: + return 'rotate(180deg) rotateY(180deg)'; + case 5: + return 'rotate(270deg) rotateY(180deg)'; + case 6: + return 'rotate(90deg)'; + case 7: + return 'rotate(90deg) rotateY(180deg)'; + case 8: + return 'rotate(270deg)'; + default: + return ''; + } + }, + setTransform: function (el, val) { + if (!el) { + return; + } + el.style.transform = val; + el.style.webkitTransform = val; + el.style['-moz-transform'] = val; + el.style['-ms-transform'] = val; + el.style['-o-transform'] = val; + }, + getObjectKeys: function (obj) { + var keys = []; + if (obj) { + $.each(obj, function (key) { + keys.push(key); + }); + } + return keys; + }, + getObjectSize: function (obj) { + return $h.getObjectKeys(obj).length; + }, + /** + * Small dependency injection for the task manager + * https://gist.github.com/fearphage/4341799 + */ + whenAll: function (array) { + var s = [].slice, resolveValues = arguments.length === 1 && $h.isArray(array) ? array : s.call(arguments), + deferred = $.Deferred(), i, failed = 0, value, length = resolveValues.length, + remaining = length, rejectContexts, rejectValues, resolveContexts, updateFunc; + rejectContexts = rejectValues = resolveContexts = Array(length); + updateFunc = function (index, contexts, values) { + return function () { + if (values !== resolveValues) { + failed++; + } + deferred.notifyWith(contexts[index] = this, values[index] = s.call(arguments)); + if (!(--remaining)) { + deferred[(!failed ? 'resolve' : 'reject') + 'With'](contexts, values); + } + }; + }; + for (i = 0; i < length; i++) { + if ((value = resolveValues[i]) && $.isFunction(value.promise)) { + value.promise() + .done(updateFunc(i, resolveContexts, resolveValues)) + .fail(updateFunc(i, rejectContexts, rejectValues)); + } else { + deferred.notifyWith(this, value); + --remaining; + } + } + if (!remaining) { + deferred.resolveWith(resolveContexts, resolveValues); + } + return deferred.promise(); + } + }; + FileInput = function (element, options) { + var self = this; + self.$element = $(element); + self.$parent = self.$element.parent(); + if (!self._validate()) { + return; + } + self.isPreviewable = $h.hasFileAPISupport(); + self.isIE9 = $h.isIE(9); + self.isIE10 = $h.isIE(10); + if (self.isPreviewable || self.isIE9) { + self._init(options); + self._listen(); + } + self.$element.removeClass('file-loading'); + }; + + FileInput.prototype = { + constructor: FileInput, + _cleanup: function () { + var self = this; + self.reader = null; + self.clearFileStack(); + self.fileBatchCompleted = true; + self.isError = false; + self.isDuplicateError = false; + self.isPersistentError = false; + self.cancelling = false; + self.paused = false; + self.lastProgress = 0; + self._initAjax(); + }, + _isAborted: function () { + var self = this; + return self.cancelling || self.paused; + }, + _initAjax: function () { + var self = this, tm = self.taskManager = { + pool: {}, + addPool: function (id) { + return (tm.pool[id] = new tm.TasksPool(id)); + }, + getPool: function (id) { + return tm.pool[id]; + }, + addTask: function (id, logic) { // add standalone task directly from task manager + return new tm.Task(id, logic); + }, + TasksPool: function (id) { + var tp = this; + tp.id = id; + tp.cancelled = false; + tp.cancelledDeferrer = $.Deferred(); + tp.tasks = {}; + tp.addTask = function (id, logic) { + return (tp.tasks[id] = new tm.Task(id, logic)); + }; + tp.size = function () { + return $h.getObjectSize(tp.tasks); + }; + tp.run = function (maxThreads) { + var i = 0, failed = false, task, tasksList = $h.getObjectKeys(tp.tasks).map(function (key) { + return tp.tasks[key]; + }), tasksDone = [], deferred = $.Deferred(), enqueue, callback; + + if (tp.cancelled) { + tp.cancelledDeferrer.resolve(); + return deferred.reject(); + } + // if run all at once + if (!maxThreads) { + var tasksDeferredList = $h.getObjectKeys(tp.tasks).map(function (key) { + return tp.tasks[key].deferred; + }); + // when all are done + $h.whenAll(tasksDeferredList).done(function () { + var argv = $h.getArray(arguments); + if (!tp.cancelled) { + deferred.resolve.apply(null, argv); + tp.cancelledDeferrer.reject(); + } else { + deferred.reject.apply(null, argv); + tp.cancelledDeferrer.resolve(); + } + }).fail(function () { + var argv = $h.getArray(arguments); + deferred.reject.apply(null, argv); + if (!tp.cancelled) { + tp.cancelledDeferrer.reject(); + } else { + tp.cancelledDeferrer.resolve(); + } + }); + // run all tasks + $.each(tp.tasks, function (id) { + task = tp.tasks[id]; + task.run(); + }); + return deferred; + } + enqueue = function (task) { + $.when(task.deferred) + .fail(function () { + failed = true; + callback.apply(null, arguments); + }) + .always(callback); + }; + callback = function () { + var argv = $h.getArray(arguments); + // notify a task just ended + deferred.notify(argv); + tasksDone.push(argv); + if (tp.cancelled) { + deferred.reject.apply(null, tasksDone); + tp.cancelledDeferrer.resolve(); + return; + } + if (tasksDone.length === tp.size()) { + if (failed) { + deferred.reject.apply(null, tasksDone); + } else { + deferred.resolve.apply(null, tasksDone); + } + } + // if there are any tasks remaining + if (tasksList.length) { + task = tasksList.shift(); + enqueue(task); + task.run(); + } + }; + // run the first "maxThreads" tasks + while (tasksList.length && i++ < maxThreads) { + task = tasksList.shift(); + enqueue(task); + task.run(); + } + return deferred; + }; + tp.cancel = function () { + tp.cancelled = true; + return tp.cancelledDeferrer; + }; + }, + Task: function (id, logic) { + var tk = this; + tk.id = id; + tk.deferred = $.Deferred(); + tk.logic = logic; + tk.context = null; + tk.run = function () { + var argv = $h.getArray(arguments); + argv.unshift(tk.deferred); // add deferrer as first argument + logic.apply(tk.context, argv); // run task + return tk.deferred; // return deferrer + }; + tk.runWithContext = function (context) { + tk.context = context; + return tk.run(); + }; + } + }; + self.ajaxQueue = []; + self.ajaxRequests = []; + self.ajaxAborted = false; + }, + _init: function (options, refreshMode) { + var self = this, f, $el = self.$element, $cont, t, tmp; + self.options = options; + self.canOrientImage = $h.canOrientImage($el); + $.each(options, function (key, value) { + switch (key) { + case 'minFileCount': + case 'maxFileCount': + case 'maxTotalFileCount': + case 'minFileSize': + case 'maxFileSize': + case 'maxFilePreviewSize': + case 'resizeQuality': + case 'resizeIfSizeMoreThan': + case 'progressUploadThreshold': + case 'initialPreviewCount': + case 'zoomModalHeight': + case 'minImageHeight': + case 'maxImageHeight': + case 'minImageWidth': + case 'maxImageWidth': + case 'bytesToKB': + self[key] = $h.getNum(value); + break; + default: + self[key] = value; + break; + } + }); + if (!self.bytesToKB || self.bytesToKB <= 0) { + self.bytesToKB = 1024; + } + if (self.errorCloseButton === undefined) { + self.errorCloseButton = $h.closeButton('kv-error-close' + ($h.isBs(5) ? ' float-end' : '')); + } + if (self.maxTotalFileCount > 0 && self.maxTotalFileCount < self.maxFileCount) { + self.maxTotalFileCount = self.maxFileCount; + } + if (self.rtl) { // swap buttons for rtl + tmp = self.previewZoomButtonIcons.prev; + self.previewZoomButtonIcons.prev = self.previewZoomButtonIcons.next; + self.previewZoomButtonIcons.next = tmp; + } + // validate chunk threads to not exceed maxAjaxThreads + if (!isNaN(self.maxAjaxThreads) && self.maxAjaxThreads < self.resumableUploadOptions.maxThreads) { + self.resumableUploadOptions.maxThreads = self.maxAjaxThreads; + } + self._initFileManager(); + if (typeof self.autoOrientImage === 'function') { + self.autoOrientImage = self.autoOrientImage(); + } + if (typeof self.autoOrientImageInitial === 'function') { + self.autoOrientImageInitial = self.autoOrientImageInitial(); + } + if (!refreshMode) { + self._cleanup(); + } + self.duplicateErrors = []; + self.$form = $el.closest('form'); + self._initTemplateDefaults(); + self.uploadFileAttr = !$h.isEmpty($el.attr('name')) ? $el.attr('name') : 'file_data'; + t = self._getLayoutTemplate('progress'); + self.progressTemplate = t.replace('{class}', self.progressClass); + self.progressInfoTemplate = t.replace('{class}', self.progressInfoClass); + self.progressPauseTemplate = t.replace('{class}', self.progressPauseClass); + self.progressCompleteTemplate = t.replace('{class}', self.progressCompleteClass); + self.progressErrorTemplate = t.replace('{class}', self.progressErrorClass); + self.isDisabled = $el.attr('disabled') || $el.attr('readonly'); + if (self.isDisabled) { + $el.attr('disabled', true); + } + self.isClickable = self.browseOnZoneClick && self.showPreview && + (self.dropZoneEnabled || !$h.isEmpty(self.defaultPreviewContent)); + self.isAjaxUpload = $h.hasFileUploadSupport() && !$h.isEmpty(self.uploadUrl); + self.dropZoneEnabled = $h.hasDragDropSupport() && self.dropZoneEnabled; + if (!self.isAjaxUpload) { + self.dropZoneEnabled = self.dropZoneEnabled && $h.canAssignFilesToInput(); + } + self.slug = typeof options.slugCallback === 'function' ? options.slugCallback : self._slugDefault; + self.mainTemplate = self.showCaption ? self._getLayoutTemplate('main1') : self._getLayoutTemplate('main2'); + self.captionTemplate = self._getLayoutTemplate('caption'); + self.previewGenericTemplate = self._getPreviewTemplate('generic'); + if (!self.imageCanvas && self.resizeImage && (self.maxImageWidth || self.maxImageHeight)) { + self.imageCanvas = document.createElement('canvas'); + self.imageCanvasContext = self.imageCanvas.getContext('2d'); + } + if ($h.isEmpty($el.attr('id'))) { + $el.attr('id', $h.uniqId()); + } + self.namespace = '.fileinput_' + $el.attr('id').replace(/-/g, '_'); + if (self.$container === undefined) { + self.$container = self._createContainer(); + } else { + self._refreshContainer(); + } + $cont = self.$container; + self.$dropZone = $cont.find('.file-drop-zone'); + self.$progress = $cont.find('.kv-upload-progress'); + self.$btnUpload = $cont.find('.fileinput-upload'); + self.$captionContainer = $h.getElement(options, 'elCaptionContainer', $cont.find('.file-caption')); + self.$caption = $h.getElement(options, 'elCaptionText', $cont.find('.file-caption-name')); + if (!$h.isEmpty(self.msgPlaceholder)) { + f = $el.attr('multiple') ? self.filePlural : self.fileSingle; + self.$caption.attr('placeholder', self.msgPlaceholder.replace('{files}', f)); + } + self.$captionIcon = self.$captionContainer.find('.file-caption-icon'); + self.$previewContainer = $h.getElement(options, 'elPreviewContainer', $cont.find('.file-preview')); + self.$preview = $h.getElement(options, 'elPreviewImage', $cont.find('.file-preview-thumbnails')); + self.$previewStatus = $h.getElement(options, 'elPreviewStatus', $cont.find('.file-preview-status')); + self.$errorContainer = $h.getElement(options, 'elErrorContainer', + self.$previewContainer.find('.kv-fileinput-error')); + self._validateDisabled(); + if (!$h.isEmpty(self.msgErrorClass)) { + $h.addCss(self.$errorContainer, self.msgErrorClass); + } + if (!refreshMode) { + self._resetErrors(); + self.$errorContainer.hide(); + self.previewInitId = 'thumb-' + $el.attr('id'); + self._initPreviewCache(); + self._initPreview(true); + self._initPreviewActions(); + if (self.$parent.hasClass('file-loading')) { + self.$container.insertBefore(self.$parent); + self.$parent.remove(); + } + } else { + if (!self._errorsExist()) { + self.$errorContainer.hide(); + } + } + self._setFileDropZoneTitle(); + if ($el.attr('disabled')) { + self.disable(); + } + self._initZoom(); + if (self.hideThumbnailContent) { + $h.addCss(self.$preview, 'hide-content'); + } + }, + _initFileManager: function () { + var self = this; + self.uploadStartTime = $h.now(); + self.fileManager = { + stack: {}, + filesProcessed: [], + errors: [], + loadedImages: {}, + totalImages: 0, + totalFiles: null, + totalSize: null, + uploadedSize: 0, + stats: {}, + bpsLog: [], + bps: 0, + initStats: function (id) { + var data = {started: $h.now()}; + if (id) { + self.fileManager.stats[id] = data; + } else { + self.fileManager.stats = data; + } + }, + getUploadStats: function (id, loaded, total) { + var fm = self.fileManager, + started = id ? fm.stats[id] && fm.stats[id].started || $h.now() : self.uploadStartTime, + elapsed = ($h.now() - started) / 1000, bps = Math.ceil(elapsed ? loaded / elapsed : 0), + pendingBytes = total - loaded, out, delay = fm.bpsLog.length ? self.bitrateUpdateDelay : 0; + setTimeout(function () { + var i, j = 0, n = 0, len, beg; + fm.bpsLog.push(bps); + fm.bpsLog.sort(function (a, b) { + return a - b; + }); + len = fm.bpsLog.length; + beg = len > 10 ? len - 10 : Math.ceil(len / 2); + for (i = len; i > beg; i--) { + n = parseFloat(fm.bpsLog[i]); + j++; + } + fm.bps = (j > 0 ? n / j : 0) * 64; + }, delay); + out = { + fileId: id, + started: started, + elapsed: elapsed, + loaded: loaded, + total: total, + bps: fm.bps, + bitrate: self._getSize(fm.bps, self.bitRateUnits), + pendingBytes: pendingBytes + }; + if (id) { + fm.stats[id] = out; + } else { + fm.stats = out; + } + return out; + }, + exists: function (id) { + return $.inArray(id, self.fileManager.getIdList()) !== -1; + }, + count: function () { + return self.fileManager.getIdList().length; + }, + total: function () { + var fm = self.fileManager; + if (!fm.totalFiles) { + fm.totalFiles = fm.count(); + } + return fm.totalFiles; + }, + getTotalSize: function () { + var fm = self.fileManager; + if (fm.totalSize) { + return fm.totalSize; + } + fm.totalSize = 0; + $.each(self.getFileStack(), function (id, f) { + var size = parseFloat(f.size); + fm.totalSize += isNaN(size) ? 0 : size; + }); + return fm.totalSize; + }, + add: function (file, id) { + if (!id) { + id = self.fileManager.getId(file); + } + if (!id) { + return; + } + self.fileManager.stack[id] = { + file: file, + name: $h.getFileName(file), + relativePath: $h.getFileRelativePath(file), + size: file.size, + nameFmt: self._getFileName(file, ''), + sizeFmt: self._getSize(file.size) + }; + }, + remove: function ($thumb) { + var id = self._getThumbFileId($thumb); + self.fileManager.removeFile(id); + }, + removeFile: function (id) { + var fm = self.fileManager; + if (!id) { + return; + } + delete fm.stack[id]; + delete fm.loadedImages[id]; + }, + move: function (idFrom, idTo) { + var result = {}, stack = self.fileManager.stack; + if (!idFrom && !idTo || idFrom === idTo) { + return; + } + $.each(stack, function (k, v) { + if (k !== idFrom) { + result[k] = v; + } + if (k === idTo) { + result[idFrom] = stack[idFrom]; + } + }); + self.fileManager.stack = result; + }, + list: function () { + var files = []; + $.each(self.getFileStack(), function (k, v) { + if (v && v.file) { + files.push(v.file); + } + }); + return files; + }, + isPending: function (id) { + return $.inArray(id, self.fileManager.filesProcessed) === -1 && self.fileManager.exists(id); + }, + isProcessed: function () { + var filesProcessed = true, fm = self.fileManager; + $.each(self.getFileStack(), function (id) { + if (fm.isPending(id)) { + filesProcessed = false; + } + }); + return filesProcessed; + }, + clear: function () { + var fm = self.fileManager; + self.isDuplicateError = false; + self.isPersistentError = false; + fm.totalFiles = null; + fm.totalSize = null; + fm.uploadedSize = 0; + fm.stack = {}; + fm.errors = []; + fm.filesProcessed = []; + fm.stats = {}; + fm.bpsLog = []; + fm.bps = 0; + fm.clearImages(); + }, + clearImages: function () { + self.fileManager.loadedImages = {}; + self.fileManager.totalImages = 0; + }, + addImage: function (id, config) { + self.fileManager.loadedImages[id] = config; + }, + removeImage: function (id) { + delete self.fileManager.loadedImages[id]; + }, + getImageIdList: function () { + return $h.getObjectKeys(self.fileManager.loadedImages); + }, + getImageCount: function () { + return self.fileManager.getImageIdList().length; + }, + getId: function (file) { + return self._getFileId(file); + }, + getIndex: function (id) { + return self.fileManager.getIdList().indexOf(id); + }, + getThumb: function (id) { + var $thumb = null; + self._getThumbs().each(function () { + var $t = $(this); + if (self._getThumbFileId($t) === id) { + $thumb = $t; + } + }); + return $thumb; + }, + getThumbIndex: function ($thumb) { + var id = self._getThumbFileId($thumb); + return self.fileManager.getIndex(id); + }, + getIdList: function () { + return $h.getObjectKeys(self.fileManager.stack); + }, + getFile: function (id) { + return self.fileManager.stack[id] || null; + }, + getFileName: function (id, fmt) { + var file = self.fileManager.getFile(id); + if (!file) { + return ''; + } + return fmt ? (file.nameFmt || '') : file.name || ''; + }, + getFirstFile: function () { + var ids = self.fileManager.getIdList(), id = ids && ids.length ? ids[0] : null; + return self.fileManager.getFile(id); + }, + setFile: function (id, file) { + if (self.fileManager.getFile(id)) { + self.fileManager.stack[id].file = file; + } else { + self.fileManager.add(file, id); + } + }, + setProcessed: function (id) { + self.fileManager.filesProcessed.push(id); + }, + getProgress: function () { + var total = self.fileManager.total(), filesProcessed = self.fileManager.filesProcessed.length; + if (!total) { + return 0; + } + return Math.ceil(filesProcessed / total * 100); + + }, + setProgress: function (id, pct) { + var f = self.fileManager.getFile(id); + if (!isNaN(pct) && f) { + f.progress = pct; + } + } + }; + }, + _setUploadData: function (fd, config) { + var self = this; + $.each(config, function (key, value) { + var param = self.uploadParamNames[key] || key; + if ($h.isArray(value)) { + fd.append(param, value[0], value[1]); + } else { + fd.append(param, value); + } + }); + }, + _initResumableUpload: function () { + var self = this, opts = self.resumableUploadOptions, logs = $h.logMessages, rm, fm = self.fileManager; + if (!self.enableResumableUpload) { + return; + } + if (opts.fallback !== false && typeof opts.fallback !== 'function') { + opts.fallback = function (s) { + s._log(logs.noResumableSupport); + s.enableResumableUpload = false; + }; + } + if (!$h.hasResumableUploadSupport() && opts.fallback !== false) { + opts.fallback(self); + return; + } + if (!self.uploadUrl && self.enableResumableUpload) { + self._log(logs.noUploadUrl); + self.enableResumableUpload = false; + return; + + } + opts.chunkSize = parseFloat(opts.chunkSize); + if (opts.chunkSize <= 0 || isNaN(opts.chunkSize)) { + self._log(logs.invalidChunkSize, {chunkSize: opts.chunkSize}); + self.enableResumableUpload = false; + return; + } + rm = self.resumableManager = { + init: function (id, f, index) { + rm.logs = []; + rm.stack = []; + rm.error = ''; + rm.id = id; + rm.file = f.file; + rm.fileName = f.name; + rm.fileIndex = index; + rm.completed = false; + rm.lastProgress = 0; + if (self.showPreview) { + rm.$thumb = fm.getThumb(id) || null; + rm.$progress = rm.$btnDelete = null; + if (rm.$thumb && rm.$thumb.length) { + rm.$progress = rm.$thumb.find('.file-thumb-progress'); + rm.$btnDelete = rm.$thumb.find('.kv-file-remove'); + } + } + rm.chunkSize = opts.chunkSize * self.bytesToKB; + rm.chunkCount = rm.getTotalChunks(); + }, + setAjaxError: function (jqXHR, textStatus, errorThrown, isTest) { + if (jqXHR.responseJSON && jqXHR.responseJSON.error) { + errorThrown = jqXHR.responseJSON.error.toString(); + } + if (!isTest) { + rm.error = errorThrown; + } + if (opts.showErrorLog) { + self._log(logs.ajaxError, { + status: jqXHR.status, + error: errorThrown, + text: jqXHR.responseText || '' + }); + } + }, + reset: function () { + rm.stack = []; + rm.chunksProcessed = {}; + }, + setProcessed: function (status) { + var id = rm.id, msg, $thumb = rm.$thumb, $prog = rm.$progress, hasThumb = $thumb && $thumb.length, + params = {id: hasThumb ? $thumb.attr('id') : '', index: fm.getIndex(id), fileId: id}, tokens, + skipErrorsAndProceed = self.resumableUploadOptions.skipErrorsAndProceed; + rm.completed = true; + rm.lastProgress = 0; + if (hasThumb) { + $thumb.removeClass('file-uploading'); + } + if (status === 'success') { + fm.uploadedSize += rm.file.size; + if (self.showPreview) { + self._setProgress(101, $prog); + self._setThumbStatus($thumb, 'Success'); + self._initUploadSuccess(rm.chunksProcessed[id].data, $thumb); + } + fm.removeFile(id); + delete rm.chunksProcessed[id]; + self._raise('fileuploaded', [params.id, params.index, params.fileId]); + if (fm.isProcessed()) { + self._setProgress(101); + } + } else { + if (status !== 'cancel') { + if (self.showPreview) { + self._setThumbStatus($thumb, 'Error'); + self._setPreviewError($thumb, true); + self._setProgress(101, $prog, self.msgProgressError); + self._setProgress(101, self.$progress, self.msgProgressError); + self.cancelling = !skipErrorsAndProceed; + } + if (!self.$errorContainer.find('li[data-file-id="' + params.fileId + '"]').length) { + tokens = {file: rm.fileName, max: opts.maxRetries, error: rm.error}; + msg = self.msgResumableUploadRetriesExceeded.setTokens(tokens); + $.extend(params, tokens); + self._showFileError(msg, params, 'filemaxretries'); + if (skipErrorsAndProceed) { + fm.removeFile(id); + delete rm.chunksProcessed[id]; + if (fm.isProcessed()) { + self._setProgress(101); + } + } + } + } + } + if (fm.isProcessed()) { + rm.reset(); + } + }, + check: function () { + var status = true; + $.each(rm.logs, function (index, value) { + if (!value) { + status = false; + return false; + } + }); + }, + processedResumables: function () { + var logs = rm.logs, i, count = 0; + if (!logs || !logs.length) { + return 0; + } + for (i = 0; i < logs.length; i++) { + if (logs[i] === true) { + count++; + } + } + return count; + }, + getUploadedSize: function () { + var size = rm.processedResumables() * rm.chunkSize; + return size > rm.file.size ? rm.file.size : size; + }, + getTotalChunks: function () { + var chunkSize = parseFloat(rm.chunkSize); + if (!isNaN(chunkSize) && chunkSize > 0) { + return Math.ceil(rm.file.size / chunkSize); + } + return 0; + }, + getProgress: function () { + var chunksProcessed = rm.processedResumables(), total = rm.chunkCount; + if (total === 0) { + return 0; + } + return Math.ceil(chunksProcessed / total * 100); + }, + checkAborted: function (intervalId) { + if (self._isAborted()) { + clearInterval(intervalId); + self.unlock(); + } + }, + upload: function () { + var ids = fm.getIdList(), flag = 'new', intervalId; + intervalId = setInterval(function () { + var id; + rm.checkAborted(intervalId); + if (flag === 'new') { + self.lock(); + flag = 'processing'; + id = ids.shift(); + fm.initStats(id); + if (fm.stack[id]) { + rm.init(id, fm.stack[id], fm.getIndex(id)); + rm.processUpload(); + } + } + if (!fm.isPending(id) && rm.completed) { + flag = 'new'; + } + if (fm.isProcessed()) { + var $initThumbs = self.$preview.find('.file-preview-initial'); + if ($initThumbs.length) { + $h.addCss($initThumbs, $h.SORT_CSS); + self._initSortable(); + } + clearInterval(intervalId); + self._clearFileInput(); + self.unlock(); + setTimeout(function () { + var data = self.previewCache.data; + if (data) { + self.initialPreview = data.content; + self.initialPreviewConfig = data.config; + self.initialPreviewThumbTags = data.tags; + } + self._raise('filebatchuploadcomplete', [ + self.initialPreview, + self.initialPreviewConfig, + self.initialPreviewThumbTags, + self._getExtraData() + ]); + }, self.processDelay); + } + }, self.processDelay); + }, + uploadResumable: function () { + var i, pool, tm = self.taskManager, total = rm.chunkCount; + pool = tm.addPool(rm.id); + for (i = 0; i < total; i++) { + rm.logs[i] = !!(rm.chunksProcessed[rm.id] && rm.chunksProcessed[rm.id][i]); + if (!rm.logs[i]) { + rm.pushAjax(i, 0); + } + } + pool.run(opts.maxThreads) + .done(function () { + rm.setProcessed('success'); + }) + .fail(function () { + rm.setProcessed(pool.cancelled ? 'cancel' : 'error'); + }); + }, + processUpload: function () { + var fd, f, id = rm.id, fnBefore, fnSuccess, fnError, fnComplete, outData; + if (!opts.testUrl) { + rm.uploadResumable(); + return; + } + fd = new FormData(); + f = fm.stack[id]; + self._setUploadData(fd, { + fileId: id, + fileName: f.fileName, + fileSize: f.size, + fileRelativePath: f.relativePath, + chunkSize: rm.chunkSize, + chunkCount: rm.chunkCount + }); + fnBefore = function (jqXHR) { + outData = self._getOutData(fd, jqXHR); + self._raise('filetestbeforesend', [id, fm, rm, outData]); + }; + fnSuccess = function (data, textStatus, jqXHR) { + outData = self._getOutData(fd, jqXHR, data); + var pNames = self.uploadParamNames, chunksUploaded = pNames.chunksUploaded || 'chunksUploaded', + params = [id, fm, rm, outData]; + if (!data[chunksUploaded] || !$h.isArray(data[chunksUploaded])) { + self._raise('filetesterror', params); + } else { + if (!rm.chunksProcessed[id]) { + rm.chunksProcessed[id] = {}; + } + $.each(data[chunksUploaded], function (key, index) { + rm.logs[index] = true; + rm.chunksProcessed[id][index] = true; + }); + rm.chunksProcessed[id].data = data; + self._raise('filetestsuccess', params); + } + rm.uploadResumable(); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + outData = self._getOutData(fd, jqXHR); + self._raise('filetestajaxerror', [id, fm, rm, outData]); + rm.setAjaxError(jqXHR, textStatus, errorThrown, true); + rm.uploadResumable(); + }; + fnComplete = function () { + self._raise('filetestcomplete', [id, fm, rm, self._getOutData(fd)]); + }; + self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, fd, id, rm.fileIndex, opts.testUrl); + }, + pushAjax: function (index, retry) { + var tm = self.taskManager, pool = tm.getPool(rm.id); + pool.addTask(pool.size() + 1, function (deferrer) { + // use fifo chunk stack + var arr = rm.stack.shift(), index; + index = arr[0]; + if (!rm.chunksProcessed[rm.id] || !rm.chunksProcessed[rm.id][index]) { + rm.sendAjax(index, arr[1], deferrer); + } else { + self._log(logs.chunkQueueError, {index: index}); + } + }); + rm.stack.push([index, retry]); + }, + sendAjax: function (index, retry, deferrer) { + var f, chunkSize = rm.chunkSize, id = rm.id, file = rm.file, $thumb = rm.$thumb, + msgs = $h.logMessages, $btnDelete = rm.$btnDelete, logError = function (msg, tokens) { + if (tokens) { + msg = msg.setTokens(tokens); + } + msg = msgs.resumableRequestError.setTokens({msg: msg}); + self._log(msg); + deferrer.reject(msg); + }; + if (rm.chunksProcessed[id] && rm.chunksProcessed[id][index]) { + return; + } + if (retry > opts.maxRetries) { + logError(msgs.resumableMaxRetriesReached, {n: opts.maxRetries}); + rm.setProcessed('error'); + return; + } + var fd, outData, fnBefore, fnSuccess, fnError, fnComplete, slice = file.slice ? 'slice' : + (file.mozSlice ? 'mozSlice' : (file.webkitSlice ? 'webkitSlice' : 'slice')), + blob = file[slice](chunkSize * index, chunkSize * (index + 1)); + fd = new FormData(); + f = fm.stack[id]; + self._setUploadData(fd, { + chunkCount: rm.chunkCount, + chunkIndex: index, + chunkSize: chunkSize, + chunkSizeStart: chunkSize * index, + fileBlob: [blob, rm.fileName], + fileId: id, + fileName: rm.fileName, + fileRelativePath: f.relativePath, + fileSize: file.size, + retryCount: retry + }); + if (rm.$progress && rm.$progress.length) { + rm.$progress.show(); + } + fnBefore = function (jqXHR) { + outData = self._getOutData(fd, jqXHR); + if (self.showPreview) { + if (!$thumb.hasClass('file-preview-success')) { + self._setThumbStatus($thumb, 'Loading'); + $h.addCss($thumb, 'file-uploading'); + } + $btnDelete.attr('disabled', true); + } + self._raise('filechunkbeforesend', [id, index, retry, fm, rm, outData]); + }; + fnSuccess = function (data, textStatus, jqXHR) { + if (self._isAborted()) { + logError(msgs.resumableAborting); + return; + } + outData = self._getOutData(fd, jqXHR, data); + var paramNames = self.uploadParamNames, chunkIndex = paramNames.chunkIndex || 'chunkIndex', + params = [id, index, retry, fm, rm, outData]; + if (data.error) { + if (opts.showErrorLog) { + self._log(logs.retryStatus, { + retry: retry + 1, + filename: rm.fileName, + chunk: index + }); + } + self._raise('filechunkerror', params); + rm.pushAjax(index, retry + 1); + rm.error = data.error; + logError(data.error); + } else { + rm.logs[data[chunkIndex]] = true; + if (!rm.chunksProcessed[id]) { + rm.chunksProcessed[id] = {}; + } + rm.chunksProcessed[id][data[chunkIndex]] = true; + rm.chunksProcessed[id].data = data; + deferrer.resolve.call(null, data); + self._raise('filechunksuccess', params); + rm.check(); + } + }; + fnError = function (jqXHR, textStatus, errorThrown) { + if (self._isAborted()) { + logError(msgs.resumableAborting); + return; + } + outData = self._getOutData(fd, jqXHR); + rm.setAjaxError(jqXHR, textStatus, errorThrown); + self._raise('filechunkajaxerror', [id, index, retry, fm, rm, outData]); + rm.pushAjax(index, retry + 1); // push another task + logError(msgs.resumableRetryError, {n: retry - 1}); // resolve the current task + }; + fnComplete = function () { + if (!self._isAborted()) { + self._raise('filechunkcomplete', [id, index, retry, fm, rm, self._getOutData(fd)]); + } + }; + self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, fd, id, rm.fileIndex); + } + }; + rm.reset(); + }, + _initTemplateDefaults: function () { + var self = this, tMain1, tMain2, tPreview, tFileIcon, tClose, tCaption, tBtnDefault, tBtnLink, tBtnBrowse, + tModalMain, tModal, tProgress, tSize, tFooter, tActions, tActionDelete, tActionUpload, tActionDownload, + tActionZoom, tActionDrag, tIndicator, tTagBef, tTagBef1, tTagBef2, tTagAft, tGeneric, tHtml, tImage, + tText, tOffice, tGdocs, tVideo, tAudio, tFlash, tObject, tPdf, tOther, tStyle, tZoomCache, vDefaultDim, + tStats, tModalLabel, tDescClose, renderObject = function (type, mime) { + return '\n' + $h.DEFAULT_PREVIEW + '\n\n'; + }, defBtnCss1 = 'btn btn-sm btn-kv ' + $h.defaultButtonCss(); + tMain1 = '{preview}\n' + + '
    \n' + + '
    \n' + + '
    \n' + + ' {caption}\n\n' + + ($h.isBs(5) ? '' : '
    \n') + + ' {remove}\n' + + ' {cancel}\n' + + ' {pause}\n' + + ' {upload}\n' + + ' {browse}\n' + + ($h.isBs(5) ? '' : '
    \n') + + '
    ' + '
    '; + tMain2 = '{preview}\n
    \n
    \n' + + '{remove}\n{cancel}\n{upload}\n{browse}\n'; + tPreview = '
    \n' + + ' {close}' + + '
    \n' + + '
    \n' + + '
    \n' + + '
    \n' + + '
    \n' + + '
    \n' + + '
    '; + tClose = $h.closeButton('fileinput-remove'); + tFileIcon = ''; + // noinspection HtmlUnknownAttribute + tCaption = '\n'; + //noinspection HtmlUnknownAttribute + tBtnDefault = ''; + //noinspection HtmlUnknownTarget,HtmlUnknownAttribute + tBtnLink = '{icon} {label}'; + //noinspection HtmlUnknownAttribute + tBtnBrowse = '
    {icon} {label}
    '; + tModalLabel = $h.MODAL_ID + 'Label'; + tModalMain = ''; + tModal = '\n'; + tDescClose = ''; + tProgress = '
    \n' + + '
    \n' + + ' {status}\n' + + '
    \n' + + '
    {stats}'; + tStats = '
    ' + + '{pendingTime} ' + + '{uploadSpeed}' + + '
    '; + tSize = ' ({sizeText})'; + tFooter = ''; + tActions = '
    \n' + + ' \n' + + '
    \n' + + '{drag}\n' + + '
    '; + //noinspection HtmlUnknownAttribute + tActionDelete = '\n'; + tActionUpload = ''; + tActionDownload = '{downloadIcon}'; + tActionZoom = ''; + tActionDrag = '{dragIcon}'; + tIndicator = '
    {indicator}
    '; + tTagBef = '
    \n'; + tTagBef2 = tTagBef + ' title="{caption}">
    \n'; + tTagAft = '
    {footer}\n{zoomCache}
    \n'; + tGeneric = '{content}\n'; + tStyle = ' {style}'; + tHtml = renderObject('html', 'text/html'); + tText = renderObject('text', 'text/plain;charset=UTF-8'); + tPdf = renderObject('pdf', 'application/pdf'); + tImage = '{alt}\n'; + tOffice = ''; + tGdocs = ''; + tVideo = '\n'; + tAudio = '\n'; + tFlash = '\n'; + tObject = '\n' + '\n' + + $h.OBJECT_PARAMS + ' ' + $h.DEFAULT_PREVIEW + '\n\n'; + tOther = '
    \n' + $h.DEFAULT_PREVIEW + '\n
    \n'; + tZoomCache = '
    {zoomContent}
    '; + vDefaultDim = {width: '100%', height: '100%', 'min-height': '480px'}; + if (self._isPdfRendered()) { + tPdf = self.pdfRendererTemplate.replace('{renderer}', self._encodeURI(self.pdfRendererUrl)); + } + self.defaults = { + layoutTemplates: { + main1: tMain1, + main2: tMain2, + preview: tPreview, + close: tClose, + fileIcon: tFileIcon, + caption: tCaption, + modalMain: tModalMain, + modal: tModal, + descriptionClose: tDescClose, + progress: tProgress, + stats: tStats, + size: tSize, + footer: tFooter, + indicator: tIndicator, + actions: tActions, + actionDelete: tActionDelete, + actionUpload: tActionUpload, + actionDownload: tActionDownload, + actionZoom: tActionZoom, + actionDrag: tActionDrag, + btnDefault: tBtnDefault, + btnLink: tBtnLink, + btnBrowse: tBtnBrowse, + zoomCache: tZoomCache + }, + previewMarkupTags: { + tagBefore1: tTagBef1, + tagBefore2: tTagBef2, + tagAfter: tTagAft + }, + previewContentTemplates: { + generic: tGeneric, + html: tHtml, + image: tImage, + text: tText, + office: tOffice, + gdocs: tGdocs, + video: tVideo, + audio: tAudio, + flash: tFlash, + object: tObject, + pdf: tPdf, + other: tOther + }, + allowedPreviewTypes: ['image', 'html', 'text', 'video', 'audio', 'flash', 'pdf', 'object'], + previewTemplates: {}, + previewSettings: { + image: {width: 'auto', height: 'auto', 'max-width': '100%', 'max-height': '100%'}, + html: {width: '213px', height: '160px'}, + text: {width: '213px', height: '160px'}, + office: {width: '213px', height: '160px'}, + gdocs: {width: '213px', height: '160px'}, + video: {width: '213px', height: '160px'}, + audio: {width: '100%', height: '30px'}, + flash: {width: '213px', height: '160px'}, + object: {width: '213px', height: '160px'}, + pdf: {width: '100%', height: '160px', 'position': 'relative'}, + other: {width: '213px', height: '160px'} + }, + previewSettingsSmall: { + image: {width: 'auto', height: 'auto', 'max-width': '100%', 'max-height': '100%'}, + html: {width: '100%', height: '160px'}, + text: {width: '100%', height: '160px'}, + office: {width: '100%', height: '160px'}, + gdocs: {width: '100%', height: '160px'}, + video: {width: '100%', height: 'auto'}, + audio: {width: '100%', height: '30px'}, + flash: {width: '100%', height: 'auto'}, + object: {width: '100%', height: 'auto'}, + pdf: {width: '100%', height: '160px'}, + other: {width: '100%', height: '160px'} + }, + previewZoomSettings: { + image: {width: 'auto', height: 'auto', 'max-width': '100%', 'max-height': '100%'}, + html: vDefaultDim, + text: vDefaultDim, + office: {width: '100%', height: '100%', 'max-width': '100%', 'min-height': '480px'}, + gdocs: {width: '100%', height: '100%', 'max-width': '100%', 'min-height': '480px'}, + video: {width: 'auto', height: '100%', 'max-width': '100%'}, + audio: {width: '100%', height: '30px'}, + flash: {width: 'auto', height: '480px'}, + object: {width: 'auto', height: '100%', 'max-width': '100%', 'min-height': '480px'}, + pdf: vDefaultDim, + other: {width: 'auto', height: '100%', 'min-height': '480px'} + }, + mimeTypeAliases: { + 'video/quicktime': 'video/mp4' + }, + fileTypeSettings: { + image: function (vType, vName) { + return ($h.compare(vType, 'image.*') && !$h.compare(vType, /(tiff?|wmf)$/i) || + $h.compare(vName, /\.(gif|png|jpe?g)$/i)); + }, + html: function (vType, vName) { + return $h.compare(vType, 'text/html') || $h.compare(vName, /\.(htm|html)$/i); + }, + office: function (vType, vName) { + return $h.compare(vType, /(word|excel|powerpoint|office)$/i) || + $h.compare(vName, /\.(docx?|xlsx?|pptx?|pps|potx?)$/i); + }, + gdocs: function (vType, vName) { + return $h.compare(vType, /(word|excel|powerpoint|office|iwork-pages|tiff?)$/i) || + $h.compare(vName, + /\.(docx?|xlsx?|pptx?|pps|potx?|rtf|ods|odt|pages|ai|dxf|ttf|tiff?|wmf|e?ps)$/i); + }, + text: function (vType, vName) { + return $h.compare(vType, 'text.*') || $h.compare(vName, /\.(xml|javascript)$/i) || + $h.compare(vName, /\.(txt|md|nfo|ini|json|php|js|css)$/i); + }, + video: function (vType, vName) { + return $h.compare(vType, 'video.*') && ($h.compare(vType, /(ogg|mp4|mp?g|mov|webm|3gp)$/i) || + $h.compare(vName, /\.(og?|mp4|webm|mp?g|mov|3gp)$/i)); + }, + audio: function (vType, vName) { + return $h.compare(vType, 'audio.*') && ($h.compare(vName, /(ogg|mp3|mp?g|wav)$/i) || + $h.compare(vName, /\.(og?|mp3|mp?g|wav)$/i)); + }, + flash: function (vType, vName) { + return $h.compare(vType, 'application/x-shockwave-flash', true) || $h.compare(vName, + /\.(swf)$/i); + }, + pdf: function (vType, vName) { + return $h.compare(vType, 'application/pdf', true) || $h.compare(vName, /\.(pdf)$/i); + }, + object: function () { + return true; + }, + other: function () { + return true; + } + }, + fileActionSettings: { + showRemove: true, + showUpload: true, + showDownload: true, + showZoom: true, + showDrag: true, + removeIcon: '', + removeClass: defBtnCss1, + removeErrorClass: 'btn btn-sm btn-kv btn-danger', + removeTitle: 'Remove file', + uploadIcon: '', + uploadClass: defBtnCss1, + uploadTitle: 'Upload file', + uploadRetryIcon: '', + uploadRetryTitle: 'Retry upload', + downloadIcon: '', + downloadClass: defBtnCss1, + downloadTitle: 'Download file', + zoomIcon: '', + zoomClass: defBtnCss1, + zoomTitle: 'View Details', + dragIcon: '', + dragClass: 'text-primary', + dragTitle: 'Move / Rearrange', + dragSettings: {}, + indicatorNew: '', + indicatorSuccess: '', + indicatorError: '', + indicatorLoading: '', + indicatorPaused: '', + indicatorNewTitle: 'Not uploaded yet', + indicatorSuccessTitle: 'Uploaded', + indicatorErrorTitle: 'Upload Error', + indicatorLoadingTitle: 'Uploading …', + indicatorPausedTitle: 'Upload Paused' + } + }; + $.each(self.defaults, function (key, setting) { + if (key === 'allowedPreviewTypes') { + if (self.allowedPreviewTypes === undefined) { + self.allowedPreviewTypes = setting; + } + return; + } + self[key] = $.extend(true, {}, setting, self[key]); + }); + self._initPreviewTemplates(); + }, + _initPreviewTemplates: function () { + var self = this, tags = self.previewMarkupTags, tagBef, tagAft = tags.tagAfter; + $.each(self.previewContentTemplates, function (key, value) { + if ($h.isEmpty(self.previewTemplates[key])) { + tagBef = tags.tagBefore2; + if (key === 'generic' || key === 'image') { + tagBef = tags.tagBefore1; + } + if (self._isPdfRendered() && key === 'pdf') { + tagBef = tagBef.replace('kv-file-content', 'kv-file-content kv-pdf-rendered'); + } + self.previewTemplates[key] = tagBef + value + tagAft; + } + }); + }, + _initPreviewCache: function () { + var self = this; + self.previewCache = { + data: {}, + init: function () { + var content = self.initialPreview; + if (content.length > 0 && !$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + self.previewCache.data = { + content: content, + config: self.initialPreviewConfig, + tags: self.initialPreviewThumbTags + }; + }, + count: function (skipNull) { + if (!self.previewCache.data || !self.previewCache.data.content) { + return 0; + } + if (skipNull) { + var chk = self.previewCache.data.content.filter(function (n) { + return n !== null; + }); + return chk.length; + } + return self.previewCache.data.content.length; + }, + get: function (i, isDisabled) { + var ind = $h.INIT_FLAG + i, data = self.previewCache.data, config = data.config[i], + content = data.content[i], out, $tmp, cat, ftr, + fname, ftype, frameClass, asData = $h.ifSet('previewAsData', config, self.initialPreviewAsData), + a = config ? {title: config.title || null, alt: config.alt || null} : {title: null, alt: null}, + parseTemplate = function (cat, dat, fname, ftype, ftr, ind, fclass, t) { + var fc = ' file-preview-initial ' + $h.SORT_CSS + (fclass ? ' ' + fclass : ''), + id = self.previewInitId + '-' + ind, + fileId = config && config.fileId || id; + /** @namespace config.zoomData */ + return self._generatePreviewTemplate(cat, dat, fname, ftype, id, fileId, false, null, fc, + ftr, ind, t, a, config && config.zoomData || dat); + }; + if (!content || !content.length) { + return ''; + } + isDisabled = isDisabled === undefined ? true : isDisabled; + cat = $h.ifSet('type', config, self.initialPreviewFileType || 'generic'); + fname = $h.ifSet('filename', config, $h.ifSet('caption', config)); + ftype = $h.ifSet('filetype', config, cat); + ftr = self.previewCache.footer(i, isDisabled, (config && config.size || null)); + frameClass = $h.ifSet('frameClass', config); + if (asData) { + out = parseTemplate(cat, content, fname, ftype, ftr, ind, frameClass); + } else { + out = parseTemplate('generic', content, fname, ftype, ftr, ind, frameClass, cat) + .setTokens({'content': data.content[i]}); + } + if (data.tags.length && data.tags[i]) { + out = $h.replaceTags(out, data.tags[i]); + } + /** @namespace config.frameAttr */ + if (!$h.isEmpty(config) && !$h.isEmpty(config.frameAttr)) { + $tmp = $h.createElement(out); + $tmp.find('.file-preview-initial').attr(config.frameAttr); + out = $tmp.html(); + $tmp.remove(); + } + return out; + }, + clean: function (data) { + data.content = $h.cleanArray(data.content); + data.config = $h.cleanArray(data.config); + data.tags = $h.cleanArray(data.tags); + self.previewCache.data = data; + }, + add: function (content, config, tags, append) { + var data = self.previewCache.data, index; + if (!content || !content.length) { + return 0; + } + index = content.length - 1; + if (!$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + if (append && data.content) { + index = data.content.push(content[0]) - 1; + data.config[index] = config; + data.tags[index] = tags; + } else { + data.content = content; + data.config = config; + data.tags = tags; + } + self.previewCache.clean(data); + return index; + }, + set: function (content, config, tags, append) { + var data = self.previewCache.data, i, chk; + if (!content || !content.length) { + return; + } + if (!$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + chk = content.filter(function (n) { + return n !== null; + }); + if (!chk.length) { + return; + } + if (data.content === undefined) { + data.content = []; + } + if (data.config === undefined) { + data.config = []; + } + if (data.tags === undefined) { + data.tags = []; + } + if (append) { + for (i = 0; i < content.length; i++) { + if (content[i]) { + data.content.push(content[i]); + } + } + for (i = 0; i < config.length; i++) { + if (config[i]) { + data.config.push(config[i]); + } + } + for (i = 0; i < tags.length; i++) { + if (tags[i]) { + data.tags.push(tags[i]); + } + } + } else { + data.content = content; + data.config = config; + data.tags = tags; + } + self.previewCache.clean(data); + }, + unset: function (index) { + var chk = self.previewCache.count(), rev = self.reversePreviewOrder; + if (!chk) { + return; + } + if (chk === 1) { + self.previewCache.data.content = []; + self.previewCache.data.config = []; + self.previewCache.data.tags = []; + self.initialPreview = []; + self.initialPreviewConfig = []; + self.initialPreviewThumbTags = []; + return; + } + self.previewCache.data.content = $h.spliceArray(self.previewCache.data.content, index, rev); + self.previewCache.data.config = $h.spliceArray(self.previewCache.data.config, index, rev); + self.previewCache.data.tags = $h.spliceArray(self.previewCache.data.tags, index, rev); + var data = $.extend(true, {}, self.previewCache.data); + self.previewCache.clean(data); + }, + out: function () { + var html = '', caption, len = self.previewCache.count(), i, content; + if (len === 0) { + return {content: '', caption: ''}; + } + for (i = 0; i < len; i++) { + content = self.previewCache.get(i); + html = self.reversePreviewOrder ? (content + html) : (html + content); + } + caption = self._getMsgSelected(len); + return {content: html, caption: caption}; + }, + footer: function (i, isDisabled, size) { + var data = self.previewCache.data || {}; + if ($h.isEmpty(data.content)) { + return ''; + } + if ($h.isEmpty(data.config) || $h.isEmpty(data.config[i])) { + data.config[i] = {}; + } + isDisabled = isDisabled === undefined ? true : isDisabled; + var config = data.config[i], caption = $h.ifSet('caption', config), a, + width = $h.ifSet('width', config, 'auto'), url = $h.ifSet('url', config, false), + key = $h.ifSet('key', config, null), fileId = $h.ifSet('fileId', config, null), + fs = self.fileActionSettings, initPreviewShowDel = self.initialPreviewShowDelete || false, + downloadInitialUrl = !self.initialPreviewDownloadUrl ? '' : + self.initialPreviewDownloadUrl + '?key=' + key + (fileId ? '&fileId=' + fileId : ''), + dUrl = config.downloadUrl || downloadInitialUrl, + dFil = config.filename || config.caption || '', + initPreviewShowDwl = !!(dUrl), + sDel = $h.ifSet('showRemove', config, initPreviewShowDel), + sDwl = $h.ifSet('showDownload', config, $h.ifSet('showDownload', fs, initPreviewShowDwl)), + sZm = $h.ifSet('showZoom', config, $h.ifSet('showZoom', fs, true)), + sDrg = $h.ifSet('showDrag', config, $h.ifSet('showDrag', fs, true)), + dis = (url === false) && isDisabled; + sDwl = sDwl && config.downloadUrl !== false && !!dUrl; + a = self._renderFileActions(config, false, sDwl, sDel, sZm, sDrg, dis, url, key, true, dUrl, dFil); + return self._getLayoutTemplate('footer').setTokens({ + 'progress': self._renderThumbProgress(), + 'actions': a, + 'caption': caption, + 'size': self._getSize(size), + 'width': width, + 'indicator': '' + }); + } + }; + self.previewCache.init(); + }, + _isPdfRendered: function () { + var self = this, useLib = self.usePdfRenderer, + flag = typeof useLib === 'function' ? useLib() : !!useLib; + return flag && self.pdfRendererUrl; + }, + _handler: function ($el, event, callback) { + var self = this, ns = self.namespace, ev = event.split(' ').join(ns + ' ') + ns; + if (!$el || !$el.length) { + return; + } + $el.off(ev).on(ev, callback); + }, + _encodeURI: function (vUrl) { + var self = this; + return self.encodeUrl ? encodeURI(vUrl) : vUrl; + }, + _log: function (msg, tokens) { + var self = this, id = self.$element.attr('id'); + if (!self.showConsoleLogs) { + return; + } + if (id) { + msg = '"' + id + '": ' + msg; + } + msg = 'bootstrap-fileinput: ' + msg; + if (typeof tokens === 'object') { + msg = msg.setTokens(tokens); + } + if (window.console && typeof window.console.log !== 'undefined') { + window.console.log(msg); + } else { + window.alert(msg); + } + }, + _validate: function () { + var self = this, status = self.$element.attr('type') === 'file'; + if (!status) { + self._log($h.logMessages.badInputType); + } + return status; + }, + _errorsExist: function () { + var self = this, $err, $errList = self.$errorContainer.find('li'); + if ($errList.length) { + return true; + } + $err = $h.createElement(self.$errorContainer.html()); + $err.find('.kv-error-close').remove(); + $err.find('ul').remove(); + return !!$.trim($err.text()).length; + }, + _errorHandler: function (evt, caption) { + var self = this, err = evt.target.error, showError = function (msg) { + self._showError(msg.replace('{name}', caption)); + }; + /** @namespace err.NOT_FOUND_ERR */ + /** @namespace err.SECURITY_ERR */ + /** @namespace err.NOT_READABLE_ERR */ + if (err.code === err.NOT_FOUND_ERR) { + showError(self.msgFileNotFound); + } else { + if (err.code === err.SECURITY_ERR) { + showError(self.msgFileSecured); + } else { + if (err.code === err.NOT_READABLE_ERR) { + showError(self.msgFileNotReadable); + } else { + if (err.code === err.ABORT_ERR) { + showError(self.msgFilePreviewAborted); + } else { + showError(self.msgFilePreviewError); + } + } + } + } + }, + _addError: function (msg) { + var self = this, $error = self.$errorContainer; + if (msg && $error.length) { + $h.setHtml($error, self.errorCloseButton + msg); + self._handler($error.find('.kv-error-close'), 'click', function () { + setTimeout(function () { + if (self.showPreview && !self.getFrames().length) { + self.clear(); + } + $error.fadeOut('slow'); + }, self.processDelay); + }); + } + }, + _setValidationError: function (css) { + var self = this; + css = (css ? css + ' ' : '') + 'has-error'; + self.$container.removeClass(css).addClass('has-error'); + $h.addCss(self.$caption, 'is-invalid'); + }, + _resetErrors: function (fade) { + var self = this, $error = self.$errorContainer, history = self.resumableUploadOptions.retainErrorHistory; + if (self.isPersistentError || (self.enableResumableUpload && history)) { + return; + } + self.isError = false; + self.$container.removeClass('has-error'); + self.$caption.removeClass('is-invalid is-valid file-processing'); + $error.html(''); + if (fade) { + $error.fadeOut('slow'); + } else { + $error.hide(); + } + }, + _showFolderError: function (folders) { + var self = this, $error = self.$errorContainer, msg; + if (!folders) { + return; + } + if (!self.isAjaxUpload) { + self._clearFileInput(); + } + msg = self.msgFoldersNotAllowed.replace('{n}', folders); + self._addError(msg); + self._setValidationError(); + $error.fadeIn(self.fadeDelay); + self._raise('filefoldererror', [folders, msg]); + }, + _showFileError: function (msg, params, event) { + var self = this, $error = self.$errorContainer, ev = event || 'fileuploaderror', + fId = params && params.fileId || '', e = params && params.id ? + '
  • ' + msg + '
  • ' : '
  • ' + msg + '
  • '; + + if ($error.find('ul').length === 0) { + self._addError('
      ' + e + '
    '); + } else { + $error.find('ul').append(e); + } + $error.fadeIn(self.fadeDelay); + self._raise(ev, [params, msg]); + self._setValidationError('file-input-new'); + return true; + }, + _showError: function (msg, params, event) { + var self = this, $error = self.$errorContainer, ev = event || 'fileerror'; + params = params || {}; + params.reader = self.reader; + self._addError(msg); + $error.fadeIn(self.fadeDelay); + self._raise(ev, [params, msg]); + if (!self.isAjaxUpload) { + self._clearFileInput(); + } + self._setValidationError('file-input-new'); + self.$btnUpload.attr('disabled', true); + return true; + }, + _noFilesError: function (params) { + var self = this, label = self.minFileCount > 1 ? self.filePlural : self.fileSingle, + msg = self.msgFilesTooLess.replace('{n}', self.minFileCount).replace('{files}', label), + $error = self.$errorContainer; + msg = '
  • ' + msg + '
  • '; + if ($error.find('ul').length === 0) { + self._addError('
      ' + msg + '
    '); + } else { + $error.find('ul').append(msg); + } + self.isError = true; + self._updateFileDetails(0); + $error.fadeIn(self.fadeDelay); + self._raise('fileerror', [params, msg]); + self._clearFileInput(); + self._setValidationError(); + }, + _parseError: function (operation, jqXHR, errorThrown, fileName) { + /** @namespace jqXHR.responseJSON */ + var self = this, errMsg = $.trim(errorThrown + ''), textPre, errText, text; + errText = jqXHR.responseJSON && jqXHR.responseJSON.error ? jqXHR.responseJSON.error.toString() : ''; + text = errText ? errText : jqXHR.responseText; + if (self.cancelling && self.msgUploadAborted) { + errMsg = self.msgUploadAborted; + } + if (self.showAjaxErrorDetails && text) { + if (errText) { + errMsg = $.trim(errText + ''); + } else { + text = $.trim(text.replace(/\n\s*\n/g, '\n')); + textPre = text.length ? '
    ' + text + '
    ' : ''; + errMsg += errMsg ? textPre : text; + } + } + if (!errMsg) { + errMsg = self.msgAjaxError.replace('{operation}', operation); + } + self.cancelling = false; + return fileName ? '' + fileName + ': ' + errMsg : errMsg; + }, + _parseFileType: function (type, name) { + var self = this, isValid, vType, cat, i, types = self.allowedPreviewTypes || []; + if (type === 'application/text-plain') { + return 'text'; + } + for (i = 0; i < types.length; i++) { + cat = types[i]; + isValid = self.fileTypeSettings[cat]; + vType = isValid(type, name) ? cat : ''; + if (!$h.isEmpty(vType)) { + return vType; + } + } + return 'other'; + }, + _getPreviewIcon: function (fname) { + var self = this, ext, out = null; + if (fname && fname.indexOf('.') > -1) { + ext = fname.split('.').pop(); + if (self.previewFileIconSettings) { + out = self.previewFileIconSettings[ext] || self.previewFileIconSettings[ext.toLowerCase()] || null; + } + if (self.previewFileExtSettings) { + $.each(self.previewFileExtSettings, function (key, func) { + if (self.previewFileIconSettings[key] && func(ext)) { + out = self.previewFileIconSettings[key]; + //noinspection UnnecessaryReturnStatementJS + return; + } + }); + } + } + return out || self.previewFileIcon; + }, + _parseFilePreviewIcon: function (content, fname) { + var self = this, icn = self._getPreviewIcon(fname), out = content; + if (out.indexOf('{previewFileIcon}') > -1) { + out = out.setTokens({'previewFileIconClass': self.previewFileIconClass, 'previewFileIcon': icn}); + } + return out; + }, + _raise: function (event, params) { + var self = this, e = $.Event(event); + if (params !== undefined) { + self.$element.trigger(e, params); + } else { + self.$element.trigger(e); + } + var out = e.result, isAborted = out === false; + if (e.isDefaultPrevented() || isAborted) { + return false; + } + if (e.type === 'filebatchpreupload' && (out || isAborted)) { + self.ajaxAborted = out; + return false; + } + switch (event) { + // ignore these events + case 'filebatchuploadcomplete': + case 'filebatchuploadsuccess': + case 'fileuploaded': + case 'fileclear': + case 'filecleared': + case 'filereset': + case 'fileerror': + case 'filefoldererror': + case 'fileuploaderror': + case 'filebatchuploaderror': + case 'filedeleteerror': + case 'filecustomerror': + case 'filesuccessremove': + break; + // receive data response via `filecustomerror` event` + default: + if (!self.ajaxAborted) { + self.ajaxAborted = out; + } + break; + } + return true; + }, + _listenFullScreen: function (isFullScreen) { + var self = this, $modal = self.$modal, $btnFull, $btnBord; + if (!$modal || !$modal.length) { + return; + } + $btnFull = $modal && $modal.find('.btn-kv-fullscreen'); + $btnBord = $modal && $modal.find('.btn-kv-borderless'); + if (!$btnFull.length || !$btnBord.length) { + return; + } + $btnFull.removeClass('active').attr('aria-pressed', 'false'); + $btnBord.removeClass('active').attr('aria-pressed', 'false'); + if (isFullScreen) { + $btnFull.addClass('active').attr('aria-pressed', 'true'); + } else { + $btnBord.addClass('active').attr('aria-pressed', 'true'); + } + if ($modal.hasClass('file-zoom-fullscreen')) { + self._maximizeZoomDialog(); + } else { + if (isFullScreen) { + self._maximizeZoomDialog(); + } else { + $btnBord.removeClass('active').attr('aria-pressed', 'false'); + } + } + }, + _listen: function () { + var self = this, $el = self.$element, $form = self.$form, $cont = self.$container, fullScreenEv; + self._handler($el, 'click', function (e) { + self._initFileSelected(); + if ($el.hasClass('file-no-browse')) { + if ($el.data('zoneClicked')) { + $el.data('zoneClicked', false); + } else { + e.preventDefault(); + } + } + }); + self._handler($el, 'change', $.proxy(self._change, self)); + self._handler(self.$caption, 'paste', $.proxy(self.paste, self)); + if (self.showBrowse) { + self._handler(self.$btnFile, 'click', $.proxy(self._browse, self)); + self._handler(self.$btnFile, 'keypress', function (e) { + var keycode = e.keyCode || e.which + if (keycode === 13) { + $el.trigger('click'); + self._browse(e); + } + }); + } + self._handler($cont.find('.fileinput-remove:not([disabled])'), 'click', $.proxy(self.clear, self)); + self._handler($cont.find('.fileinput-cancel'), 'click', $.proxy(self.cancel, self)); + self._handler($cont.find('.fileinput-pause'), 'click', $.proxy(self.pause, self)); + self._initDragDrop(); + self._handler($form, 'reset', $.proxy(self.clear, self)); + if (!self.isAjaxUpload) { + self._handler($form, 'submit', $.proxy(self._submitForm, self)); + } + self._handler(self.$container.find('.fileinput-upload'), 'click', $.proxy(self._uploadClick, self)); + self._handler($(window), 'resize', function () { + self._listenFullScreen(screen.width === window.innerWidth && screen.height === window.innerHeight); + }); + fullScreenEv = 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'; + self._handler($(document), fullScreenEv, function () { + self._listenFullScreen($h.checkFullScreen()); + }); + self.$caption.on('focus', function () { + self.$captionContainer.focus(); + }); + self._autoFitContent(); + self._initClickable(); + self._refreshPreview(); + }, + _autoFitContent: function () { + var width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + self = this, config = width < 400 ? (self.previewSettingsSmall || self.defaults.previewSettingsSmall) : + (self.previewSettings || self.defaults.previewSettings), sel; + $.each(config, function (cat, settings) { + sel = '.file-preview-frame .file-preview-' + cat; + self.$preview.find(sel + '.kv-preview-data,' + sel + ' .kv-preview-data').css(settings); + }); + }, + _scanDroppedItems: function (item, files, path) { + path = path || ''; + var self = this, i, dirReader, readDir, errorHandler = function (e) { + self._log($h.logMessages.badDroppedFiles); + self._log(e); + }; + if (item.isFile) { + item.file(function (file) { + if (path) { + file.newPath = path + file.name; + } + files.push(file); + }, errorHandler); + } else { + if (item.isDirectory) { + dirReader = item.createReader(); + readDir = function () { + dirReader.readEntries(function (entries) { + if (entries && entries.length > 0) { + for (i = 0; i < entries.length; i++) { + self._scanDroppedItems(entries[i], files, path + item.name + '/'); + } + // recursively call readDir() again, since browser can only handle first 100 entries. + readDir(); + } + return null; + }, errorHandler); + }; + readDir(); + } + } + + }, + _initDragDrop: function () { + var self = this, $zone = self.$dropZone; + if (self.dropZoneEnabled && self.showPreview) { + self._handler($zone, 'dragenter dragover', $.proxy(self._zoneDragEnter, self)); + self._handler($zone, 'dragleave', $.proxy(self._zoneDragLeave, self)); + self._handler($zone, 'drop', $.proxy(self._zoneDrop, self)); + self._handler($(document), 'dragenter dragover drop', self._zoneDragDropInit); + } + }, + _zoneDragDropInit: function (e) { + e.stopPropagation(); + e.preventDefault(); + }, + _zoneDragEnter: function (e) { + var self = this, dt = e.originalEvent.dataTransfer, hasFiles = $.inArray('Files', dt.types) > -1; + self._zoneDragDropInit(e); + if (self.isDisabled || !hasFiles) { + dt.effectAllowed = 'none'; + dt.dropEffect = 'none'; + return; + } + dt.dropEffect = 'copy'; + if (self._raise('fileDragEnter', {'sourceEvent': e, 'files': dt.types.Files})) { + $h.addCss(self.$dropZone, 'file-highlighted'); + } + }, + _zoneDragLeave: function (e) { + var self = this; + self._zoneDragDropInit(e); + if (self.isDisabled) { + return; + } + if (self._raise('fileDragLeave', {'sourceEvent': e})) { + self.$dropZone.removeClass('file-highlighted'); + } + + }, + _dropFiles: function (e, files) { + var self = this, $el = self.$element; + if (!self.isAjaxUpload) { + self.changeTriggered = true; + $el.get(0).files = files; + setTimeout(function () { + self.changeTriggered = false; + $el.trigger('change' + self.namespace); + }, self.processDelay); + } else { + self._change(e, files); + } + self.$dropZone.removeClass('file-highlighted'); + }, + _zoneDrop: function (e) { + /** @namespace e.originalEvent.dataTransfer */ + var self = this, i, $el = self.$element, dt = e.originalEvent.dataTransfer, + files = dt.files, items = dt.items, folders = $h.getDragDropFolders(items); + e.preventDefault(); + if (self.isDisabled || $h.isEmpty(files)) { + return; + } + if (!self._raise('fileDragDrop', {'sourceEvent': e, 'files': files})) { + return; + } + if (folders > 0) { + if (!self.isAjaxUpload) { + self._showFolderError(folders); + return; + } + files = []; + for (i = 0; i < items.length; i++) { + var item = items[i].webkitGetAsEntry(); + if (item) { + self._scanDroppedItems(item, files); + } + } + setTimeout(function () { + self._dropFiles(e, files); + }, 500); + } else { + self._dropFiles(e, files); + } + }, + _uploadClick: function (e) { + var self = this, $btn = self.$container.find('.fileinput-upload'), $form, + isEnabled = !$btn.hasClass('disabled') && $h.isEmpty($btn.attr('disabled')); + if (e && e.isDefaultPrevented()) { + return; + } + if (!self.isAjaxUpload) { + if (isEnabled && $btn.attr('type') !== 'submit') { + $form = $btn.closest('form'); + // downgrade to normal form submit if possible + if ($form.length) { + $form.trigger('submit'); + } + e.preventDefault(); + } + return; + } + e.preventDefault(); + if (isEnabled) { + self.upload(); + } + }, + _submitForm: function () { + var self = this; + return self._isFileSelectionValid() && !self._abort({}); + }, + _clearPreview: function () { + var self = this, + $thumbs = self.showUploadedThumbs ? self.getFrames(':not(.file-preview-success)') : self.getFrames(); + $thumbs.each(function () { + var $thumb = $(this); + $thumb.remove(); + }); + if (!self.getFrames().length || !self.showPreview) { + self._resetUpload(); + } + self._validateDefaultPreview(); + }, + _initSortable: function () { + var self = this, $el = self.$preview, settings, selector = '.' + $h.SORT_CSS, $cont, $body = $('body'), + $html = $('html'), rev = self.reversePreviewOrder, Sortable = window.Sortable, beginGrab, endGrab; + if (!Sortable || $el.find(selector).length === 0) { + return; + } + $cont = $body.length ? $body : ($html.length ? $html : self.$container); + beginGrab = function () { + $cont.addClass('file-grabbing'); + }; + endGrab = function () { + $cont.removeClass('file-grabbing'); + }; + settings = { + handle: '.drag-handle-init', + dataIdAttr: 'data-fileid', + animation: 600, + draggable: selector, + scroll: false, + forceFallback: true, + onChoose: beginGrab, + onStart: beginGrab, + onUnchoose: endGrab, + onEnd: endGrab, + onSort: function (e) { + var oldIndex = e.oldIndex, newIndex = e.newIndex, i = 0, len = self.initialPreviewConfig.length, + exceedsLast = len > 0 && newIndex >= len, $item = $(e.item), $first; + if (exceedsLast) { + newIndex = len - 1; + } + self.initialPreview = $h.moveArray(self.initialPreview, oldIndex, newIndex, rev); + self.initialPreviewConfig = $h.moveArray(self.initialPreviewConfig, oldIndex, newIndex, rev); + self.previewCache.init(); + self.getFrames('.file-preview-initial').each(function () { + $(this).attr('data-fileindex', $h.INIT_FLAG + i); + i++; + }); + if (exceedsLast) { + $first = self.getFrames(':not(.file-preview-initial):first'); + if ($first.length) { + $item.slideUp(function () { + $item.insertBefore($first).slideDown(); + }); + } + } + self._raise('filesorted', { + previewId: $item.attr('id'), + 'oldIndex': oldIndex, + 'newIndex': newIndex, + stack: self.initialPreviewConfig + }); + }, + }; + $.extend(true, settings, self.fileActionSettings.dragSettings); + if (self.sortable) { + self.sortable.destroy(); + } + self.sortable = Sortable.create($el[0], settings); + }, + _setPreviewContent: function (content) { + var self = this; + $h.setHtml(self.$preview, content); + self._autoFitContent(); + }, + _initPreviewImageOrientations: function () { + var self = this, i = 0, canOrientImage = self.canOrientImage; + if (!self.autoOrientImageInitial && !canOrientImage) { + return; + } + self.getFrames('.file-preview-initial').each(function () { + var $thumb = $(this), $img, $zoomImg, id, config = self.initialPreviewConfig[i]; + /** @namespace config.exif */ + if (config && config.exif && config.exif.Orientation) { + id = $thumb.attr('id'); + $img = $thumb.find('>.kv-file-content img'); + $zoomImg = self._getZoom(id, ' >.kv-file-content img'); + if (canOrientImage) { + $img.css('image-orientation', (self.autoOrientImageInitial ? 'from-image' : 'none')); + } else { + self.setImageOrientation($img, $zoomImg, config.exif.Orientation, $thumb); + } + } + i++; + }); + }, + _initPreview: function (isInit) { + var self = this, cap = self.initialCaption || '', out; + if (!self.previewCache.count(true)) { + self._clearPreview(); + if (isInit) { + self._setCaption(cap); + } else { + self._initCaption(); + } + return; + } + out = self.previewCache.out(); + cap = isInit && self.initialCaption ? self.initialCaption : out.caption; + self._setPreviewContent(out.content); + self._setInitThumbAttr(); + self._setCaption(cap); + self._initSortable(); + if (!$h.isEmpty(out.content)) { + self.$container.removeClass('file-input-new'); + } + self._initPreviewImageOrientations(); + }, + _getZoomButton: function (type) { + var self = this, label = self.previewZoomButtonIcons[type], css = self.previewZoomButtonClasses[type], + title = ' title="' + (self.previewZoomButtonTitles[type] || '') + '" ', tag = $h.isBs(5) ? 'bs-' : '', + params = title + (type === 'close' ? ' data-' + tag + 'dismiss="modal" aria-hidden="true"' : ''); + if (type === 'fullscreen' || type === 'borderless' || type === 'toggleheader') { + params += ' data-toggle="button" aria-pressed="false" autocomplete="off"'; + } + return ''; + }, + _getModalContent: function () { + var self = this; + return self._getLayoutTemplate('modal').setTokens({ + 'rtl': self.rtl ? ' kv-rtl' : '', + 'zoomFrameClass': self.frameClass, + 'prev': self._getZoomButton('prev'), + 'next': self._getZoomButton('next'), + 'toggleheader': self._getZoomButton('toggleheader'), + 'fullscreen': self._getZoomButton('fullscreen'), + 'borderless': self._getZoomButton('borderless'), + 'close': self._getZoomButton('close') + }); + }, + _listenModalEvent: function (event) { + var self = this, $modal = self.$modal, getParams = function (e) { + return { + sourceEvent: e, + previewId: $modal.data('previewId'), + modal: $modal + }; + }; + $modal.on(event + '.bs.modal', function (e) { + if (e.namespace !== 'bs.modal') { + return; + } + var $btnFull = $modal.find('.btn-fullscreen'), $btnBord = $modal.find('.btn-borderless'); + if ($modal.data('fileinputPluginId') === self.$element.attr('id')) { + self._raise('filezoom' + event, getParams(e)); + } + if (event === 'shown') { + $btnBord.removeClass('active').attr('aria-pressed', 'false'); + $btnFull.removeClass('active').attr('aria-pressed', 'false'); + if ($modal.hasClass('file-zoom-fullscreen')) { + self._maximizeZoomDialog(); + if ($h.checkFullScreen()) { + $btnFull.addClass('active').attr('aria-pressed', 'true'); + } else { + $btnBord.addClass('active').attr('aria-pressed', 'true'); + } + } + } + }); + }, + _initZoom: function () { + var self = this, $dialog, modalMain = self._getLayoutTemplate('modalMain'), modalId = '#' + $h.MODAL_ID; + modalMain = self._setTabIndex('modal', modalMain); + if (!self.showPreview) { + return; + } + self.$modal = $(modalId); + if (!self.$modal || !self.$modal.length) { + $dialog = $h.createElement($h.cspBuffer.stash(modalMain)).insertAfter(self.$container); + self.$modal = $(modalId).insertBefore($dialog); + $h.cspBuffer.apply(self.$modal); + $dialog.remove(); + } + $h.initModal(self.$modal); + self.$modal.html($h.cspBuffer.stash(self._getModalContent())); + $h.cspBuffer.apply(self.$modal); + $.each($h.MODAL_EVENTS, function (key, event) { + self._listenModalEvent(event); + }); + }, + _initZoomButtons: function () { + var self = this, previewId = self.$modal.data('previewId') || '', $first, $last, + thumbs = self.getFrames().toArray(), len = thumbs.length, $prev = self.$modal.find('.btn-kv-prev'), + $next = self.$modal.find('.btn-kv-next'); + if (thumbs.length < 2) { + $prev.hide(); + $next.hide(); + return; + } else { + $prev.show(); + $next.show(); + } + if (!len) { + return; + } + $first = $(thumbs[0]); + $last = $(thumbs[len - 1]); + $prev.removeAttr('disabled'); + $next.removeAttr('disabled'); + if (self.reversePreviewOrder) { + [$prev, $next] = [$next, $prev]; // swap + } + if ($first.length && $first.attr('id') === previewId) { + $prev.attr('disabled', true); + } + if ($last.length && $last.attr('id') === previewId) { + $next.attr('disabled', true); + } + }, + _maximizeZoomDialog: function () { + var self = this, $modal = self.$modal, $head = $modal.find('.modal-header:visible'), + $foot = $modal.find('.modal-footer:visible'), $body = $modal.find('.kv-zoom-body'), + h = $(window).height(), diff = 0; + $modal.addClass('file-zoom-fullscreen'); + if ($head && $head.length) { + h -= $head.outerHeight(true); + } + if ($foot && $foot.length) { + h -= $foot.outerHeight(true); + } + if ($body && $body.length) { + diff = $body.outerHeight(true) - $body.height(); + h -= diff; + } + $modal.find('.kv-zoom-body').height(h); + }, + _resizeZoomDialog: function (fullScreen) { + var self = this, $modal = self.$modal, $btnFull = $modal.find('.btn-kv-fullscreen'), + $btnBord = $modal.find('.btn-kv-borderless'); + if ($modal.hasClass('file-zoom-fullscreen')) { + $h.toggleFullScreen(false); + if (!fullScreen) { + if (!$btnFull.hasClass('active')) { + $modal.removeClass('file-zoom-fullscreen'); + self.$modal.find('.kv-zoom-body').css('height', self.zoomModalHeight); + } else { + $btnFull.removeClass('active').attr('aria-pressed', 'false'); + } + } else { + if (!$btnFull.hasClass('active')) { + $modal.removeClass('file-zoom-fullscreen'); + self._resizeZoomDialog(true); + if ($btnBord.hasClass('active')) { + $btnBord.removeClass('active').attr('aria-pressed', 'false'); + } + } + } + } else { + if (!fullScreen) { + self._maximizeZoomDialog(); + return; + } + $h.toggleFullScreen(true); + } + $modal.focus(); + }, + _setZoomContent: function ($frame, navigate) { + var self = this, $content, tmplt, body, title, $body, $dataEl, config, previewId = $frame.attr('id'), + $zoomPreview = self._getZoom(previewId), $modal = self.$modal, $tmp, desc, $desc, + $btnFull = $modal.find('.btn-kv-fullscreen'), $btnBord = $modal.find('.btn-kv-borderless'), cap, size, + $btnTogh = $modal.find('.btn-kv-toggleheader'), dir = navigate === 'prev' ? 'Left' : 'Right', + slideIn = 'slideIn' + dir, slideOut = 'slideOut' + dir, parsed, zoomData = $frame.data('zoom'); + if (zoomData) { + zoomData = decodeURIComponent(zoomData); + parsed = $zoomPreview.html().setTokens({zoomData: zoomData}); + $zoomPreview.html(parsed); + $frame.data('zoom', ''); + $zoomPreview.attr('data-zoom', zoomData); + } + tmplt = $zoomPreview.attr('data-template') || 'generic'; + $content = $zoomPreview.find('.kv-file-content'); + body = $content.length ? '\n' + $content.html() : ''; + cap = $frame.data('caption') || self.msgZoomModalHeading; + size = $frame.data('size') || ''; + desc = $frame.data('description') || ''; + $modal.find('.kv-zoom-caption').attr('title', cap).html(cap); + $modal.find('.kv-zoom-size').html(size); + $desc = $modal.find('.kv-zoom-description').hide(); + if (desc) { + if (self.showDescriptionClose) { + desc = self._getLayoutTemplate('descriptionClose').setTokens({ + closeIcon: self.previewZoomButtonIcons.close + }) + '' + desc; + } + $desc.show().html(desc); + if (self.showDescriptionClose) { + self._handler($modal.find('.kv-desc-hide'), 'click', function () { + $(this).parent().fadeOut('fast', function () { + $modal.focus(); + }); + }); + } + } + $body = $modal.find('.kv-zoom-body'); + $modal.removeClass('kv-single-content'); + if (navigate) { + $tmp = $body.addClass('file-thumb-loading').clone().insertAfter($body); + $h.setHtml($body, body).hide(); + $tmp.fadeOut('fast', function () { + $body.fadeIn('fast', function () { + $body.removeClass('file-thumb-loading'); + }); + $tmp.remove(); + }); + } else { + $h.setHtml($body, body); + } + config = self.previewZoomSettings[tmplt]; + if (config) { + $dataEl = $body.find('.kv-preview-data'); + $h.addCss($dataEl, 'file-zoom-detail'); + $.each(config, function (key, value) { + $dataEl.css(key, value); + if (($dataEl.attr('width') && key === 'width') || ($dataEl.attr('height') && key === 'height')) { + $dataEl.removeAttr(key); + } + }); + } + $modal.data('previewId', previewId); + self._handler($modal.find('.btn-kv-prev'), 'click', function () { + self._zoomSlideShow('prev', previewId); + }); + self._handler($modal.find('.btn-kv-next'), 'click', function () { + self._zoomSlideShow('next', previewId); + }); + self._handler($btnFull, 'click', function () { + self._resizeZoomDialog(true); + }); + self._handler($btnBord, 'click', function () { + self._resizeZoomDialog(false); + }); + self._handler($btnTogh, 'click', function () { + var $header = $modal.find('.modal-header'), $floatBar = $modal.find('.floating-buttons'), + ht, $actions = $header.find('.kv-zoom-actions'), resize = function (height) { + var $body = self.$modal.find('.kv-zoom-body'), h = self.zoomModalHeight; + if ($modal.hasClass('file-zoom-fullscreen')) { + h = $body.outerHeight(true); + if (!height) { + h = h - $header.outerHeight(true); + } + } + $body.css('height', height ? h + height : h); + }; + if ($header.is(':visible')) { + ht = $header.outerHeight(true); + $header.slideUp('slow', function () { + $actions.find('.btn').appendTo($floatBar); + resize(ht); + }); + } else { + $floatBar.find('.btn').appendTo($actions); + $header.slideDown('slow', function () { + resize(); + }); + } + $modal.focus(); + }); + self._handler($modal, 'keydown', function (e) { + var key = e.which || e.keyCode, delay = self.processDelay + 1, $prev = $(this).find('.btn-kv-prev'), + $next = $(this).find('.btn-kv-next'), vId = $(this).data('previewId'), vPrevKey, vNextKey; + [vPrevKey, vNextKey] = self.rtl ? [39, 37] : [37, 39]; + $.each({prev: [$prev, vPrevKey], next: [$next, vNextKey]}, function (direction, config) { + var $btn = config[0], vKey = config[1]; + if (key === vKey && $btn.length) { + $modal.focus(); + if (!$btn.attr('disabled')) { + $btn.focus(); + self._zoomSlideShow(direction, vId); + setTimeout(function () { + if ($btn.attr('disabled')) { + $modal.focus(); + } + }, delay); + } + } + }); + }); + }, + _showModal: function ($frame) { + var self = this, $modal = self.$modal, bs5Modal; + if (!$frame || !$frame.length) { + return; + } + $h.initModal($modal); + $h.setHtml($modal, self._getModalContent()); + self._setZoomContent($frame); + $modal.data({backdrop: false}); + //$modal.data('fileinputPluginId', self.$element.attr('id')); + $modal.modal('show'); + self._initZoomButtons(); + }, + _zoomPreview: function ($btn) { + var self = this, $frame; + if (!$btn.length) { + throw 'Cannot zoom to detailed preview!'; + } + $frame = $btn.closest($h.FRAMES); + self._showModal($frame); + }, + _zoomSlideShow: function (dir, previewId) { + var self = this, $btn = self.$modal.find('.kv-zoom-actions .btn-kv-' + dir), $targFrame, i, $thumb, + thumbsData = self.getFrames().toArray(), thumbs = [], len = thumbsData.length, out; + if (self.reversePreviewOrder) { + dir = dir === 'prev' ? 'next' : 'prev'; + } + if ($btn.attr('disabled')) { + return; + } + for (i = 0; i < len; i++) { + $thumb = $(thumbsData[i]); + if ($thumb && $thumb.length && $thumb.find('.kv-file-zoom:visible').length) { + thumbs.push(thumbsData[i]); + } + } + len = thumbs.length; + for (i = 0; i < len; i++) { + if ($(thumbs[i]).attr('id') === previewId) { + out = dir === 'prev' ? i - 1 : i + 1; + break; + } + } + if (out < 0 || out >= len || !thumbs[out]) { + return; + } + $targFrame = $(thumbs[out]); + if ($targFrame.length) { + self._setZoomContent($targFrame, dir); + } + self._initZoomButtons(); + self._raise('filezoom' + dir, {'previewId': previewId, modal: self.$modal}); + }, + _initZoomButton: function () { + var self = this; + self.$preview.find('.kv-file-zoom').each(function () { + var $el = $(this); + self._handler($el, 'click', function () { + self._zoomPreview($el); + }); + }); + }, + _inputFileCount: function () { + return this.$element[0].files.length; + }, + _refreshPreview: function () { + var self = this, files; + if ((!self._inputFileCount() && !self.isAjaxUpload) || !self.showPreview || !self.isPreviewable) { + return; + } + if (self.isAjaxUpload) { + if (self.fileManager.count() > 0) { + files = $.extend(true, {}, self.getFileList()); + self.fileManager.clear(); + self._clearFileInput(); + } else { + files = self.$element[0].files; + } + } else { + files = self.$element[0].files; + } + if (files && files.length) { + self.readFiles(files); + self._setFileDropZoneTitle(); + } + }, + _clearObjects: function ($el) { + $el.find('video audio').each(function () { + this.pause(); + $(this).remove(); + }); + $el.find('img object div').each(function () { + $(this).remove(); + }); + }, + _clearFileInput: function () { + var self = this, $el = self.$element, $srcFrm, $tmpFrm, $tmpEl; + if (!self._inputFileCount()) { + return; + } + $srcFrm = $el.closest('form'); + $tmpFrm = $(document.createElement('form')); + $tmpEl = $(document.createElement('div')); + $el.before($tmpEl); + if ($srcFrm.length) { + $srcFrm.after($tmpFrm); + } else { + $tmpEl.after($tmpFrm); + } + $tmpFrm.append($el).trigger('reset'); + $tmpEl.before($el).remove(); + $tmpFrm.remove(); + }, + _resetUpload: function () { + var self = this; + self.uploadStartTime = $h.now(); + self.uploadCache = []; + self.$btnUpload.removeAttr('disabled'); + self._setProgress(0); + self._hideProgress(); + self._resetErrors(false); + self._initAjax(); + self.fileManager.clearImages(); + self._resetCanvas(); + if (self.overwriteInitial) { + self.initialPreview = []; + self.initialPreviewConfig = []; + self.initialPreviewThumbTags = []; + self.previewCache.data = { + content: [], + config: [], + tags: [] + }; + } + }, + _resetCanvas: function () { + var self = this; + if (self.imageCanvas && self.imageCanvasContext) { + self.imageCanvasContext.clearRect(0, 0, self.imageCanvas.width, self.imageCanvas.height); + } + }, + _hasInitialPreview: function () { + var self = this; + return !self.overwriteInitial && self.previewCache.count(true); + }, + _resetPreview: function () { + var self = this, out, cap, $div, hasSuc = self.showUploadedThumbs, hasErr = !self.removeFromPreviewOnError, + includeProcessed = (hasSuc || hasErr) && self.isDuplicateError; + if (self.previewCache.count(true)) { + out = self.previewCache.out(); + if (includeProcessed) { + $div = $h.createElement('').insertAfter(self.$container); + self.getFrames().each(function () { + var $thumb = $(this); + if ((hasSuc && $thumb.hasClass('file-preview-success')) || + (hasErr && $thumb.hasClass('file-preview-error'))) { + $div.append($thumb); + } + }); + } + self._setPreviewContent(out.content); + self._setInitThumbAttr(); + cap = self.initialCaption ? self.initialCaption : out.caption; + self._setCaption(cap); + if (includeProcessed) { + $div.contents().appendTo(self.$preview); + $div.remove(); + } + } else { + self._clearPreview(); + self._initCaption(); + } + if (self.showPreview) { + self._initZoom(); + self._initSortable(); + } + self.isDuplicateError = false; + }, + _clearDefaultPreview: function () { + var self = this; + self.$preview.find('.file-default-preview').remove(); + }, + _validateDefaultPreview: function () { + var self = this; + if (!self.showPreview || $h.isEmpty(self.defaultPreviewContent)) { + return; + } + self._setPreviewContent('
    ' + self.defaultPreviewContent + '
    '); + self.$container.removeClass('file-input-new'); + self._initClickable(); + }, + _resetPreviewThumbs: function (isAjax) { + var self = this, out; + if (isAjax) { + self._clearPreview(); + self.clearFileStack(); + return; + } + if (self._hasInitialPreview()) { + out = self.previewCache.out(); + self._setPreviewContent(out.content); + self._setInitThumbAttr(); + self._setCaption(out.caption); + self._initPreviewActions(); + } else { + self._clearPreview(); + } + }, + _getLayoutTemplate: function (t) { + var self = this, template = self.layoutTemplates[t]; + if ($h.isEmpty(self.customLayoutTags)) { + return template; + } + return $h.replaceTags(template, self.customLayoutTags); + }, + _getPreviewTemplate: function (t) { + var self = this, templates = self.previewTemplates, template = templates[t] || templates.other; + if ($h.isEmpty(self.customPreviewTags)) { + return template; + } + return $h.replaceTags(template, self.customPreviewTags); + }, + _getOutData: function (formdata, jqXHR, responseData, filesData) { + var self = this; + jqXHR = jqXHR || {}; + responseData = responseData || {}; + filesData = filesData || self.fileManager.list(); + return { + formdata: formdata, + files: filesData, + filenames: self.filenames, + filescount: self.getFilesCount(), + extra: self._getExtraData(), + response: responseData, + reader: self.reader, + jqXHR: jqXHR + }; + }, + _getMsgSelected: function (n, processing) { + var self = this, strFiles = n === 1 ? self.fileSingle : self.filePlural; + return n > 0 ? self.msgSelected.replace('{n}', n).replace('{files}', strFiles) : + (processing ? self.msgProcessing : self.msgNoFilesSelected); + }, + _getFrame: function (id, skipWarning) { + var self = this, $frame = $h.getFrameElement(self.$preview, id); + if (self.showPreview && !skipWarning && !$frame.length) { + self._log($h.logMessages.invalidThumb, {id: id}); + } + return $frame; + }, + _getZoom: function (id, selector) { + var self = this, $frame = $h.getZoomElement(self.$preview, id, selector); + if (self.showPreview && !$frame.length) { + self._log($h.logMessages.invalidThumb, {id: id}); + } + return $frame; + }, + _getThumbs: function (css) { + css = css || ''; + return this.getFrames(':not(.file-preview-initial)' + css); + }, + _getThumbId: function (fileId) { + var self = this; + return self.previewInitId + '-' + fileId; + }, + _getExtraData: function (fileId, index) { + var self = this, data = self.uploadExtraData; + if (typeof self.uploadExtraData === 'function') { + data = self.uploadExtraData(fileId, index); + } + return data; + }, + _initXhr: function (xhrobj, fileId) { + var self = this, fm = self.fileManager, func = function (event) { + var pct = 0, total = event.total, loaded = event.loaded || event.position, + stats = fm.getUploadStats(fileId, loaded, total); + /** @namespace event.lengthComputable */ + if (event.lengthComputable && !self.enableResumableUpload) { + pct = $h.round(loaded / total * 100); + } + if (fileId) { + self._setFileUploadStats(fileId, pct, stats); + } else { + self._setProgress(pct, null, null, self._getStats(stats)); + } + self._raise('fileajaxprogress', [stats]); + }; + if (xhrobj.upload) { + if (self.progressDelay) { + func = $h.debounce(func, self.progressDelay); + } + xhrobj.upload.addEventListener('progress', func, false); + } + return xhrobj; + }, + _initAjaxSettings: function () { + var self = this; + self._ajaxSettings = $.extend(true, {}, self.ajaxSettings); + self._ajaxDeleteSettings = $.extend(true, {}, self.ajaxDeleteSettings); + }, + _mergeAjaxCallback: function (funcName, srcFunc, type) { + var self = this, settings = self._ajaxSettings, flag = self.mergeAjaxCallbacks, targFunc; + if (type === 'delete') { + settings = self._ajaxDeleteSettings; + flag = self.mergeAjaxDeleteCallbacks; + } + targFunc = settings[funcName]; + if (flag && typeof targFunc === 'function') { + if (flag === 'before') { + settings[funcName] = function () { + targFunc.apply(this, arguments); + srcFunc.apply(this, arguments); + }; + } else { + settings[funcName] = function () { + srcFunc.apply(this, arguments); + targFunc.apply(this, arguments); + }; + } + } else { + settings[funcName] = srcFunc; + } + }, + _ajaxSubmit: function (fnBefore, fnSuccess, fnComplete, fnError, formdata, fileId, index, vUrl) { + var self = this, settings, defaults, data, ajaxTask; + if (!self._raise('filepreajax', [formdata, fileId, index])) { + return; + } + formdata.append('initialPreview', JSON.stringify(self.initialPreview)); + formdata.append('initialPreviewConfig', JSON.stringify(self.initialPreviewConfig)); + formdata.append('initialPreviewThumbTags', JSON.stringify(self.initialPreviewThumbTags)); + self._initAjaxSettings(); + self._mergeAjaxCallback('beforeSend', fnBefore); + self._mergeAjaxCallback('success', fnSuccess); + self._mergeAjaxCallback('complete', fnComplete); + self._mergeAjaxCallback('error', fnError); + vUrl = vUrl || self.uploadUrlThumb || self.uploadUrl; + if (typeof vUrl === 'function') { + vUrl = vUrl(); + } + data = self._getExtraData(fileId, index) || {}; + if (typeof data === 'object') { + $.each(data, function (key, value) { + formdata.append(key, value); + }); + } + defaults = { + xhr: function () { + var xhrobj = $.ajaxSettings.xhr(); + return self._initXhr(xhrobj, fileId); + }, + url: self._encodeURI(vUrl), + type: 'POST', + dataType: 'json', + data: formdata, + cache: false, + processData: false, + contentType: false + }; + settings = $.extend(true, {}, defaults, self._ajaxSettings); + ajaxTask = self.taskManager.addTask(fileId + '-' + index, function () { + var self = this.self, config, xhr; + config = self.ajaxQueue.shift(); + xhr = $.ajax(config); + self.ajaxRequests.push(xhr); + }); + self.ajaxQueue.push(settings); + ajaxTask.runWithContext({self: self}); + }, + _mergeArray: function (prop, content) { + var self = this, arr1 = $h.cleanArray(self[prop]), arr2 = $h.cleanArray(content); + self[prop] = arr1.concat(arr2); + }, + _initUploadSuccess: function (out, $thumb, allFiles) { + var self = this, append, data, index, $div, content, config, tags, id, i; + if (!self.showPreview || typeof out !== 'object' || $.isEmptyObject(out)) { + self._resetCaption(); + return; + } + if (out.initialPreview !== undefined && out.initialPreview.length > 0) { + self.hasInitData = true; + content = out.initialPreview || []; + config = out.initialPreviewConfig || []; + tags = out.initialPreviewThumbTags || []; + append = out.append === undefined || out.append; + if (content.length > 0 && !$h.isArray(content)) { + content = content.split(self.initialPreviewDelimiter); + } + if (content.length) { + self._mergeArray('initialPreview', content); + self._mergeArray('initialPreviewConfig', config); + self._mergeArray('initialPreviewThumbTags', tags); + } + if ($thumb !== undefined) { + if (!allFiles) { + index = self.previewCache.add(content[0], config[0], tags[0], append); + data = self.previewCache.get(index, false); + $div = $h.createElement(data).hide().appendTo($thumb); + $thumb.fadeOut('slow', function () { + var $newThumb = $div.find('> .file-preview-frame'); + if ($newThumb && $newThumb.length) { + $newThumb.insertBefore($thumb).fadeIn('slow').css('display:inline-block'); + } + self._initPreviewActions(); + self._clearFileInput(); + $thumb.remove(); + $div.remove(); + self._initSortable(); + }); + } else { + id = $thumb.attr('id'); + i = self._getUploadCacheIndex(id); + if (i !== null) { + self.uploadCache[i] = { + id: id, + content: content[0], + config: config[0] || [], + tags: tags[0] || [], + append: append + }; + } + } + } else { + self.previewCache.set(content, config, tags, append); + self._initPreview(); + self._initPreviewActions(); + } + } + self._resetCaption(); + }, + _getUploadCacheIndex: function (id) { + var self = this, i, len = self.uploadCache.length, config; + for (i = 0; i < len; i++) { + config = self.uploadCache[i]; + if (config.id === id) { + return i; + } + } + return null; + }, + _initSuccessThumbs: function () { + var self = this; + if (!self.showPreview) { + return; + } + setTimeout(function () { + self._getThumbs($h.FRAMES + '.file-preview-success').each(function () { + var $thumb = $(this), $remove = $thumb.find('.kv-file-remove'); + $remove.removeAttr('disabled'); + self._handler($remove, 'click', function () { + var id = $thumb.attr('id'), + out = self._raise('filesuccessremove', [id, $thumb.attr('data-fileindex')]); + $h.cleanMemory($thumb); + if (out === false) { + return; + } + self.$caption.attr('title', ''); + $thumb.fadeOut('slow', function () { + var fm = self.fileManager; + $thumb.remove(); + if (!self.getFrames().length) { + self.reset(); + } + }); + }); + }); + }, self.processDelay); + }, + _updateInitialPreview: function () { + var self = this, u = self.uploadCache; + if (self.showPreview) { + $.each(u, function (key, setting) { + self.previewCache.add(setting.content, setting.config, setting.tags, setting.append); + }); + if (self.hasInitData) { + self._initPreview(); + self._initPreviewActions(); + } + } + }, + _getThumbFileId: function ($thumb) { + var self = this; + if (self.showPreview && $thumb !== undefined) { + return $thumb.attr('data-fileid'); + } + return null; + }, + _getThumbFile: function ($thumb) { + var self = this, id = self._getThumbFileId($thumb); + return id ? self.fileManager.getFile(id) : null; + }, + _uploadSingle: function (i, id, isBatch) { + var self = this, fm = self.fileManager, count = fm.count(), formdata = new FormData(), outData, + previewId = self._getThumbId(id), $thumb, chkComplete, $btnUpload, $btnDelete, + hasPostData = count > 0 || !$.isEmptyObject(self.uploadExtraData), uploadFailed, $prog, fnBefore, + errMsg, fnSuccess, fnComplete, fnError, updateUploadLog, op = self.ajaxOperations.uploadThumb, + fileObj = fm.getFile(id), params = {id: previewId, index: i, fileId: id}, + fileName = self.fileManager.getFileName(id, true); + if (self.enableResumableUpload) { // not enabled for resumable uploads + return; + } + if (self.showPreview) { + $thumb = fm.getThumb(id); + $prog = $thumb.find('.file-thumb-progress'); + $btnUpload = $thumb.find('.kv-file-upload'); + $btnDelete = $thumb.find('.kv-file-remove'); + $prog.show(); + } + if (count === 0 || !hasPostData || (self.showPreview && $btnUpload && $btnUpload.hasClass('disabled')) || + self._abort(params)) { + return; + } + updateUploadLog = function () { + if (!uploadFailed) { + fm.removeFile(id); + } else { + fm.errors.push(id); + } + fm.setProcessed(id); + if (fm.isProcessed()) { + self.fileBatchCompleted = true; + chkComplete(); + } + }; + chkComplete = function () { + var $initThumbs; + if (!self.fileBatchCompleted) { + return; + } + setTimeout(function () { + var triggerReset = fm.count() === 0, errCount = fm.errors.length; + self._updateInitialPreview(); + self.unlock(triggerReset); + if (triggerReset) { + self._clearFileInput(); + } + $initThumbs = self.$preview.find('.file-preview-initial'); + if (self.uploadAsync && $initThumbs.length) { + $h.addCss($initThumbs, $h.SORT_CSS); + self._initSortable(); + } + self._raise('filebatchuploadcomplete', [fm.stack, self._getExtraData()]); + if (!self.retryErrorUploads || errCount === 0) { + fm.clear(); + } + self._setProgress(101); + self.ajaxAborted = false; + }, self.processDelay); + }; + fnBefore = function (jqXHR) { + outData = self._getOutData(formdata, jqXHR); + fm.initStats(id); + self.fileBatchCompleted = false; + if (!isBatch) { + self.ajaxAborted = false; + } + if (self.showPreview) { + if (!$thumb.hasClass('file-preview-success')) { + self._setThumbStatus($thumb, 'Loading'); + $h.addCss($thumb, 'file-uploading'); + } + $btnUpload.attr('disabled', true); + $btnDelete.attr('disabled', true); + } + if (!isBatch) { + self.lock(); + } + if (fm.errors.indexOf(id) !== -1) { + delete fm.errors[id]; + } + self._raise('filepreupload', [outData, previewId, i, self._getThumbFileId($thumb)]); + $.extend(true, params, outData); + if (self._abort(params)) { + jqXHR.abort(); + if (!isBatch) { + self._setThumbStatus($thumb, 'New'); + $thumb.removeClass('file-uploading'); + $btnUpload.removeAttr('disabled'); + $btnDelete.removeAttr('disabled'); + } + self._setProgressCancelled(); + } + }; + fnSuccess = function (data, textStatus, jqXHR) { + var pid = self.showPreview && $thumb.attr('id') ? $thumb.attr('id') : previewId; + outData = self._getOutData(formdata, jqXHR, data); + $.extend(true, params, outData); + setTimeout(function () { + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { + if (self.showPreview) { + self._setThumbStatus($thumb, 'Success'); + $btnUpload.hide(); + self._initUploadSuccess(data, $thumb, isBatch); + self._setProgress(101, $prog); + } + self._raise('fileuploaded', [outData, pid, i, self._getThumbFileId($thumb)]); + if (!isBatch) { + self.fileManager.remove($thumb); + } else { + updateUploadLog(); + } + } else { + uploadFailed = true; + errMsg = self._parseError(op, jqXHR, self.msgUploadError, self.fileManager.getFileName(id)); + self._showFileError(errMsg, params); + self._setPreviewError($thumb, true); + if (!self.retryErrorUploads) { + $btnUpload.hide(); + } + if (isBatch) { + updateUploadLog(); + } + self._setProgress(101, self._getFrame(pid).find('.file-thumb-progress'), + self.msgUploadError); + } + }, self.processDelay); + }; + fnComplete = function () { + if (self.showPreview) { + $btnUpload.removeAttr('disabled'); + $btnDelete.removeAttr('disabled'); + $thumb.removeClass('file-uploading'); + } + if (!isBatch) { + self.unlock(false); + self._clearFileInput(); + } else { + chkComplete(); + } + self._initSuccessThumbs(); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + errMsg = self._parseError(op, jqXHR, errorThrown, self.fileManager.getFileName(id)); + uploadFailed = true; + setTimeout(function () { + var $prog; + if (isBatch) { + updateUploadLog(); + } + self.fileManager.setProgress(id, 100); + self._setPreviewError($thumb, true); + if (!self.retryErrorUploads) { + $btnUpload.hide(); + } + $.extend(true, params, self._getOutData(formdata, jqXHR)); + self._setProgress(101, self.$progress, self.msgAjaxProgressError.replace('{operation}', op)); + $prog = self.showPreview && $thumb ? $thumb.find('.file-thumb-progress') : ''; + self._setProgress(101, $prog, self.msgUploadError); + self._showFileError(errMsg, params); + }, self.processDelay); + }; + self._setFileData(formdata, fileObj.file, fileName, id); + self._setUploadData(formdata, {fileId: id}); + self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, formdata, id, i); + }, + _setFileData: function (formdata, file, fileName, fileId) { + var self = this, preProcess = self.preProcessUpload; + if (preProcess && typeof preProcess === 'function') { + formdata.append(self.uploadFileAttr, preProcess(fileId, file)); + } else { + formdata.append(self.uploadFileAttr, file, fileName); + } + }, + _checkBatchPreupload: function (outData, jqXHR) { + var self = this, out = self._raise('filebatchpreupload', [outData]); + if (out) { + return true; + } + self._abort(outData); + if (jqXHR) { + jqXHR.abort(); + } + self._getThumbs().each(function () { + var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), + $btnDelete = $thumb.find('.kv-file-remove'); + if ($thumb.hasClass('file-preview-loading')) { + self._setThumbStatus($thumb, 'New'); + $thumb.removeClass('file-uploading'); + } + $btnUpload.removeAttr('disabled'); + $btnDelete.removeAttr('disabled'); + }); + self._setProgressCancelled(); + return false; + }, + _uploadBatch: function () { + var self = this, fm = self.fileManager, total = fm.total(), params = {}, fnBefore, fnSuccess, fnError, + fnComplete, hasPostData = total > 0 || !$.isEmptyObject(self.uploadExtraData), errMsg, + setAllUploaded, formdata = new FormData(), op = self.ajaxOperations.uploadBatch; + if (total === 0 || !hasPostData || self._abort(params)) { + return; + } + setAllUploaded = function () { + self.fileManager.clear(); + self._clearFileInput(); + }; + fnBefore = function (jqXHR) { + self.lock(); + fm.initStats(); + var outData = self._getOutData(formdata, jqXHR); + self.ajaxAborted = false; + if (self.showPreview) { + self._getThumbs().each(function () { + var $thumb = $(this), $btnUpload = $thumb.find('.kv-file-upload'), + $btnDelete = $thumb.find('.kv-file-remove'); + if (!$thumb.hasClass('file-preview-success')) { + self._setThumbStatus($thumb, 'Loading'); + $h.addCss($thumb, 'file-uploading'); + } + $btnUpload.attr('disabled', true); + $btnDelete.attr('disabled', true); + }); + } + self._checkBatchPreupload(outData, jqXHR); + }; + fnSuccess = function (data, textStatus, jqXHR) { + /** @namespace data.errorkeys */ + var outData = self._getOutData(formdata, jqXHR, data), key = 0, + $thumbs = self._getThumbs(':not(.file-preview-success)'), + keys = $h.isEmpty(data) || $h.isEmpty(data.errorkeys) ? [] : data.errorkeys; + + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { + self._raise('filebatchuploadsuccess', [outData]); + setAllUploaded(); + if (self.showPreview) { + $thumbs.each(function () { + var $thumb = $(this); + self._setThumbStatus($thumb, 'Success'); + $thumb.removeClass('file-uploading'); + $thumb.find('.kv-file-upload').hide().removeAttr('disabled'); + }); + self._initUploadSuccess(data); + } else { + self.reset(); + } + self._setProgress(101); + } else { + if (self.showPreview) { + $thumbs.each(function () { + var $thumb = $(this); + $thumb.removeClass('file-uploading'); + $thumb.find('.kv-file-upload').removeAttr('disabled'); + $thumb.find('.kv-file-remove').removeAttr('disabled'); + if (keys.length === 0 || $.inArray(key, keys) !== -1) { + self._setPreviewError($thumb, true); + if (!self.retryErrorUploads) { + $thumb.find('.kv-file-upload').hide(); + self.fileManager.remove($thumb); + } + } else { + $thumb.find('.kv-file-upload').hide(); + self._setThumbStatus($thumb, 'Success'); + self.fileManager.remove($thumb); + } + if (!$thumb.hasClass('file-preview-error') || self.retryErrorUploads) { + key++; + } + }); + self._initUploadSuccess(data); + } + errMsg = self._parseError(op, jqXHR, self.msgUploadError); + self._showFileError(errMsg, outData, 'filebatchuploaderror'); + self._setProgress(101, self.$progress, self.msgUploadError); + } + }; + fnComplete = function () { + self.unlock(); + self._initSuccessThumbs(); + self._clearFileInput(); + self._raise('filebatchuploadcomplete', [self.fileManager.stack, self._getExtraData()]); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + var outData = self._getOutData(formdata, jqXHR); + errMsg = self._parseError(op, jqXHR, errorThrown); + self._showFileError(errMsg, outData, 'filebatchuploaderror'); + self.uploadFileCount = total - 1; + if (!self.showPreview) { + return; + } + self._getThumbs().each(function () { + var $thumb = $(this); + $thumb.removeClass('file-uploading'); + if (self._getThumbFile($thumb)) { + self._setPreviewError($thumb); + } + }); + self._getThumbs().removeClass('file-uploading'); + self._getThumbs(' .kv-file-upload').removeAttr('disabled'); + self._getThumbs(' .kv-file-delete').removeAttr('disabled'); + self._setProgress(101, self.$progress, self.msgAjaxProgressError.replace('{operation}', op)); + }; + var ctr = 0; + $.each(self.fileManager.stack, function (key, data) { + if (!$h.isEmpty(data.file)) { + self._setFileData(formdata, data.file, (data.nameFmt || ('untitled_' + ctr)), key); + } + ctr++; + }); + self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, formdata); + }, + _uploadExtraOnly: function () { + var self = this, params = {}, fnBefore, fnSuccess, fnComplete, fnError, formdata = new FormData(), errMsg, + op = self.ajaxOperations.uploadExtra; + fnBefore = function (jqXHR) { + self.lock(); + var outData = self._getOutData(formdata, jqXHR); + self._setProgress(50); + params.data = outData; + params.xhr = jqXHR; + self._checkBatchPreupload(outData, jqXHR); + }; + fnSuccess = function (data, textStatus, jqXHR) { + var outData = self._getOutData(formdata, jqXHR, data); + if ($h.isEmpty(data) || $h.isEmpty(data.error)) { + self._raise('filebatchuploadsuccess', [outData]); + self._clearFileInput(); + self._initUploadSuccess(data); + self._setProgress(101); + } else { + errMsg = self._parseError(op, jqXHR, self.msgUploadError); + self._showFileError(errMsg, outData, 'filebatchuploaderror'); + } + }; + fnComplete = function () { + self.unlock(); + self._clearFileInput(); + self._raise('filebatchuploadcomplete', [self.fileManager.stack, self._getExtraData()]); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + var outData = self._getOutData(formdata, jqXHR); + errMsg = self._parseError(op, jqXHR, errorThrown); + params.data = outData; + self._showFileError(errMsg, outData, 'filebatchuploaderror'); + self._setProgress(101, self.$progress, self.msgAjaxProgressError.replace('{operation}', op)); + }; + self._ajaxSubmit(fnBefore, fnSuccess, fnComplete, fnError, formdata); + }, + _deleteFileIndex: function ($frame) { + var self = this, ind = $frame.attr('data-fileindex'), rev = self.reversePreviewOrder; + if (ind.substring(0, 5) === $h.INIT_FLAG) { + ind = parseInt(ind.replace($h.INIT_FLAG, '')); + self.initialPreview = $h.spliceArray(self.initialPreview, ind, rev); + self.initialPreviewConfig = $h.spliceArray(self.initialPreviewConfig, ind, rev); + self.initialPreviewThumbTags = $h.spliceArray(self.initialPreviewThumbTags, ind, rev); + self.getFrames().each(function () { + var $nFrame = $(this), nInd = $nFrame.attr('data-fileindex'); + if (nInd.substring(0, 5) === $h.INIT_FLAG) { + nInd = parseInt(nInd.replace($h.INIT_FLAG, '')); + if (nInd > ind) { + nInd--; + $nFrame.attr('data-fileindex', $h.INIT_FLAG + nInd); + } + } + }); + } + }, + _resetCaption: function () { + var self = this; + setTimeout(function () { + var cap = '', n, chk = self.previewCache.count(true), len = self.fileManager.count(), file, + incomplete = ':not(.file-preview-success):not(.file-preview-error)', cfg, + hasThumb = self.showPreview && self.getFrames(incomplete).length; + if (len === 0 && chk === 0 && !hasThumb) { + self.reset(); + } else { + n = chk + len; + if (n > 1) { + cap = self._getMsgSelected(n); + } else { + if (len === 0) { + cfg = self.initialPreviewConfig[0]; + cap = ''; + if (cfg) { + cap = cfg.caption || cfg.filename || '' + } + if (!cap) { + cap = self._getMsgSelected(n); + } + } else { + file = self.fileManager.getFirstFile(); + cap = file ? file.nameFmt : '_'; + } + } + self._setCaption(cap); + } + }, self.processDelay); + }, + _initFileActions: function () { + var self = this; + if (!self.showPreview) { + return; + } + self._initZoomButton(); + self.getFrames(' .kv-file-remove').each(function () { + var $el = $(this), $frame = $el.closest($h.FRAMES), hasError, id = $frame.attr('id'), + ind = $frame.attr('data-fileindex'), status, fm = self.fileManager; + self._handler($el, 'click', function () { + status = self._raise('filepreremove', [id, ind]); + if (status === false || !self._validateMinCount()) { + return false; + } + hasError = $frame.hasClass('file-preview-error'); + $h.cleanMemory($frame); + $frame.fadeOut('slow', function () { + self.fileManager.remove($frame); + self._clearObjects($frame); + $frame.remove(); + if (id && hasError) { + self.$errorContainer.find('li[data-thumb-id="' + id + '"]').fadeOut('fast', function () { + $(this).remove(); + if (!self._errorsExist()) { + self._resetErrors(); + } + }); + } + self._clearFileInput(); + self._resetCaption(); + self._raise('fileremoved', [id, ind]); + }); + }); + }); + self.getFrames(' .kv-file-upload').each(function () { + var $el = $(this); + self._handler($el, 'click', function () { + var $frame = $el.closest($h.FRAMES), fileId = self._getThumbFileId($frame); + self._hideProgress(); + if ($frame.hasClass('file-preview-error') && !self.retryErrorUploads) { + return; + } + self._uploadSingle(self.fileManager.getIndex(fileId), fileId, false); + }); + }); + }, + _initPreviewActions: function () { + var self = this, $preview = self.$preview, deleteExtraData = self.deleteExtraData || {}, + btnRemove = $h.FRAMES + ' .kv-file-remove', settings = self.fileActionSettings, + origClass = settings.removeClass, errClass = settings.removeErrorClass, + resetProgress = function () { + var hasFiles = self.isAjaxUpload ? self.previewCache.count(true) : self._inputFileCount(); + if (!self.getFrames().length && !hasFiles) { + self._setCaption(''); + self.reset(); + self.initialCaption = ''; + } else { + self._resetCaption(); + } + }; + self._initZoomButton(); + $preview.find(btnRemove).each(function () { + var $el = $(this), vUrl = $el.data('url') || self.deleteUrl, vKey = $el.data('key'), errMsg, fnBefore, + fnSuccess, fnError, op = self.ajaxOperations.deleteThumb; + if ($h.isEmpty(vUrl) || vKey === undefined) { + return; + } + if (typeof vUrl === 'function') { + vUrl = vUrl(); + } + var $frame = $el.closest($h.FRAMES), cache = self.previewCache.data, settings, params, config, + fileName, extraData, index = $frame.attr('data-fileindex'); + index = parseInt(index.replace($h.INIT_FLAG, '')); + config = $h.isEmpty(cache.config) && $h.isEmpty(cache.config[index]) ? null : cache.config[index]; + extraData = $h.isEmpty(config) || $h.isEmpty(config.extra) ? deleteExtraData : config.extra; + fileName = config && (config.filename || config.caption) || ''; + if (typeof extraData === 'function') { + extraData = extraData(); + } + params = {id: $el.attr('id'), key: vKey, extra: extraData}; + fnBefore = function (jqXHR) { + self.ajaxAborted = false; + self._raise('filepredelete', [vKey, jqXHR, extraData]); + if (self._abort()) { + jqXHR.abort(); + } else { + $el.removeClass(errClass); + $h.addCss($frame, 'file-uploading'); + $h.addCss($el, 'disabled ' + origClass); + } + }; + fnSuccess = function (data, textStatus, jqXHR) { + var n, cap; + if (!$h.isEmpty(data) && !$h.isEmpty(data.error)) { + params.jqXHR = jqXHR; + params.response = data; + errMsg = self._parseError(op, jqXHR, self.msgDeleteError, fileName); + self._showFileError(errMsg, params, 'filedeleteerror'); + $frame.removeClass('file-uploading'); + $el.removeClass('disabled ' + origClass).addClass(errClass); + resetProgress(); + return; + } + $frame.removeClass('file-uploading').addClass('file-deleted'); + $frame.fadeOut('slow', function () { + index = parseInt(($frame.attr('data-fileindex')).replace($h.INIT_FLAG, '')); + self.previewCache.unset(index); + self._deleteFileIndex($frame); + n = self.previewCache.count(true); + cap = n > 0 ? self._getMsgSelected(n) : ''; + self._setCaption(cap); + self._raise('filedeleted', [vKey, jqXHR, extraData]); + self._clearObjects($frame); + $frame.remove(); + resetProgress(); + }); + }; + fnError = function (jqXHR, textStatus, errorThrown) { + var errMsg = self._parseError(op, jqXHR, errorThrown, fileName); + params.jqXHR = jqXHR; + params.response = {}; + self._showFileError(errMsg, params, 'filedeleteerror'); + $frame.removeClass('file-uploading'); + $el.removeClass('disabled ' + origClass).addClass(errClass); + resetProgress(); + }; + self._initAjaxSettings(); + self._mergeAjaxCallback('beforeSend', fnBefore, 'delete'); + self._mergeAjaxCallback('success', fnSuccess, 'delete'); + self._mergeAjaxCallback('error', fnError, 'delete'); + settings = $.extend(true, {}, { + url: self._encodeURI(vUrl), + type: 'POST', + dataType: 'json', + data: $.extend(true, {}, {key: vKey}, extraData) + }, self._ajaxDeleteSettings); + self._handler($el, 'click', function () { + if (!self._validateMinCount()) { + return false; + } + self.ajaxAborted = false; + self._raise('filebeforedelete', [vKey, extraData]); + if (self.ajaxAborted instanceof Promise) { + self.ajaxAborted.then(function (result) { + if (!result) { + $.ajax(settings); + } + }); + } else { + if (!self.ajaxAborted) { + $.ajax(settings); + } + } + }); + }); + }, + _hideFileIcon: function () { + var self = this; + if (self.overwriteInitial) { + self.$captionContainer.removeClass('icon-visible'); + } + }, + _showFileIcon: function () { + var self = this; + $h.addCss(self.$captionContainer, 'icon-visible'); + }, + _getSize: function (bytes, sizes) { + var self = this, size = parseFloat(bytes), i, func = self.fileSizeGetter, out; + if (!$.isNumeric(bytes) || !$.isNumeric(size)) { + return ''; + } + if (typeof func === 'function') { + out = func(size); + } else { + if (size === 0) { + out = '0.00 B'; + } else { + if (!sizes) { + sizes = self.sizeUnits; + } + i = Math.floor(Math.log(size) / Math.log(self.bytesToKB)); + out = (size / Math.pow(self.bytesToKB, i)).toFixed(2) + ' ' + sizes[i]; + } + } + return self._getLayoutTemplate('size').replace('{sizeText}', out); + }, + _getFileType: function (ftype) { + var self = this; + return self.mimeTypeAliases[ftype] || ftype; + }, + _generatePreviewTemplate: function ( + cat, + data, + fname, + ftype, + previewId, + fileId, + isError, + size, + frameClass, + foot, + ind, + templ, + attrs, + zoomData + ) { + var self = this, caption = self.slug(fname), prevContent, zoomContent = '', styleAttribs = '', + screenW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth, + config, title = caption, alt = caption, typeCss = 'type-default', getContent, + footer = foot || self._renderFileFooter(cat, caption, size, 'auto', isError), + forcePrevIcon = self.preferIconicPreview, forceZoomIcon = self.preferIconicZoomPreview, + newCat = forcePrevIcon ? 'other' : cat; + config = screenW < 400 ? (self.previewSettingsSmall[newCat] || self.defaults.previewSettingsSmall[newCat]) : + (self.previewSettings[newCat] || self.defaults.previewSettings[newCat]); + if (config) { + $.each(config, function (key, val) { + styleAttribs += key + ':' + val + ';'; + }); + } + getContent = function (vCat, vData, zoom, frameCss, vZoomData) { + var id = zoom ? 'zoom-' + previewId : previewId, tmplt = self._getPreviewTemplate(vCat), + css = (frameClass || '') + ' ' + frameCss, tokens; + if (self.frameClass) { + css = self.frameClass + ' ' + css; + } + if (zoom) { + css = css.replace(' ' + $h.SORT_CSS, ''); + } + tmplt = self._parseFilePreviewIcon(tmplt, fname); + if (cat === 'object' && !ftype) { + $.each(self.defaults.fileTypeSettings, function (key, func) { + if (key === 'object' || key === 'other') { + return; + } + if (func(fname, ftype)) { + typeCss = 'type-' + key; + } + }); + } + if (!$h.isEmpty(attrs)) { + if (attrs.title !== undefined && attrs.title !== null) { + title = attrs.title; + } + if (attrs.alt !== undefined && attrs.alt !== null) { + title = attrs.alt; + } + } + tokens = { + 'previewId': id, + 'caption': caption, + 'title': title, + 'alt': alt, + 'frameClass': css, + 'type': self._getFileType(ftype), + 'fileindex': ind, + 'fileid': fileId || '', + 'typeCss': typeCss, + 'footer': footer, + 'data': zoom && vZoomData ? '{zoomData}' : vData, + 'template': templ || cat, + 'style': styleAttribs ? 'style="' + styleAttribs + '"' : '', + 'zoomData': vZoomData ? encodeURIComponent(vZoomData) : '' + }; + if (zoom) { + tokens.zoomCache = ''; + tokens.zoomData = '{zoomData}'; + } + return tmplt.setTokens(tokens); + }; + ind = ind || previewId.slice(previewId.lastIndexOf('-') + 1); + if (self.fileActionSettings.showZoom) { + zoomContent = getContent((forceZoomIcon ? 'other' : cat), data, true, 'kv-zoom-thumb', zoomData); + } + zoomContent = '\n' + self._getLayoutTemplate('zoomCache').replace('{zoomContent}', zoomContent); + if (typeof self.sanitizeZoomCache === 'function') { + zoomContent = self.sanitizeZoomCache(zoomContent); + } + prevContent = getContent((forcePrevIcon ? 'other' : cat), data, false, 'kv-preview-thumb', zoomData); + return prevContent.setTokens({zoomCache: zoomContent}); + }, + _addToPreview: function ($preview, content) { + var self = this, $el; + content = $h.cspBuffer.stash(content); + $el = self.reversePreviewOrder ? $preview.prepend(content) : $preview.append(content); + $h.cspBuffer.apply($preview); + return $el; + }, + _previewDefault: function (file, isDisabled) { + var self = this, $preview = self.$preview; + if (!self.showPreview) { + return; + } + var fname = $h.getFileName(file), ftype = file ? file.type : '', content, size = file.size || 0, + caption = self._getFileName(file, ''), isError = isDisabled === true && !self.isAjaxUpload, + data = $h.createObjectURL(file), fileId = self.fileManager.getId(file), + previewId = self._getThumbId(fileId); + self._clearDefaultPreview(); + content = self._generatePreviewTemplate('other', data, fname, ftype, previewId, fileId, isError, size); + self._addToPreview($preview, content); + self._setThumbAttr(previewId, caption, size); + if (isDisabled === true && self.isAjaxUpload) { + self._setThumbStatus(self._getFrame(previewId), 'Error'); + } + }, + _previewFile: function (i, file, theFile, data, fileInfo) { + if (!this.showPreview) { + return; + } + var self = this, fname = $h.getFileName(file), ftype = fileInfo.type, caption = fileInfo.name, + cat = self._parseFileType(ftype, fname), content, $preview = self.$preview, fsize = file.size || 0, + iData = cat === 'image' ? theFile.target.result : data, fm = self.fileManager, + fileId = fm.getId(file), previewId = self._getThumbId(fileId); + /** @namespace window.DOMPurify */ + content = self._generatePreviewTemplate(cat, iData, fname, ftype, previewId, fileId, false, fsize); + self._clearDefaultPreview(); + self._addToPreview($preview, content); + var $thumb = self._getFrame(previewId); + self._validateImageOrientation($thumb.find('img'), file, previewId, fileId, caption, ftype, fsize, iData); + self._setThumbAttr(previewId, caption, fsize); + self._initSortable(); + }, + _setThumbAttr: function (id, caption, size, description) { + var self = this, $frame = self._getFrame(id); + if ($frame.length) { + size = size && size > 0 ? self._getSize(size) : ''; + $frame.data({'caption': caption, 'size': size, 'description': description || ''}); + } + }, + _setInitThumbAttr: function () { + var self = this, data = self.previewCache.data, len = self.previewCache.count(true), config, + caption, size, description, previewId; + if (len === 0) { + return; + } + for (var i = 0; i < len; i++) { + config = data.config[i]; + previewId = self.previewInitId + '-' + $h.INIT_FLAG + i; + caption = $h.ifSet('caption', config, $h.ifSet('filename', config)); + size = $h.ifSet('size', config); + description = $h.ifSet('description', config); + self._setThumbAttr(previewId, caption, size, description); + } + }, + _slugDefault: function (text) { + // noinspection RegExpRedundantEscape + return $h.isEmpty(text, true) ? '' : String(text).replace(/[\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_'); + }, + _updateFileDetails: function (numFiles) { + var self = this, $el = self.$element, label, n, log, nFiles, file, + name = ($h.isIE(9) && $h.findFileName($el.val())) || ($el[0].files[0] && $el[0].files[0].name); + if (!name && self.fileManager.count() > 0) { + file = self.fileManager.getFirstFile(); + label = file.nameFmt; + } else { + label = name ? self.slug(name) : '_'; + } + n = self.isAjaxUpload ? self.fileManager.count() : numFiles; + nFiles = self.previewCache.count(true) + n; + log = n === 1 ? label : self._getMsgSelected(nFiles, !self.isAjaxUpload && !self.isError); + if (self.isError) { + self.$previewContainer.removeClass('file-thumb-loading'); + self._initCapStatus(); + self.$previewStatus.html(''); + self.$captionContainer.removeClass('icon-visible'); + } else { + self._showFileIcon(); + } + self._setCaption(log, self.isError); + self.$container.removeClass('file-input-new file-input-ajax-new'); + self._raise('fileselect', [numFiles, label]); + if (self.previewCache.count(true)) { + self._initPreviewActions(); + } + }, + _setThumbStatus: function ($thumb, status) { + var self = this; + if (!self.showPreview) { + return; + } + var icon = 'indicator' + status, msg = icon + 'Title', + css = 'file-preview-' + status.toLowerCase(), + $indicator = $thumb.find('.file-upload-indicator'), + config = self.fileActionSettings; + $thumb.removeClass('file-preview-success file-preview-error file-preview-paused file-preview-loading'); + if (status === 'Success') { + $thumb.find('.file-drag-handle').remove(); + } + $h.setHtml($indicator, config[icon]); + $indicator.attr('title', config[msg]); + $thumb.addClass(css); + if (status === 'Error' && !self.retryErrorUploads) { + $thumb.find('.kv-file-upload').attr('disabled', true); + } + }, + _setProgressCancelled: function () { + var self = this; + self._setProgress(101, self.$progress, self.msgCancelled); + }, + _setProgress: function (p, $el, error, stats) { + var self = this; + $el = $el || self.$progress; + if (!$el.length) { + return; + } + var pct = Math.min(p, 100), out, pctLimit = self.progressUploadThreshold, + t = p <= 100 ? self.progressTemplate : self.progressCompleteTemplate, + template = pct < 100 ? self.progressTemplate : + (error ? (self.paused ? self.progressPauseTemplate : self.progressErrorTemplate) : t); + if (p >= 100) { + stats = ''; + } + if (!$h.isEmpty(template)) { + if (pctLimit && pct > pctLimit && p <= 100) { + out = template.setTokens({'percent': pctLimit, 'status': self.msgUploadThreshold}); + } else { + out = template.setTokens({'percent': pct, 'status': (p > 100 ? self.msgUploadEnd : pct + '%')}); + } + stats = stats || ''; + out = out.setTokens({stats: stats}); + $h.setHtml($el, out); + if (error) { + $h.setHtml($el.find('[role="progressbar"]'), error); + } + } + }, + _hasFiles: function () { + var el = this.$element[0]; + return !!(el && el.files && el.files.length); + }, + _setFileDropZoneTitle: function () { + var self = this, $zone = self.$container.find('.file-drop-zone'), title = self.dropZoneTitle, strFiles; + if (self.isClickable) { + strFiles = $h.isEmpty(self.$element.attr('multiple')) ? self.fileSingle : self.filePlural; + title += self.dropZoneClickTitle.replace('{files}', strFiles); + } + $zone.find('.' + self.dropZoneTitleClass).remove(); + if (!self.showPreview || $zone.length === 0 || self.fileManager.count() > 0 || !self.dropZoneEnabled || + self.previewCache.count() > 0 || (!self.isAjaxUpload && self._hasFiles())) { + return; + } + if ($zone.find($h.FRAMES).length === 0 && $h.isEmpty(self.defaultPreviewContent)) { + $zone.prepend('
    ' + title + '
    '); + } + self.$container.removeClass('file-input-new'); + $h.addCss(self.$container, 'file-input-ajax-new'); + }, + _getStats: function (stats) { + var self = this, pendingTime, t; + if (!self.showUploadStats || !stats || !stats.bitrate) { + return ''; + } + t = self._getLayoutTemplate('stats'); + pendingTime = (!stats.elapsed || !stats.bps) ? self.msgCalculatingTime : + self.msgPendingTime.setTokens({time: $h.getElapsed(Math.ceil(stats.pendingBytes / stats.bps))}); + + return t.setTokens({ + uploadSpeed: stats.bitrate, + pendingTime: pendingTime + }); + }, + _setResumableProgress: function (pct, stats, $thumb) { + var self = this, rm = self.resumableManager, obj = $thumb ? rm : self, + $prog = $thumb ? $thumb.find('.file-thumb-progress') : null; + if (obj.lastProgress === 0) { + obj.lastProgress = pct; + } + if (pct < obj.lastProgress) { + pct = obj.lastProgress; + } + self._setProgress(pct, $prog, null, self._getStats(stats)); + obj.lastProgress = pct; + }, + _toggleResumableProgress: function (template, message) { + var self = this, $progress = self.$progress; + if ($progress && $progress.length) { + $h.setHtml($progress, template.setTokens({ + percent: 101, + status: message, + stats: '' + })); + } + }, + _setFileUploadStats: function (id, pct, stats) { + var self = this, $prog = self.$progress; + if (!self.showPreview && (!$prog || !$prog.length)) { + return; + } + var fm = self.fileManager, rm = self.resumableManager, $thumb = fm.getThumb(id), pctTot, + totUpSize = 0, totSize = fm.getTotalSize(), totStats = $.extend(true, {}, stats); + if (self.enableResumableUpload) { + var loaded = stats.loaded, currUplSize = rm.getUploadedSize(), currTotSize = rm.file.size, totLoaded; + loaded += currUplSize; + totLoaded = fm.uploadedSize + loaded; + pct = $h.round(100 * loaded / currTotSize); + stats.pendingBytes = currTotSize - currUplSize; + self._setResumableProgress(pct, stats, $thumb); + pctTot = Math.floor(100 * totLoaded / totSize); + totStats.pendingBytes = totSize - totLoaded; + self._setResumableProgress(pctTot, totStats); + } else { + fm.setProgress(id, pct); + $prog = $thumb && $thumb.length ? $thumb.find('.file-thumb-progress') : null; + self._setProgress(pct, $prog, null, self._getStats(stats)); + $.each(fm.stats, function (id, cfg) { + totUpSize += cfg.loaded; + }); + totStats.pendingBytes = totSize - totUpSize; + pctTot = $h.round(totUpSize / totSize * 100); + self._setProgress(pctTot, null, null, self._getStats(totStats)); + } + }, + _validateMinCount: function () { + var self = this, len = self.isAjaxUpload ? self.fileManager.count() : self._inputFileCount(); + if (self.validateInitialCount && self.minFileCount > 0 && self._getFileCount(len - 1) < self.minFileCount) { + self._noFilesError({}); + return false; + } + return true; + }, + _getFileCount: function (fileCount, includeInitial) { + var self = this, addCount = 0; + if (includeInitial === undefined) { + includeInitial = self.validateInitialCount && !self.overwriteInitial; + } + if (includeInitial) { + addCount = self.previewCache.count(true); + fileCount += addCount; + } + return fileCount; + }, + _getFileId: function (file) { + return $h.getFileId(file, this.generateFileId); + }, + _getFileName: function (file, defaultValue) { + var self = this, fileName = $h.getFileName(file); + return fileName ? self.slug(fileName) : defaultValue; + }, + _getFileNames: function (skipNull) { + var self = this; + return self.filenames.filter(function (n) { + return (skipNull ? n !== undefined : n !== undefined && n !== null); + }); + }, + _setPreviewError: function ($thumb, keepFile) { + var self = this, removeFrame = self.removeFromPreviewOnError && !self.retryErrorUploads; + if (!keepFile || removeFrame) { + self.fileManager.remove($thumb); + } + if (!self.showPreview) { + return; + } + if (removeFrame) { + $thumb.remove(); + return; + } else { + self._setThumbStatus($thumb, 'Error'); + } + self._refreshUploadButton($thumb); + }, + _refreshUploadButton: function ($thumb) { + var self = this, $btn = $thumb.find('.kv-file-upload'), cfg = self.fileActionSettings, + icon = cfg.uploadIcon, title = cfg.uploadTitle; + if (!$btn.length) { + return; + } + if (self.retryErrorUploads) { + icon = cfg.uploadRetryIcon; + title = cfg.uploadRetryTitle; + } + $btn.attr('title', title); + $h.setHtml($btn, icon); + }, + _checkDimensions: function (i, chk, $img, $thumb, fname, type, params) { + var self = this, msg, dim, tag = chk === 'Small' ? 'min' : 'max', limit = self[tag + 'Image' + type], + $imgEl, isValid; + if ($h.isEmpty(limit) || !$img.length) { + return; + } + $imgEl = $img[0]; + dim = (type === 'Width') ? $imgEl.naturalWidth || $imgEl.width : $imgEl.naturalHeight || $imgEl.height; + isValid = chk === 'Small' ? dim >= limit : dim <= limit; + if (isValid) { + return; + } + msg = self['msgImage' + type + chk].setTokens({'name': fname, 'size': limit}); + self._showFileError(msg, params); + self._setPreviewError($thumb); + }, + _getExifObj: function (data) { + var self = this, exifObj, error = $h.logMessages.exifWarning; + if (data.slice(0, 23) !== 'data:image/jpeg;base64,' && data.slice(0, 22) !== 'data:image/jpg;base64,') { + exifObj = null; + return; + } + try { + exifObj = window.piexif ? window.piexif.load(data) : null; + } catch (err) { + exifObj = null; + error = err && err.message || ''; + } + if (!exifObj) { + self._log($h.logMessages.badExifParser, {details: error}); + } + return exifObj; + }, + setImageOrientation: function ($img, $zoomImg, value, $thumb) { + var self = this, invalidImg = !$img || !$img.length, invalidZoomImg = !$zoomImg || !$zoomImg.length, $mark, + isHidden = false, $div, zoomOnly = invalidImg && $thumb && $thumb.attr('data-template') === 'image', ev; + if (invalidImg && invalidZoomImg) { + return; + } + ev = 'load.fileinputimageorient'; + if (zoomOnly) { + $img = $zoomImg; + $zoomImg = null; + $img.css(self.previewSettings.image); + $div = $(document.createElement('div')).appendTo($thumb.find('.kv-file-content')); + $mark = $(document.createElement('span')).insertBefore($img); + $img.css('visibility', 'hidden').removeClass('file-zoom-detail').appendTo($div); + } else { + isHidden = !$img.is(':visible'); + } + $img.off(ev).on(ev, function () { + if (isHidden) { + self.$preview.removeClass('hide-content'); + $thumb.find('.kv-file-content').css('visibility', 'hidden'); + } + var img = $img[0], zoomImg = $zoomImg && $zoomImg.length ? $zoomImg[0] : null, + h = img.offsetHeight, w = img.offsetWidth, r = $h.getRotation(value); + if (isHidden) { + $thumb.find('.kv-file-content').css('visibility', 'visible'); + self.$preview.addClass('hide-content'); + } + $img.data('orientation', value); + if (zoomImg) { + $zoomImg.data('orientation', value); + } + if (value < 5) { + $h.setTransform(img, r); + $h.setTransform(zoomImg, r); + return; + } + var offsetAngle = Math.atan(w / h), origFactor = Math.sqrt(Math.pow(h, 2) + Math.pow(w, 2)), + scale = !origFactor ? 1 : (h / Math.cos(Math.PI / 2 + offsetAngle)) / origFactor, + s = ' scale(' + Math.abs(scale) + ')'; + $h.setTransform(img, r + s); + $h.setTransform(zoomImg, r + s); + if (zoomOnly) { + $img.css('visibility', 'visible').insertAfter($mark).addClass('file-zoom-detail'); + $mark.remove(); + $div.remove(); + } + }); + }, + _validateImageOrientation: function ($img, file, previewId, fileId, caption, ftype, fsize, iData) { + var self = this, exifObj = null, value, autoOrientImage = self.autoOrientImage, selector; + if (self.canOrientImage) { + $img.css('image-orientation', (autoOrientImage ? 'from-image' : 'none')); + self._validateImage(previewId, fileId, caption, ftype, fsize, iData, exifObj); + return; + } + selector = $h.getZoomSelector(previewId, ' img'); + exifObj = autoOrientImage ? self._getExifObj(iData) : null; + value = exifObj ? exifObj['0th'][piexif.ImageIFD.Orientation] : null; // jshint ignore:line + if (!value) { + self._validateImage(previewId, fileId, caption, ftype, fsize, iData, exifObj); + return; + } + self.setImageOrientation($img, $(selector), value, self._getFrame(previewId)); + self._raise('fileimageoriented', {'$img': $img, 'file': file}); + self._validateImage(previewId, fileId, caption, ftype, fsize, iData, exifObj); + }, + _validateImage: function (previewId, fileId, fname, ftype, fsize, iData, exifObj) { + var self = this, $preview = self.$preview, params, w1, w2, $thumb = self._getFrame(previewId), + i = $thumb.attr('data-fileindex'), $img = $thumb.find('img'); + fname = fname || 'Untitled'; + $img.one('load', function () { + w1 = $thumb.width(); + w2 = $preview.width(); + if (w1 > w2) { + $img.css('width', '100%'); + } + params = {ind: i, id: previewId, fileId: fileId}; + self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Width', params); + self._checkDimensions(i, 'Small', $img, $thumb, fname, 'Height', params); + if (!self.resizeImage) { + self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Width', params); + self._checkDimensions(i, 'Large', $img, $thumb, fname, 'Height', params); + } + self._raise('fileimageloaded', [previewId]); + self.fileManager.addImage(fileId, { + ind: i, + img: $img, + thumb: $thumb, + pid: previewId, + typ: ftype, + siz: fsize, + validated: false, + imgData: iData, + exifObj: exifObj + }); + $thumb.data('exif', exifObj); + self._validateAllImages(); + }).one('error', function () { + self._raise('fileimageloaderror', [previewId]); + }); + }, + _validateAllImages: function () { + var self = this, counter = {val: 0}, numImgs = self.fileManager.getImageCount(), fsize, + minSize = self.resizeIfSizeMoreThan; + if (numImgs !== self.fileManager.totalImages) { + return; + } + self._raise('fileimagesloaded'); + if (!self.resizeImage) { + return; + } + $.each(self.fileManager.loadedImages, function (id, config) { + if (!config.validated) { + fsize = config.siz; + if (fsize && fsize > minSize * self.bytesToKB) { + self._getResizedImage(id, config, counter, numImgs); + } + config.validated = true; + } + }); + }, + _getResizedImage: function (id, config, counter, numImgs) { + var self = this, img = $(config.img)[0], width = img.naturalWidth, height = img.naturalHeight, blob, + ratio = 1, maxWidth = self.maxImageWidth || width, maxHeight = self.maxImageHeight || height, + isValidImage = !!(width && height), chkWidth, chkHeight, canvas = self.imageCanvas, dataURI, + context = self.imageCanvasContext, type = config.typ, pid = config.pid, ind = config.ind, + $thumb = config.thumb, throwError, msg, exifObj = config.exifObj, exifStr, file, params, evParams; + throwError = function (msg, params, ev) { + if (self.isAjaxUpload) { + self._showFileError(msg, params, ev); + } else { + self._showError(msg, params, ev); + } + self._setPreviewError($thumb); + }; + file = self.fileManager.getFile(id); + params = {id: pid, 'index': ind, fileId: id}; + evParams = [id, pid, ind]; + if (!file || !isValidImage || (width <= maxWidth && height <= maxHeight)) { + if (isValidImage && file) { + self._raise('fileimageresized', evParams); + } + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized'); + } + if (!isValidImage) { + throwError(self.msgImageResizeError, params, 'fileimageresizeerror'); + return; + } + } + type = type || self.resizeDefaultImageType; + chkWidth = width > maxWidth; + chkHeight = height > maxHeight; + if (self.resizePreference === 'width') { + ratio = chkWidth ? maxWidth / width : (chkHeight ? maxHeight / height : 1); + } else { + ratio = chkHeight ? maxHeight / height : (chkWidth ? maxWidth / width : 1); + } + self._resetCanvas(); + width *= ratio; + height *= ratio; + canvas.width = width; + canvas.height = height; + try { + context.drawImage(img, 0, 0, width, height); + dataURI = canvas.toDataURL(type, self.resizeQuality); + if (exifObj) { + exifStr = window.piexif.dump(exifObj); + dataURI = window.piexif.insert(exifStr, dataURI); + } + blob = $h.dataURI2Blob(dataURI); + self.fileManager.setFile(id, blob); + self._raise('fileimageresized', evParams); + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized', [undefined, undefined]); + } + if (!(blob instanceof Blob)) { + throwError(self.msgImageResizeError, params, 'fileimageresizeerror'); + } + } catch (err) { + counter.val++; + if (counter.val === numImgs) { + self._raise('fileimagesresized', [undefined, undefined]); + } + msg = self.msgImageResizeException.replace('{errors}', err.message); + throwError(msg, params, 'fileimageresizeexception'); + } + }, + _showProgress: function () { + var self = this; + if (self.$progress && self.$progress.length) { + self.$progress.show(); + } + }, + _hideProgress: function () { + var self = this; + if (self.$progress && self.$progress.length) { + self.$progress.hide(); + } + }, + _initBrowse: function ($container) { + var self = this, $el = self.$element; + if (self.showBrowse) { + self.$btnFile = $container.find('.btn-file').append($el); + } else { + $el.appendTo($container).attr('tabindex', -1); + $h.addCss($el, 'file-no-browse'); + } + }, + _initClickable: function () { + var self = this, $zone, $tmpZone; + if (!self.isClickable) { + return; + } + $zone = self.$dropZone; + if (!self.isAjaxUpload) { + $tmpZone = self.$preview.find('.file-default-preview'); + if ($tmpZone.length) { + $zone = $tmpZone; + } + } + + $h.addCss($zone, 'clickable'); + $zone.attr('tabindex', -1); + self._handler($zone, 'click', function (e) { + var $tar = $(e.target); + if (!self.$errorContainer.is(':visible') && (!$tar.parents( + '.file-preview-thumbnails').length || $tar.parents( + '.file-default-preview').length)) { + self.$element.data('zoneClicked', true).trigger('click'); + $zone.blur(); + } + }); + }, + _initCaption: function () { + var self = this, cap = self.initialCaption || ''; + if (self.overwriteInitial || $h.isEmpty(cap)) { + self.$caption.val(''); + return false; + } + self._setCaption(cap); + return true; + }, + _setCaption: function (content, isError) { + var self = this, title, out, icon, n, cap, file; + if (!self.$caption.length) { + return; + } + self.$captionContainer.removeClass('icon-visible'); + if (isError) { + title = $('
    ' + self.msgValidationError + '
    ').text(); + n = self.fileManager.count(); + if (n) { + file = self.fileManager.getFirstFile(); + cap = n === 1 && file ? file.nameFmt : self._getMsgSelected(n); + } else { + cap = self._getMsgSelected(self.msgNo); + } + out = $h.isEmpty(content) ? cap : content; + icon = '' + self.msgValidationErrorIcon + ''; + } else { + if ($h.isEmpty(content)) { + self.$caption.attr('title', ''); + return; + } + title = $('
    ' + content + '
    ').text(); + out = title; + icon = self._getLayoutTemplate('fileIcon'); + } + self.$captionContainer.addClass('icon-visible'); + self.$caption.attr('title', title).val(out); + $h.setHtml(self.$captionIcon, icon); + }, + _createContainer: function () { + var self = this, attribs = {'class': 'file-input file-input-new' + (self.rtl ? ' kv-rtl' : '')}, + $container = $h.createElement($h.cspBuffer.stash(self._renderMain())); + $h.cspBuffer.apply($container); + $container.insertBefore(self.$element).attr(attribs); + self._initBrowse($container); + if (self.theme) { + $container.addClass('theme-' + self.theme); + } + return $container; + }, + _refreshContainer: function () { + var self = this, $container = self.$container, $el = self.$element; + $el.insertAfter($container); + $h.setHtml($container, self._renderMain()); + self._initBrowse($container); + self._validateDisabled(); + }, + _validateDisabled: function () { + var self = this; + self.$caption.attr({readonly: self.isDisabled}); + }, + _setTabIndex: function (type, html) { + var self = this, index = self.tabIndexConfig[type]; + return html.setTokens({ + tabIndexConfig: index === undefined || index === null ? '' : 'tabindex="' + index + '"' + }); + }, + _renderMain: function () { + var self = this, + dropCss = self.dropZoneEnabled ? ' file-drop-zone' : 'file-drop-disabled', + close = !self.showClose ? '' : self._getLayoutTemplate('close'), + preview = !self.showPreview ? '' : self._getLayoutTemplate('preview') + .setTokens({'class': self.previewClass, 'dropClass': dropCss}), + css = self.isDisabled ? self.captionClass + ' file-caption-disabled' : self.captionClass, + caption = self.captionTemplate.setTokens({'class': css + ' kv-fileinput-caption'}); + caption = self._setTabIndex('caption', caption); + return self.mainTemplate.setTokens({ + 'class': self.mainClass + (!self.showBrowse && self.showCaption ? ' no-browse' : ''), + 'inputGroupClass': self.inputGroupClass, + 'preview': preview, + 'close': close, + 'caption': caption, + 'upload': self._renderButton('upload'), + 'remove': self._renderButton('remove'), + 'cancel': self._renderButton('cancel'), + 'pause': self._renderButton('pause'), + 'browse': self._renderButton('browse') + }); + + }, + _renderButton: function (type) { + var self = this, tmplt = self._getLayoutTemplate('btnDefault'), css = self[type + 'Class'], + title = self[type + 'Title'], icon = self[type + 'Icon'], label = self[type + 'Label'], + status = self.isDisabled ? ' disabled' : '', btnType = 'button'; + switch (type) { + case 'remove': + if (!self.showRemove) { + return ''; + } + break; + case 'cancel': + if (!self.showCancel) { + return ''; + } + css += ' kv-hidden'; + break; + case 'pause': + if (!self.showPause) { + return ''; + } + css += ' kv-hidden'; + break; + case 'upload': + if (!self.showUpload) { + return ''; + } + if (self.isAjaxUpload && !self.isDisabled) { + tmplt = self._getLayoutTemplate('btnLink').replace('{href}', self.uploadUrl); + } else { + btnType = 'submit'; + } + break; + case 'browse': + if (!self.showBrowse) { + return ''; + } + tmplt = self._getLayoutTemplate('btnBrowse'); + break; + default: + return ''; + } + tmplt = self._setTabIndex(type, tmplt); + + css += type === 'browse' ? ' btn-file' : ' fileinput-' + type + ' fileinput-' + type + '-button'; + if (!$h.isEmpty(label)) { + label = ' ' + label + ''; + } + return tmplt.setTokens({ + 'type': btnType, 'css': css, 'title': title, 'status': status, 'icon': icon, 'label': label + }); + }, + _renderThumbProgress: function () { + var self = this; + return '
    ' + + self.progressInfoTemplate.setTokens({percent: 101, status: self.msgUploadBegin, stats: ''}) + + '
    '; + }, + _renderFileFooter: function (cat, caption, size, width, isError) { + var self = this, config = self.fileActionSettings, rem = config.showRemove, drg = config.showDrag, + upl = config.showUpload, zoom = config.showZoom, out, params, + template = self._getLayoutTemplate('footer'), tInd = self._getLayoutTemplate('indicator'), + ind = isError ? config.indicatorError : config.indicatorNew, + title = isError ? config.indicatorErrorTitle : config.indicatorNewTitle, + indicator = tInd.setTokens({'indicator': ind, 'indicatorTitle': title}); + size = self._getSize(size); + params = {type: cat, caption: caption, size: size, width: width, progress: '', indicator: indicator}; + if (self.isAjaxUpload) { + params.progress = self._renderThumbProgress(); + params.actions = self._renderFileActions(params, upl, false, rem, zoom, drg, false, false, false); + } else { + params.actions = self._renderFileActions(params, false, false, false, zoom, drg, false, false, false); + } + out = template.setTokens(params); + out = $h.replaceTags(out, self.previewThumbTags); + return out; + }, + _renderFileActions: function ( + cfg, + showUpl, + showDwn, + showDel, + showZoom, + showDrag, + disabled, + url, + key, + isInit, + dUrl, + dFile + ) { + var self = this; + if (!cfg.type && isInit) { + cfg.type = 'image'; + } + if (self.enableResumableUpload) { + showUpl = false; + } else { + if (typeof showUpl === 'function') { + showUpl = showUpl(cfg); + } + } + if (typeof showDwn === 'function') { + showDwn = showDwn(cfg); + } + if (typeof showDel === 'function') { + showDel = showDel(cfg); + } + if (typeof showZoom === 'function') { + showZoom = showZoom(cfg); + } + if (typeof showDrag === 'function') { + showDrag = showDrag(cfg); + } + if (!showUpl && !showDwn && !showDel && !showZoom && !showDrag) { + return ''; + } + var vUrl = url === false ? '' : ' data-url="' + url + '"', btnZoom = '', btnDrag = '', css, + vKey = key === false ? '' : ' data-key="' + key + '"', btnDelete = '', btnUpload = '', btnDownload = '', + template = self._getLayoutTemplate('actions'), config = self.fileActionSettings, + otherButtons = self.otherActionButtons.setTokens({'dataKey': vKey, 'key': key}), + removeClass = disabled ? config.removeClass + ' disabled' : config.removeClass; + if (showDel) { + btnDelete = self._getLayoutTemplate('actionDelete').setTokens({ + 'removeClass': removeClass, + 'removeIcon': config.removeIcon, + 'removeTitle': config.removeTitle, + 'dataUrl': vUrl, + 'dataKey': vKey, + 'key': key + }); + } + if (showUpl) { + btnUpload = self._getLayoutTemplate('actionUpload').setTokens({ + 'uploadClass': config.uploadClass, + 'uploadIcon': config.uploadIcon, + 'uploadTitle': config.uploadTitle + }); + } + if (showDwn) { + btnDownload = self._getLayoutTemplate('actionDownload').setTokens({ + 'downloadClass': config.downloadClass, + 'downloadIcon': config.downloadIcon, + 'downloadTitle': config.downloadTitle, + 'downloadUrl': dUrl || self.initialPreviewDownloadUrl + }); + btnDownload = btnDownload.setTokens({'filename': dFile, 'key': key}); + } + if (showZoom) { + btnZoom = self._getLayoutTemplate('actionZoom').setTokens({ + 'zoomClass': config.zoomClass, + 'zoomIcon': config.zoomIcon, + 'zoomTitle': config.zoomTitle + }); + } + if (showDrag && isInit) { + css = 'drag-handle-init ' + config.dragClass; + btnDrag = self._getLayoutTemplate('actionDrag').setTokens({ + 'dragClass': css, + 'dragTitle': config.dragTitle, + 'dragIcon': config.dragIcon + }); + } + return template.setTokens({ + 'delete': btnDelete, + 'upload': btnUpload, + 'download': btnDownload, + 'zoom': btnZoom, + 'drag': btnDrag, + 'other': otherButtons + }); + }, + _browse: function (e) { + var self = this; + if (e && e.isDefaultPrevented() || !self._raise('filebrowse')) { + return; + } + if (self.isError && !self.isAjaxUpload) { + self.clear(); + } + if (self.focusCaptionOnBrowse) { + self.$captionContainer.focus(); + } + }, + _change: function (e) { + var self = this; + $(document.body).off('focusin.fileinput focusout.fileinput'); + if (self.changeTriggered) { + return; + } + self._setLoading('show'); + var $el = self.$element, isDragDrop = arguments.length > 1, isAjaxUpload = self.isAjaxUpload, + tfiles, files = isDragDrop ? arguments[1] : $el[0].files, ctr = self.fileManager.count(), + total, initCount, len, isSingleUpl = $h.isEmpty($el.attr('multiple')), + maxCount = !isAjaxUpload && isSingleUpl ? 1 : self.maxFileCount, maxTotCount = self.maxTotalFileCount, + inclAll = maxTotCount > 0 && maxTotCount > maxCount, flagSingle = (isSingleUpl && ctr > 0), + throwError = function (mesg, file, previewId, index) { + var p1 = $.extend(true, {}, self._getOutData(null, {}, {}, files), {id: previewId, index: index}), + p2 = {id: previewId, index: index, file: file, files: files}; + self.isPersistentError = true; + self._setLoading('hide'); + return isAjaxUpload ? self._showFileError(mesg, p1) : self._showError(mesg, p2); + }, + maxCountCheck = function (n, m, all) { + var msg = all ? self.msgTotalFilesTooMany : self.msgFilesTooMany; + msg = msg.replace('{m}', m).replace('{n}', n); + self.isError = throwError(msg, null, null, null); + self.$captionContainer.removeClass('icon-visible'); + self._setCaption('', true); + self.$container.removeClass('file-input-new file-input-ajax-new'); + }; + self.reader = null; + self._resetUpload(); + self._hideFileIcon(); + if (self.dropZoneEnabled) { + self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); + } + if (!isAjaxUpload) { + if (e.target && e.target.files === undefined) { + files = e.target.value ? [{name: e.target.value.replace(/^.+\\/, '')}] : []; + } else { + files = e.target.files || {}; + } + } + tfiles = files; + if ($h.isEmpty(tfiles) || tfiles.length === 0) { + if (!isAjaxUpload) { + self.clear(); + } + self._raise('fileselectnone'); + return; + } + self._resetErrors(); + len = tfiles.length; + initCount = isAjaxUpload ? (self.fileManager.count() + len) : len; + total = self._getFileCount(initCount, inclAll ? false : undefined); + if (maxCount > 0 && total > maxCount) { + if (!self.autoReplace || len > maxCount) { + maxCountCheck((self.autoReplace && len > maxCount ? len : total), maxCount); + return; + } + if (total > maxCount) { + self._resetPreviewThumbs(isAjaxUpload); + } + } else { + if (inclAll) { + total = self._getFileCount(initCount, true); + if (maxTotCount > 0 && total > maxTotCount) { + if (!self.autoReplace || len > maxCount) { + maxCountCheck((self.autoReplace && len > maxTotCount ? len : total), maxTotCount, true); + return; + } + if (total > maxCount) { + self._resetPreviewThumbs(isAjaxUpload); + } + } + } + if (!isAjaxUpload || flagSingle) { + self._resetPreviewThumbs(false); + if (flagSingle) { + self.clearFileStack(); + } + } else { + if (isAjaxUpload && ctr === 0 && (!self.previewCache.count(true) || self.overwriteInitial)) { + self._resetPreviewThumbs(true); + } + } + } + self.readFiles(tfiles); + self._setLoading('hide'); + }, + _abort: function (params) { + var self = this, data; + if (self.ajaxAborted && typeof self.ajaxAborted === 'object' && self.ajaxAborted.message !== undefined) { + data = $.extend(true, {}, self._getOutData(null), params); + data.abortData = self.ajaxAborted.data || {}; + data.abortMessage = self.ajaxAborted.message; + self._setProgress(101, self.$progress, self.msgCancelled); + self._showFileError(self.ajaxAborted.message, data, 'filecustomerror'); + self.cancel(); + self.unlock(); + return true; + } + return !!self.ajaxAborted; + }, + _resetFileStack: function () { + var self = this, i = 0; + self._getThumbs().each(function () { + var $thumb = $(this), ind = $thumb.attr('data-fileindex'), pid = $thumb.attr('id'); + if (ind === '-1' || ind === -1) { + return; + } + if (!self._getThumbFile($thumb)) { + $thumb.attr({'data-fileindex': i}); + i++; + } else { + $thumb.attr({'data-fileindex': '-1'}); + } + self._getZoom(pid).attr({ + 'data-fileindex': $thumb.attr('data-fileindex') + }); + }); + }, + _isFileSelectionValid: function (cnt) { + var self = this; + cnt = cnt || 0; + if (self.required && !self.getFilesCount()) { + self.$errorContainer.html(''); + self._showFileError(self.msgFileRequired); + return false; + } + if (self.minFileCount > 0 && self._getFileCount(cnt) < self.minFileCount) { + self._noFilesError({}); + return false; + } + return true; + }, + _canPreview: function (file) { + var self = this; + if (!file || !self.showPreview || !self.$preview || !self.$preview.length) { + return false; + } + var name = file.name || '', type = file.type || '', size = (file.size || 0) / self.bytesToKB, + cat = self._parseFileType(type, name), allowedTypes, allowedMimes, allowedExts, skipPreview, + types = self.allowedPreviewTypes, mimes = self.allowedPreviewMimeTypes, + exts = self.allowedPreviewExtensions || [], dTypes = self.disabledPreviewTypes, + dMimes = self.disabledPreviewMimeTypes, dExts = self.disabledPreviewExtensions || [], + maxSize = self.maxFilePreviewSize && parseFloat(self.maxFilePreviewSize) || 0, + expAllExt = new RegExp('\\.(' + exts.join('|') + ')$', 'i'), + expDisExt = new RegExp('\\.(' + dExts.join('|') + ')$', 'i'); + allowedTypes = !types || types.indexOf(cat) !== -1; + allowedMimes = !mimes || mimes.indexOf(type) !== -1; + allowedExts = !exts.length || $h.compare(name, expAllExt); + skipPreview = (dTypes && dTypes.indexOf(cat) !== -1) || (dMimes && dMimes.indexOf(type) !== -1) || + (dExts.length && $h.compare(name, expDisExt)) || (maxSize && !isNaN(maxSize) && size > maxSize); + return !skipPreview && (allowedTypes || allowedMimes || allowedExts); + }, + addToStack: function (file, id) { + this.fileManager.add(file, id); + }, + clearFileStack: function () { + var self = this; + self.fileManager.clear(); + self._initResumableUpload(); + if (self.enableResumableUpload) { + if (self.showPause === null) { + self.showPause = true; + } + if (self.showCancel === null) { + self.showCancel = false; + } + } else { + self.showPause = false; + if (self.showCancel === null) { + self.showCancel = true; + } + } + return self.$element; + }, + getFileStack: function () { + return this.fileManager.stack; + }, + getFileList: function () { + return this.fileManager.list(); + }, + getFilesSize: function () { + return this.fileManager.getTotalSize(); + }, + getFilesCount: function (includeInitial) { + var self = this, len = self.isAjaxUpload ? self.fileManager.count() : self._inputFileCount(); + if (includeInitial) { + len += self.previewCache.count(true); + } + return self._getFileCount(len); + }, + _initCapStatus: function (status) { + var self = this, $cap = self.$caption; + $cap.removeClass('is-valid file-processing'); + if (!status) { + return; + } + if (status === 'processing') { + $cap.addClass('file-processing'); + } else { + $cap.addClass('is-valid'); + } + }, + _setLoading: function (type) { + var self = this; + self.$previewStatus.html(type === 'hide' ? '' : self.msgProcessing); + self.$container.removeClass('file-thumb-loading'); + self._initCapStatus(type === 'hide' ? '' : 'processing'); + if (type !== 'hide') { + if (self.dropZoneEnabled) { + self.$container.find('.file-drop-zone .' + self.dropZoneTitleClass).remove(); + } + self.$container.addClass('file-thumb-loading'); + } + }, + _initFileSelected: function () { + var self = this, $el = self.$element, $body = $(document.body), ev = 'focusin.fileinput focusout.fileinput'; + if ($body.length) { + $body.off(ev).on('focusout.fileinput', function () { + self._setLoading('show'); + }).on('focusin.fileinput', function () { + setTimeout(function () { + if (!$el.val()) { + self._setLoading('hide'); + self._setFileDropZoneTitle(); + } + $body.off(ev); + }, 2500); + }); + } else { + self._setLoading('hide'); + } + }, + readFiles: function (files) { + this.reader = new FileReader(); + var self = this, reader = self.reader, $container = self.$previewContainer, + $status = self.$previewStatus, msgLoading = self.msgLoading, msgProgress = self.msgProgress, + previewInitId = self.previewInitId, numFiles = files.length, settings = self.fileTypeSettings, + readFile, fileTypes = self.allowedFileTypes, typLen = fileTypes ? fileTypes.length : 0, + fileExt = self.allowedFileExtensions, strExt = $h.isEmpty(fileExt) ? '' : fileExt.join(', '), + throwError = function (msg, file, previewId, index, fileId) { + var $thumb, p1 = $.extend(true, {}, self._getOutData(null, {}, {}, files), + {id: previewId, index: index, fileId: fileId}), + p2 = {id: previewId, index: index, fileId: fileId, file: file, files: files}; + self._previewDefault(file, true); + $thumb = self._getFrame(previewId, true); + self._setLoading('hide'); + if (self.isAjaxUpload) { + setTimeout(function () { + readFile(index + 1); + }, self.processDelay); + } else { + self.unlock(); + numFiles = 0; + } + if (self.removeFromPreviewOnError && $thumb.length) { + $thumb.remove(); + } else { + self._initFileActions(); + $thumb.find('.kv-file-upload').remove(); + } + self.isPersistentError = true; + self.isError = self.isAjaxUpload ? self._showFileError(msg, p1) : self._showError(msg, p2); + self._updateFileDetails(numFiles); + }; + self.fileManager.clearImages(); + $.each(files, function (key, file) { + var func = self.fileTypeSettings.image; + if (func && func(file.type)) { + self.fileManager.totalImages++; + } + }); + readFile = function (i) { + var $error = self.$errorContainer, errors, fm = self.fileManager; + if (i >= numFiles) { + self.unlock(); + if (self.duplicateErrors.length) { + errors = '
  • ' + self.duplicateErrors.join('
  • ') + '
  • '; + if ($error.find('ul').length === 0) { + $h.setHtml($error, self.errorCloseButton + '
      ' + errors + '
    '); + } else { + $error.find('ul').append(errors); + } + $error.fadeIn(self.fadeDelay); + self._handler($error.find('.kv-error-close'), 'click', function () { + $error.fadeOut(self.fadeDelay); + }); + self.duplicateErrors = []; + } + if (self.isAjaxUpload) { + self._raise('filebatchselected', [fm.stack]); + if (fm.count() === 0 && !self.isError) { + self.reset(); + } + } else { + self._raise('filebatchselected', [files]); + } + $container.removeClass('file-thumb-loading'); + self._initCapStatus('valid'); + $status.html(''); + return; + } + self.lock(true); + var file = files[i], id = self._getFileId(file), previewId = previewInitId + '-' + id, fSizeKB, j, msg, + fnImage = settings.image, typ, chk, typ1, typ2, + caption = self._getFileName(file, ''), fileSize = (file && file.size || 0) / self.bytesToKB, + fileExtExpr = '', previewData = $h.createObjectURL(file), fileCount = 0, + strTypes = '', fileId, canLoad, fileReaderAborted = false, + func, knownTypes = 0, isImage, txtFlag, processFileLoaded = function () { + var isImageResized = !!fm.loadedImages[id], msg = msgProgress.setTokens({ + 'index': i + 1, + 'files': numFiles, + 'percent': 50, + 'name': caption + }); + setTimeout(function () { + $status.html(msg); + self._updateFileDetails(numFiles); + readFile(i + 1); + }, self.processDelay); + if (self._raise('fileloaded', [file, previewId, id, i, reader]) && self.isAjaxUpload) { + if (!isImageResized) { + fm.add(file); + } + } else { + if (isImageResized) { + fm.removeFile(id); + } + } + }; + if (!file) { + return; + } + fileId = fm.getId(file); + if (typLen > 0) { + for (j = 0; j < typLen; j++) { + typ1 = fileTypes[j]; + typ2 = self.msgFileTypes[typ1] || typ1; + strTypes += j === 0 ? typ2 : ', ' + typ2; + } + } + if (caption === false) { + readFile(i + 1); + return; + } + if (caption.length === 0) { + msg = self.msgInvalidFileName.replace('{name}', $h.htmlEncode($h.getFileName(file), '[unknown]')); + throwError(msg, file, previewId, i, fileId); + return; + } + if (!$h.isEmpty(fileExt)) { + fileExtExpr = new RegExp('\\.(' + fileExt.join('|') + ')$', 'i'); + } + fSizeKB = fileSize.toFixed(2); + if (self.isAjaxUpload && fm.exists(fileId) || self._getFrame(previewId, true).length) { + var p2 = {id: previewId, index: i, fileId: fileId, file: file, files: files}; + msg = self.msgDuplicateFile.setTokens({name: caption, size: fSizeKB}); + if (self.isAjaxUpload) { + self.duplicateErrors.push(msg); + self.isDuplicateError = true; + self._raise('fileduplicateerror', [file, fileId, caption, fSizeKB, previewId, i]); + readFile(i + 1); + self._updateFileDetails(numFiles); + } else { + self._showError(msg, p2); + self.unlock(); + numFiles = 0; + self._clearFileInput(); + self.reset(); + self._updateFileDetails(numFiles); + } + return; + } + if (self.maxFileSize > 0 && fileSize > self.maxFileSize) { + msg = self.msgSizeTooLarge.setTokens({ + 'name': caption, + 'size': fSizeKB, + 'maxSize': self.maxFileSize + }); + throwError(msg, file, previewId, i, fileId); + return; + } + if (self.minFileSize !== null && fileSize <= $h.getNum(self.minFileSize)) { + msg = self.msgSizeTooSmall.setTokens({ + 'name': caption, + 'size': fSizeKB, + 'minSize': self.minFileSize + }); + throwError(msg, file, previewId, i, fileId); + return; + } + if (!$h.isEmpty(fileTypes) && $h.isArray(fileTypes)) { + for (j = 0; j < fileTypes.length; j += 1) { + typ = fileTypes[j]; + func = settings[typ]; + fileCount += !func || (typeof func !== 'function') ? 0 : (func(file.type, + $h.getFileName(file)) ? 1 : 0); + } + if (fileCount === 0) { + msg = self.msgInvalidFileType.setTokens({name: caption, types: strTypes}); + throwError(msg, file, previewId, i, fileId); + return; + } + } + if (fileCount === 0 && !$h.isEmpty(fileExt) && $h.isArray(fileExt) && !$h.isEmpty(fileExtExpr)) { + chk = $h.compare(caption, fileExtExpr); + fileCount += $h.isEmpty(chk) ? 0 : chk.length; + if (fileCount === 0) { + msg = self.msgInvalidFileExtension.setTokens({name: caption, extensions: strExt}); + throwError(msg, file, previewId, i, fileId); + return; + } + } + if (!self._canPreview(file)) { + canLoad = self.isAjaxUpload && self._raise('filebeforeload', [file, i, reader]); + if (self.isAjaxUpload && canLoad) { + fm.add(file); + } + if (self.showPreview && canLoad) { + $container.addClass('file-thumb-loading'); + self._initCapStatus('processing'); + self._previewDefault(file); + self._initFileActions(); + } + setTimeout(function () { + if (canLoad) { + self._updateFileDetails(numFiles); + } + readFile(i + 1); + self._raise('fileloaded', [file, previewId, id, i]); + }, 10); + return; + } + isImage = fnImage(file.type, caption); + $status.html(msgLoading.replace('{index}', i + 1).replace('{files}', numFiles)); + $container.addClass('file-thumb-loading'); + self._initCapStatus('processing'); + reader.onerror = function (evt) { + self._errorHandler(evt, caption); + }; + reader.onload = function (theFile) { + var hex, fileInfo, uint, byte, bytes = [], contents, mime, readImage = function () { + var newReader = new FileReader(); + newReader.onerror = function (theFileNew) { + self._errorHandler(theFileNew, caption); + }; + newReader.onload = function (theFileNew) { + if (self.isAjaxUpload && !self._raise('filebeforeload', [file, i, reader])) { + fileReaderAborted = true; + self._resetCaption(); + reader.abort(); + $status.html(''); + $container.removeClass('file-thumb-loading'); + self._initCapStatus('valid'); + self.enable(); + return; + } + self._previewFile(i, file, theFileNew, previewData, fileInfo); + self._initFileActions(); + processFileLoaded(); + }; + newReader.readAsDataURL(file); + }; + fileInfo = {'name': caption, 'type': file.type}; + $.each(settings, function (k, f) { + if (k !== 'object' && k !== 'other' && typeof f === 'function' && f(file.type, caption)) { + knownTypes++; + } + }); + if (knownTypes === 0) { // auto detect mime types from content if no known file types detected + uint = new Uint8Array(theFile.target.result); + for (j = 0; j < uint.length; j++) { + byte = uint[j].toString(16); + bytes.push(byte); + } + hex = bytes.join('').toLowerCase().substring(0, 8); + mime = $h.getMimeType(hex, '', ''); + if ($h.isEmpty(mime)) { // look for ascii text content + contents = $h.arrayBuffer2String(reader.result); + mime = $h.isSvg(contents) ? 'image/svg+xml' : $h.getMimeType(hex, contents, file.type); + } + fileInfo = {'name': caption, 'type': mime}; + isImage = fnImage(mime, ''); + if (isImage) { + readImage(txtFlag); + return; + } + } + if (self.isAjaxUpload && !self._raise('filebeforeload', [file, i, reader])) { + fileReaderAborted = true; + self._resetCaption(); + reader.abort(); + $status.html(''); + $container.removeClass('file-thumb-loading'); + self._initCapStatus('valid'); + self.enable(); + return; + } + self._previewFile(i, file, theFile, previewData, fileInfo); + self._initFileActions(); + processFileLoaded(); + }; + reader.onprogress = function (data) { + if (data.lengthComputable) { + var fact = (data.loaded / data.total) * 100, progress = Math.ceil(fact); + msg = msgProgress.setTokens({ + 'index': i + 1, + 'files': numFiles, + 'percent': progress, + 'name': caption + }); + setTimeout(function () { + if (!fileReaderAborted) { + $status.html(msg); + } + }, self.processDelay); + } + }; + if (isImage) { + reader.readAsDataURL(file); + } else { + reader.readAsArrayBuffer(file); + } + }; + + readFile(0); + self._updateFileDetails(numFiles); + }, + lock: function (selectMode) { + var self = this, $container = self.$container; + self._resetErrors(); + self.disable(); + if (!selectMode && self.showCancel) { + $container.find('.fileinput-cancel').show(); + } + if (!selectMode && self.showPause) { + $container.find('.fileinput-pause').show(); + } + self._initCapStatus('processing'); + self._raise('filelock', [self.fileManager.stack, self._getExtraData()]); + return self.$element; + }, + unlock: function (reset) { + var self = this, $container = self.$container; + if (reset === undefined) { + reset = true; + } + self.enable(); + $container.removeClass('is-locked'); + if (self.showCancel) { + $container.find('.fileinput-cancel').hide(); + } + if (self.showPause) { + $container.find('.fileinput-pause').hide(); + } + if (reset) { + self._resetFileStack(); + } + self._initCapStatus(); + self._raise('fileunlock', [self.fileManager.stack, self._getExtraData()]); + return self.$element; + }, + resume: function () { + var self = this, fm = self.fileManager, flag = false, rm = self.resumableManager; + fm.bpsLog = []; + fm.bps = 0; + if (!self.enableResumableUpload) { + return self.$element; + } + if (self.paused) { + self._toggleResumableProgress(self.progressPauseTemplate, self.msgUploadResume); + } else { + flag = true; + } + self.paused = false; + if (flag) { + self._toggleResumableProgress(self.progressInfoTemplate, self.msgUploadBegin); + } + setTimeout(function () { + rm.upload(); + }, self.processDelay); + return self.$element; + }, + paste: function (e) { + var self = this, ev = e.originalEvent, files = ev.clipboardData && ev.clipboardData.files || null; + if (files) { + self._dropFiles(e, files); + } + return self.$element; + }, + pause: function () { + var self = this, rm = self.resumableManager, xhr = self.ajaxRequests, len = xhr.length, i, + pct = rm.getProgress(), actions = self.fileActionSettings, tm = self.taskManager, + pool = tm.getPool(rm.id); + if (!self.enableResumableUpload) { + return self.$element; + } else { + if (pool) { + pool.cancel(); + } + } + self._raise('fileuploadpaused', [self.fileManager, rm]); + if (len > 0) { + for (i = 0; i < len; i += 1) { + self.paused = true; + xhr[i].abort(); + } + } + if (self.showPreview) { + self._getThumbs().each(function () { + var $thumb = $(this), t = self._getLayoutTemplate('stats'), stats, + $indicator = $thumb.find('.file-upload-indicator'); + $thumb.removeClass('file-uploading'); + if ($indicator.attr('title') === actions.indicatorLoadingTitle) { + self._setThumbStatus($thumb, 'Paused'); + stats = t.setTokens({pendingTime: self.msgPaused, uploadSpeed: ''}); + self.paused = true; + self._setProgress(pct, $thumb.find('.file-thumb-progress'), pct + '%', stats); + } + if (!self._getThumbFile($thumb)) { + $thumb.find('.kv-file-remove').removeClass('disabled').removeAttr('disabled'); + } + }); + } + self._setProgress(101, self.$progress, self.msgPaused); + return self.$element; + }, + cancel: function () { + var self = this, xhr = self.ajaxRequests, + rm = self.resumableManager, tm = self.taskManager, + pool = rm ? tm.getPool(rm.id) : undefined, len = xhr.length, i; + + if (self.enableResumableUpload && pool) { + pool.cancel().done(function () { + self._setProgressCancelled(); + }); + rm.reset(); + self._raise('fileuploadcancelled', [self.fileManager, rm]); + } else { + self._raise('fileuploadcancelled', [self.fileManager]); + } + self._initAjax(); + if (len > 0) { + for (i = 0; i < len; i += 1) { + self.cancelling = true; + xhr[i].abort(); + } + } + self._getThumbs().each(function () { + var $thumb = $(this), $prog = $thumb.find('.file-thumb-progress'); + $thumb.removeClass('file-uploading'); + self._setProgress(0, $prog); + $prog.hide(); + if (!self._getThumbFile($thumb)) { + $thumb.find('.kv-file-upload').removeClass('disabled').removeAttr('disabled'); + $thumb.find('.kv-file-remove').removeClass('disabled').removeAttr('disabled'); + } + self.unlock(); + }); + setTimeout(function () { + self._setProgressCancelled(); + }, self.processDelay); + return self.$element; + }, + clear: function () { + var self = this, cap; + if (!self._raise('fileclear')) { + return; + } + self.$btnUpload.removeAttr('disabled'); + self._getThumbs().find('video,audio,img').each(function () { + $h.cleanMemory($(this)); + }); + self._clearFileInput(); + self._resetUpload(); + self.clearFileStack(); + self.isDuplicateError = false; + self.isPersistentError = false; + self._resetErrors(true); + if (self._hasInitialPreview()) { + self._showFileIcon(); + self._resetPreview(); + self._initPreviewActions(); + self.$container.removeClass('file-input-new'); + } else { + self._getThumbs().each(function () { + self._clearObjects($(this)); + }); + if (self.isAjaxUpload) { + self.previewCache.data = {}; + } + self.$preview.html(''); + cap = (!self.overwriteInitial && self.initialCaption.length > 0) ? self.initialCaption : ''; + self.$caption.attr('title', '').val(cap); + $h.addCss(self.$container, 'file-input-new'); + self._validateDefaultPreview(); + } + if (self.$container.find($h.FRAMES).length === 0) { + if (!self._initCaption()) { + self.$captionContainer.removeClass('icon-visible'); + } + } + self._hideFileIcon(); + if (self.focusCaptionOnClear) { + self.$captionContainer.focus(); + } + self._setFileDropZoneTitle(); + self._raise('filecleared'); + return self.$element; + }, + reset: function () { + var self = this; + if (!self._raise('filereset')) { + return; + } + self.lastProgress = 0; + self._resetPreview(); + self.$container.find('.fileinput-filename').text(''); + $h.addCss(self.$container, 'file-input-new'); + if (self.getFrames().length) { + self.$container.removeClass('file-input-new'); + } + self.clearFileStack(); + self._setFileDropZoneTitle(); + return self.$element; + }, + disable: function () { + var self = this, $container = self.$container; + self.isDisabled = true; + self._raise('filedisabled'); + self.$element.attr('disabled', 'disabled'); + $container.addClass('is-locked'); + $h.addCss($container.find('.btn-file'), 'disabled'); + $container.find('.kv-fileinput-caption').addClass('file-caption-disabled'); + $container.find('.fileinput-remove, .fileinput-upload, .file-preview-frame button') + .attr('disabled', true); + self._initDragDrop(); + return self.$element; + }, + enable: function () { + var self = this, $container = self.$container; + self.isDisabled = false; + self._raise('fileenabled'); + self.$element.removeAttr('disabled'); + $container.removeClass('is-locked'); + $container.find('.kv-fileinput-caption').removeClass('file-caption-disabled'); + $container.find('.fileinput-remove, .fileinput-upload, .file-preview-frame button') + .removeAttr('disabled'); + $container.find('.btn-file').removeClass('disabled'); + self._initDragDrop(); + return self.$element; + }, + upload: function () { + var self = this, fm = self.fileManager, totLen = fm.count(), i, outData, + hasExtraData = !$.isEmptyObject(self._getExtraData()); + fm.bpsLog = []; + fm.bps = 0; + if (!self.isAjaxUpload || self.isDisabled || !self._isFileSelectionValid(totLen)) { + return; + } + self.lastProgress = 0; + self._resetUpload(); + if (totLen === 0 && !hasExtraData) { + self._showFileError(self.msgUploadEmpty); + return; + } + self.cancelling = false; + self._showProgress(); + self.lock(); + if (totLen === 0 && hasExtraData) { + self._setProgress(2); + self._uploadExtraOnly(); + return; + } + if (self.enableResumableUpload) { + return self.resume(); + } + if (self.uploadAsync || self.enableResumableUpload) { + outData = self._getOutData(null); + if (!self._checkBatchPreupload(outData)) { + return; + } + self.fileBatchCompleted = false; + self.uploadCache = []; + $.each(self.getFileStack(), function (id) { + var previewId = self._getThumbId(id); + self.uploadCache.push({id: previewId, content: null, config: null, tags: null, append: true}); + }); + self.$preview.find('.file-preview-initial').removeClass($h.SORT_CSS); + self._initSortable(); + } + self._setProgress(2); + self.hasInitData = false; + if (self.uploadAsync) { + i = 0; + $.each(self.getFileStack(), function (id) { + self._uploadSingle(i, id, true); + i++; + }); + return; + } + self._uploadBatch(); + return self.$element; + }, + destroy: function () { + var self = this, $form = self.$form, $cont = self.$container, $el = self.$element, ns = self.namespace; + $(document).off(ns); + $(window).off(ns); + if ($form && $form.length) { + $form.off(ns); + } + if (self.isAjaxUpload) { + self._clearFileInput(); + } + self._cleanup(); + self._initPreviewCache(); + $el.insertBefore($cont).off(ns).removeData(); + $cont.off().remove(); + return $el; + }, + refresh: function (options) { + var self = this, $el = self.$element; + if (typeof options !== 'object' || $h.isEmpty(options)) { + options = self.options; + } else { + options = $.extend(true, {}, self.options, options); + } + self._init(options, true); + self._listen(); + return $el; + }, + zoom: function (frameId) { + var self = this, $frame = self._getFrame(frameId); + self._showModal($frame); + }, + getExif: function (frameId) { + var self = this, $frame = self._getFrame(frameId); + return $frame && $frame.data('exif') || null; + }, + getFrames: function (cssFilter) { + var self = this, $frames; + cssFilter = cssFilter || ''; + $frames = self.$preview.find($h.FRAMES + cssFilter); + if (self.reversePreviewOrder) { + $frames = $($frames.get().reverse()); + } + return $frames; + }, + getPreview: function () { + var self = this; + return { + content: self.initialPreview, + config: self.initialPreviewConfig, + tags: self.initialPreviewThumbTags + }; + } + }; + + $.fn.fileinput = function (option) { + if (!$h.hasFileAPISupport() && !$h.isIE(9)) { + return; + } + var args = Array.apply(null, arguments), retvals = []; + args.shift(); + this.each(function () { + var self = $(this), data = self.data('fileinput'), options = typeof option === 'object' && option, + theme = options.theme || self.data('theme'), l = {}, t = {}, + lang = options.language || self.data('language') || $.fn.fileinput.defaults.language || 'en', opt; + if (!data) { + if (theme) { + t = $.fn.fileinputThemes[theme] || {}; + } + if (lang !== 'en' && !$h.isEmpty($.fn.fileinputLocales[lang])) { + l = $.fn.fileinputLocales[lang] || {}; + } + opt = $.extend(true, {}, $.fn.fileinput.defaults, t, $.fn.fileinputLocales.en, l, options, self.data()); + data = new FileInput(this, opt); + self.data('fileinput', data); + } + + if (typeof option === 'string') { + retvals.push(data[option].apply(data, args)); + } + }); + switch (retvals.length) { + case 0: + return this; + case 1: + return retvals[0]; + default: + return retvals; + } + }; + + var IFRAME_ATTRIBS = 'class="kv-preview-data file-preview-pdf" src="{renderer}?file={data}" {style}', + defBtnCss1 = 'btn btn-sm btn-kv ' + $h.defaultButtonCss(), defBtnCss2 = 'btn ' + $h.defaultButtonCss(true); + + $.fn.fileinput.defaults = { + language: 'zh', + bytesToKB: 1024, + showCaption: true, + showBrowse: true, + showPreview: true, + showRemove: true, + showUpload: true, + showUploadStats: true, + showCancel: null, + showPause: null, + showClose: true, + showUploadedThumbs: true, + showConsoleLogs: false, + browseOnZoneClick: false, + autoReplace: false, + showDescriptionClose: true, + autoOrientImage: function () { // applicable for JPEG images only and non ios safari + var ua = window.navigator.userAgent, webkit = !!ua.match(/WebKit/i), + iOS = !!ua.match(/iP(od|ad|hone)/i), iOSSafari = iOS && webkit && !ua.match(/CriOS/i); + return !iOSSafari; + }, + autoOrientImageInitial: true, + required: false, + rtl: false, + hideThumbnailContent: false, + encodeUrl: true, + focusCaptionOnBrowse: true, + focusCaptionOnClear: true, + generateFileId: null, + previewClass: '', + captionClass: '', + frameClass: 'krajee-default', + mainClass: '', + inputGroupClass: '', + mainTemplate: null, + fileSizeGetter: null, + initialCaption: '', + initialPreview: [], + initialPreviewDelimiter: '*$$*', + initialPreviewAsData: false, + initialPreviewFileType: 'image', + initialPreviewConfig: [], + initialPreviewThumbTags: [], + previewThumbTags: {}, + initialPreviewShowDelete: true, + initialPreviewDownloadUrl: '', + removeFromPreviewOnError: false, + deleteUrl: '', + deleteExtraData: {}, + overwriteInitial: true, + sanitizeZoomCache: function (content) { + var $container = $h.createElement(content); + $container.find('input,textarea,select,datalist,form,.file-thumbnail-footer').remove(); + return $container.html(); + }, + previewZoomButtonIcons: { + prev: '', + next: '', + toggleheader: '', + fullscreen: '', + borderless: '', + close: '' + }, + previewZoomButtonClasses: { + prev: 'btn btn-default btn-outline-secondary btn-navigate', + next: 'btn btn-default btn-outline-secondary btn-navigate', + toggleheader: defBtnCss1, + fullscreen: defBtnCss1, + borderless: defBtnCss1, + close: defBtnCss1 + }, + previewTemplates: {}, + previewContentTemplates: {}, + preferIconicPreview: false, + preferIconicZoomPreview: false, + allowedFileTypes: null, + allowedFileExtensions: null, + allowedPreviewTypes: undefined, + allowedPreviewMimeTypes: null, + allowedPreviewExtensions: null, + disabledPreviewTypes: undefined, + disabledPreviewExtensions: ['msi', 'exe', 'com', 'zip', 'rar', 'app', 'vb', 'scr'], + disabledPreviewMimeTypes: null, + defaultPreviewContent: null, + customLayoutTags: {}, + customPreviewTags: {}, + previewFileIcon: '', + previewFileIconClass: 'file-other-icon', + previewFileIconSettings: {}, + previewFileExtSettings: {}, + buttonLabelClass: 'hidden-xs', + browseIcon: ' ', + browseClass: 'btn btn-primary', + removeIcon: '', + removeClass: defBtnCss2, + cancelIcon: '', + cancelClass: defBtnCss2, + pauseIcon: '', + pauseClass: defBtnCss2, + uploadIcon: '', + uploadClass: defBtnCss2, + uploadUrl: null, + uploadUrlThumb: null, + uploadAsync: true, + uploadParamNames: { + chunkCount: 'chunkCount', + chunkIndex: 'chunkIndex', + chunkSize: 'chunkSize', + chunkSizeStart: 'chunkSizeStart', + chunksUploaded: 'chunksUploaded', + fileBlob: 'fileBlob', + fileId: 'fileId', + fileName: 'fileName', + fileRelativePath: 'fileRelativePath', + fileSize: 'fileSize', + retryCount: 'retryCount' + }, + maxAjaxThreads: 5, + fadeDelay: 800, + processDelay: 100, + bitrateUpdateDelay: 500, + queueDelay: 10, // must be lesser than process delay + progressDelay: 0, // must be lesser than process delay + enableResumableUpload: false, + resumableUploadOptions: { + fallback: null, + testUrl: null, // used for checking status of chunks/ files previously / partially uploaded + chunkSize: 2048, // in KB + maxThreads: 4, + maxRetries: 3, + showErrorLog: true, + retainErrorHistory: true, // display complete error history always unless user explicitly resets upload + skipErrorsAndProceed: false // when set to true, files with errors will be skipped and upload will continue with other files + }, + uploadExtraData: {}, + zoomModalHeight: 480, + minImageWidth: null, + minImageHeight: null, + maxImageWidth: null, + maxImageHeight: null, + resizeImage: false, + resizePreference: 'width', + resizeQuality: 0.92, + resizeDefaultImageType: 'image/jpeg', + resizeIfSizeMoreThan: 0, // in KB + minFileSize: -1, + maxFileSize: 0, + maxFilePreviewSize: 25600, // 25 MB + minFileCount: 0, + maxFileCount: 0, + maxTotalFileCount: 0, + validateInitialCount: false, + msgValidationErrorClass: 'text-danger', + msgValidationErrorIcon: ' ', + msgErrorClass: 'file-error-message', + progressThumbClass: 'progress-bar progress-bar-striped active progress-bar-animated', + progressClass: 'progress-bar bg-success progress-bar-success progress-bar-striped active progress-bar-animated', + progressInfoClass: 'progress-bar bg-info progress-bar-info progress-bar-striped active progress-bar-animated', + progressCompleteClass: 'progress-bar bg-success progress-bar-success', + progressPauseClass: 'progress-bar bg-primary progress-bar-primary progress-bar-striped active progress-bar-animated', + progressErrorClass: 'progress-bar bg-danger progress-bar-danger', + progressUploadThreshold: 99, + previewFileType: 'image', + elCaptionContainer: null, + elCaptionText: null, + elPreviewContainer: null, + elPreviewImage: null, + elPreviewStatus: null, + elErrorContainer: null, + errorCloseButton: undefined, + slugCallback: null, + dropZoneEnabled: true, + dropZoneTitleClass: 'file-drop-zone-title', + fileActionSettings: {}, + otherActionButtons: '', + textEncoding: 'UTF-8', + preProcessUpload: null, + ajaxSettings: {}, + ajaxDeleteSettings: {}, + showAjaxErrorDetails: true, + mergeAjaxCallbacks: false, + mergeAjaxDeleteCallbacks: false, + retryErrorUploads: true, + reversePreviewOrder: false, + usePdfRenderer: function () { + var isIE11 = !!window.MSInputMethodContext && !!document.documentMode; + return !!navigator.userAgent.match(/(iPod|iPhone|iPad|Android)/i) || isIE11; + }, + pdfRendererUrl: '', + pdfRendererTemplate: '', + tabIndexConfig: { + browse: 500, + remove: 500, + upload: 500, + cancel: null, + pause: null, + modal: -1 + } + }; + + // noinspection HtmlUnknownAttribute + $.fn.fileinputLocales.en = { + sizeUnits: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + bitRateUnits: ['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s', 'PB/s', 'EB/s', 'ZB/s', 'YB/s'], + fileSingle: 'file', + filePlural: 'files', + browseLabel: 'Browse …', + removeLabel: 'Remove', + removeTitle: 'Clear all unprocessed files', + cancelLabel: 'Cancel', + cancelTitle: 'Abort ongoing upload', + pauseLabel: 'Pause', + pauseTitle: 'Pause ongoing upload', + uploadLabel: 'Upload', + uploadTitle: 'Upload selected files', + msgNo: 'No', + msgNoFilesSelected: 'No files selected', + msgCancelled: 'Cancelled', + msgPaused: 'Paused', + msgPlaceholder: 'Select {files} ...', + msgZoomModalHeading: 'Detailed Preview', + msgFileRequired: 'You must select a file to upload.', + msgSizeTooSmall: 'File "{name}" ({size} KB) is too small and must be larger than {minSize} KB.', + msgSizeTooLarge: 'File "{name}" ({size} KB) exceeds maximum allowed upload size of {maxSize} KB.', + msgFilesTooLess: 'You must select at least {n} {files} to upload.', + msgFilesTooMany: 'Number of files selected for upload ({n}) exceeds maximum allowed limit of {m}.', + msgTotalFilesTooMany: 'You can upload a maximum of {m} files ({n} files detected).', + msgFileNotFound: 'File "{name}" not found!', + msgFileSecured: 'Security restrictions prevent reading the file "{name}".', + msgFileNotReadable: 'File "{name}" is not readable.', + msgFilePreviewAborted: 'File preview aborted for "{name}".', + msgFilePreviewError: 'An error occurred while reading the file "{name}".', + msgInvalidFileName: 'Invalid or unsupported characters in file name "{name}".', + msgInvalidFileType: 'Invalid type for file "{name}". Only "{types}" files are supported.', + msgInvalidFileExtension: 'Invalid extension for file "{name}". Only "{extensions}" files are supported.', + msgFileTypes: { + 'image': 'image', + 'html': 'HTML', + 'text': 'text', + 'video': 'video', + 'audio': 'audio', + 'flash': 'flash', + 'pdf': 'PDF', + 'object': 'object' + }, + msgUploadAborted: 'The file upload was aborted', + msgUploadThreshold: 'Processing …', + msgUploadBegin: 'Initializing …', + msgUploadEnd: 'Done', + msgUploadResume: 'Resuming upload …', + msgUploadEmpty: 'No valid data available for upload.', + msgUploadError: 'Upload Error', + msgDeleteError: 'Delete Error', + msgProgressError: 'Error', + msgValidationError: 'Validation Error', + msgLoading: 'Loading file {index} of {files} …', + msgProgress: 'Loading file {index} of {files} - {name} - {percent}% completed.', + msgSelected: '{n} {files} selected', + msgProcessing: 'Processing ...', + msgFoldersNotAllowed: 'Drag & drop files only! {n} folder(s) dropped were skipped.', + msgImageWidthSmall: 'Width of image file "{name}" must be at least {size} px.', + msgImageHeightSmall: 'Height of image file "{name}" must be at least {size} px.', + msgImageWidthLarge: 'Width of image file "{name}" cannot exceed {size} px.', + msgImageHeightLarge: 'Height of image file "{name}" cannot exceed {size} px.', + msgImageResizeError: 'Could not get the image dimensions to resize.', + msgImageResizeException: 'Error while resizing the image.
    {errors}
    ', + msgAjaxError: 'Something went wrong with the {operation} operation. Please try again later!', + msgAjaxProgressError: '{operation} failed', + msgDuplicateFile: 'File "{name}" of same size "{size} KB" has already been selected earlier. Skipping duplicate selection.', + msgResumableUploadRetriesExceeded: 'Upload aborted beyond {max} retries for file {file}! Error Details:
    {error}
    ', + msgPendingTime: '{time} remaining', + msgCalculatingTime: 'calculating time remaining', + ajaxOperations: { + deleteThumb: 'file delete', + uploadThumb: 'file upload', + uploadBatch: 'batch file upload', + uploadExtra: 'form data upload' + }, + dropZoneTitle: 'Drag & drop files here …', + dropZoneClickTitle: '
    (or click to select {files})', + previewZoomButtonTitles: { + prev: 'View previous file', + next: 'View next file', + toggleheader: 'Toggle header', + fullscreen: 'Toggle full screen', + borderless: 'Toggle borderless mode', + close: 'Close detailed preview' + } + }; + + $.fn.fileinputLocales.zh = { + sizeUnits: ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], + bitRateUnits: ['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s', 'PB/s', 'EB/s', 'ZB/s', 'YB/s'], + fileSingle: '文件', + filePlural: '个文件', + browseLabel: '选择 …', + removeLabel: '移除', + removeTitle: '清除选中文件', + cancelLabel: '取消', + cancelTitle: '取消进行中的上传', + pauseLabel: '暂停', + pauseTitle: '暂停上传', + uploadLabel: '上传', + uploadTitle: '上传选中文件', + msgNo: '没有', + msgNoFilesSelected: '未选择文件', + msgPaused: '已暂停', + msgCancelled: '取消', + msgPlaceholder: '选择 {files} ...', + msgZoomModalHeading: '详细预览', + msgFileRequired: '必须选择一个文件上传.', + msgSizeTooSmall: '文件 "{name}" ({size} KB) 必须大于限定大小 {minSize} KB.', + msgSizeTooLarge: '文件 "{name}" ({size} KB) 超过了允许大小 {maxSize} KB.', + msgFilesTooLess: '你必须选择最少 {n} {files} 来上传. ', + msgFilesTooMany: '选择的上传文件个数 ({n}) 超出最大文件的限制个数 {m}.', + msgTotalFilesTooMany: '你最多可以上传 {m} 个文件 (当前有{n} 个文件).', + msgFileNotFound: '文件 "{name}" 未找到!', + msgFileSecured: '安全限制,为了防止读取文件 "{name}".', + msgFileNotReadable: '文件 "{name}" 不可读.', + msgFilePreviewAborted: '取消 "{name}" 的预览.', + msgFilePreviewError: '读取 "{name}" 时出现了一个错误.', + msgInvalidFileName: '文件名 "{name}" 包含非法字符.', + msgInvalidFileType: '不正确的类型 "{name}". 只支持 "{types}" 类型的文件.', + msgInvalidFileExtension: '不正确的文件扩展名 "{name}". 只支持 "{extensions}" 的文件扩展名.', + msgFileTypes: { + 'image': 'image', + 'html': 'HTML', + 'text': 'text', + 'video': 'video', + 'audio': 'audio', + 'flash': 'flash', + 'pdf': 'PDF', + 'object': 'object' + }, + msgUploadAborted: '该文件上传被中止', + msgUploadThreshold: '处理中 …', + msgUploadBegin: '正在初始化 …', + msgUploadEnd: '完成', + msgUploadResume: '继续上传 …', + msgUploadEmpty: '无效的文件上传.', + msgUploadError: '上传出错', + msgDeleteError: '删除出错', + msgProgressError: '上传出错', + msgValidationError: '验证错误', + msgLoading: '加载第 {index} 文件 共 {files} …', + msgProgress: '加载第 {index} 文件 共 {files} - {name} - {percent}% 完成.', + msgSelected: '{n} {files} 选中', + msgProcessing: '处理中 ...', + msgFoldersNotAllowed: '只支持拖拽文件! 跳过 {n} 拖拽的文件夹.', + msgImageWidthSmall: '图像文件的"{name}"的宽度必须是至少{size}像素.', + msgImageHeightSmall: '图像文件的"{name}"的高度必须至少为{size}像素.', + msgImageWidthLarge: '图像文件"{name}"的宽度不能超过{size}像素.', + msgImageHeightLarge: '图像文件"{name}"的高度不能超过{size}像素.', + msgImageResizeError: '无法获取的图像尺寸调整。', + msgImageResizeException: '调整图像大小时发生错误。
    {errors}
    ', + msgAjaxError: '{operation} 发生错误. 请重试!', + msgAjaxProgressError: '{operation} 失败', + msgDuplicateFile: '文件 "{name}",大小 "{size} KB" 已经被选中.忽略相同的文件.', + msgResumableUploadRetriesExceeded: '文件 {file} 上传失败超过 {max} 次重试 ! 错误详情:
    {error}
    ', + msgPendingTime: '{time} 剩余', + msgCalculatingTime: '计算剩余时间', + ajaxOperations: { + deleteThumb: '删除文件', + uploadThumb: '上传文件', + uploadBatch: '批量上传', + uploadExtra: '表单数据上传' + }, + dropZoneTitle: '拖拽文件到这里 …
    支持多文件同时上传', + dropZoneClickTitle: '
    (或点击{files}按钮选择文件)', + fileActionSettings: { + removeTitle: '删除文件', + uploadTitle: '上传文件', + downloadTitle: '下载文件', + uploadRetryTitle: '重试', + zoomTitle: '查看详情', + dragTitle: '移动 / 重置', + indicatorNewTitle: '没有上传', + indicatorSuccessTitle: '上传', + indicatorErrorTitle: '上传错误', + indicatorPausedTitle: '上传已暂停', + indicatorLoadingTitle: '上传 …' + }, + previewZoomButtonTitles: { + prev: '预览上一个文件', + next: '预览下一个文件', + toggleheader: '缩放', + fullscreen: '全屏', + borderless: '无边界模式', + close: '关闭当前预览' + } + }; + + $.fn.fileinput.Constructor = FileInput; + + /** + * Convert automatically file inputs with class 'file' into a bootstrap fileinput control. + */ + $(document).ready(function () { + var $input = $('input.file[type=file]'); + if ($input.length) { + $input.fileinput(); + } + }); +})); diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.css new file mode 100644 index 000000000..0642fc086 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.css @@ -0,0 +1,12 @@ +/*! + * bootstrap-fileinput v5.2.3 + * http://plugins.krajee.com/file-input + * + * Krajee default styling for bootstrap-fileinput. + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2021, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */.btn-file input[type=file],.file-caption-icon,.file-no-browse,.file-preview .fileinput-remove,.file-zoom-dialog .btn-navigate,.file-zoom-dialog .floating-buttons,.krajee-default .file-thumb-progress{position:absolute}.file-loading input[type=file],input[type=file].file-loading{width:0;height:0}.file-no-browse{left:50%;bottom:20%;width:1px;height:1px;font-size:0;opacity:0;border:none;background:0 0;outline:0;box-shadow:none}.file-caption-icon,.file-input-ajax-new .fileinput-remove-button,.file-input-ajax-new .fileinput-upload-button,.file-input-ajax-new .no-browse .input-group-btn,.file-input-new .close,.file-input-new .file-preview,.file-input-new .fileinput-remove-button,.file-input-new .fileinput-upload-button,.file-input-new .glyphicon-file,.file-input-new .no-browse .input-group-btn,.file-zoom-dialog .modal-header:after,.file-zoom-dialog .modal-header:before,.hide-content .kv-file-content,.is-locked .fileinput-remove-button,.is-locked .fileinput-upload-button,.kv-hidden{display:none}.file-caption-icon .kv-caption-icon{line-height:inherit}.btn-file,.file-caption,.file-input,.file-loading:before,.file-preview,.file-zoom-dialog .modal-dialog,.krajee-default .file-thumbnail-footer,.krajee-default.file-preview-frame{position:relative}.file-error-message pre,.file-error-message ul,.krajee-default .file-actions,.krajee-default .file-other-error{text-align:left}.file-error-message pre,.file-error-message ul{margin:0}.krajee-default .file-drag-handle,.krajee-default .file-upload-indicator{float:left;margin-top:10px;width:16px;height:16px}.file-thumb-progress .progress,.file-thumb-progress .progress-bar{font-family:Verdana,Helvetica,sans-serif;font-size:.7rem}.krajee-default .file-thumb-progress .progress,.kv-upload-progress .progress{background-color:#ccc}.krajee-default .file-caption-info,.krajee-default .file-size-info{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;width:160px;height:15px;margin:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-image,.file-zoom-content>.file-object.type-video{max-width:100%;max-height:100%;width:auto}.file-zoom-content>.file-object.type-flash,.file-zoom-content>.file-object.type-video{height:100%}.file-zoom-content>.file-object.type-default,.file-zoom-content>.file-object.type-html,.file-zoom-content>.file-object.type-pdf,.file-zoom-content>.file-object.type-text{width:100%}.file-loading:before{content:" Loading...";display:inline-block;padding-left:20px;line-height:16px;font-size:13px;font-variant:small-caps;color:#999;background:url(loading.gif) top left no-repeat}.file-object{margin:0 0 -5px;padding:0}.btn-file{overflow:hidden}.btn-file input[type=file]{top:0;left:0;min-width:100%;min-height:100%;text-align:right;opacity:0;background:none;cursor:inherit;display:block}.btn-file ::-ms-browse{font-size:10000px;width:100%;height:100%}.file-caption.icon-visible .file-caption-icon{display:inline-block}.file-caption.icon-visible .file-caption-name{padding-left:1.875rem}.file-caption.icon-visible>.input-group-lg .file-caption-name{padding-left:2.1rem}.file-caption.icon-visible>.input-group-sm .file-caption-name{padding-left:1.5rem}.file-caption-name:not(.file-caption-disabled){background-color:transparent}.file-caption-name.file-processing{font-style:italic;border-color:#bbb;opacity:.5}.file-caption-icon{padding:.5rem;left:4px}.input-group-lg .file-caption-icon{font-size:1.25rem}.input-group-sm .file-caption-icon{font-size:.875rem;padding:.25rem}.file-error-message{color:#a94442;background-color:#f2dede;margin:5px;border:1px solid #ebccd1;border-radius:4px;padding:15px}.file-error-message pre{margin:5px 0}.file-caption-disabled{background-color:#eee;cursor:not-allowed;opacity:1}.file-preview{border-radius:5px;border:1px solid #ddd;padding:8px;width:100%;margin-bottom:5px}.file-preview .btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.file-preview .fileinput-remove{top:1px;right:1px;line-height:10px}.file-preview .clickable{cursor:pointer}.file-preview-image{font:40px Impact,Charcoal,sans-serif;color:green;width:auto;height:auto;max-width:100%;max-height:100%}.krajee-default.file-preview-frame{margin:8px;border:1px solid rgba(0,0,0,.2);box-shadow:0 0 10px 0 rgba(0,0,0,.2);padding:6px;float:left;text-align:center}.krajee-default.file-preview-frame .kv-file-content{width:213px;height:160px}.krajee-default .file-preview-other-frame{display:flex;align-items:center;justify-content:center}.krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered{width:400px}.krajee-default.file-preview-frame[data-template=audio] .kv-file-content{width:240px;height:55px}.krajee-default.file-preview-frame .file-thumbnail-footer{height:70px}.krajee-default.file-preview-frame:not(.file-preview-error):hover{border:1px solid rgba(0,0,0,.3);box-shadow:0 0 10px 0 rgba(0,0,0,.4)}.krajee-default .file-preview-text{color:#428bca;border:1px solid #ddd;outline:0;resize:none}.krajee-default .file-preview-html{border:1px solid #ddd}.krajee-default .file-other-icon{font-size:6em;line-height:1}.krajee-default .file-footer-buttons{float:right}.krajee-default .file-footer-caption{display:block;text-align:center;padding-top:4px;font-size:11px;color:#777;margin-bottom:30px}.file-upload-stats{font-size:10px;text-align:center;width:100%}.kv-upload-progress .file-upload-stats{font-size:12px;margin:-10px 0 5px}.krajee-default .file-preview-error{opacity:.65;box-shadow:none}.krajee-default .file-thumb-progress{top:37px;left:0;right:0}.krajee-default.kvsortable-ghost{background:#e1edf7;border:2px solid #a1abff}.krajee-default .file-preview-other:hover{opacity:.8}.krajee-default .file-preview-frame:not(.file-preview-error) .file-footer-caption:hover{color:#000}.kv-upload-progress .progress{height:20px;margin:10px 0;overflow:hidden}.kv-upload-progress .progress-bar{height:20px;font-family:Verdana,Helvetica,sans-serif}.file-zoom-dialog .file-other-icon{font-size:22em;font-size:50vmin}.file-zoom-dialog .modal-dialog{width:auto}.file-zoom-dialog .modal-header{display:flex;align-items:center;justify-content:space-between}.file-zoom-dialog .btn-navigate{margin:0 .1rem;padding:0;font-size:1.2rem;width:2.4rem;height:2.4rem;top:50%;border-radius:50%;text-align:center}.btn-navigate *{width:auto}.file-zoom-dialog .floating-buttons{top:5px;right:10px}.file-zoom-dialog .btn-kv-prev{left:0}.file-zoom-dialog .btn-kv-next{right:0}.file-zoom-dialog .kv-zoom-caption{max-width:50%;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.file-zoom-dialog .kv-zoom-header{padding:.5rem}.file-zoom-dialog .kv-zoom-body{padding:.25rem .5rem .25rem 0}.file-zoom-dialog .kv-zoom-description{position:absolute;opacity:.8;font-size:.8rem;background-color:#1a1a1a;padding:1rem;text-align:center;border-radius:.5rem;color:#fff;left:15%;right:15%;bottom:15%}.file-zoom-dialog .kv-desc-hide{float:right;color:#fff;padding:0 .1rem;background:0 0;border:none}.file-zoom-dialog .kv-desc-hide:hover{opacity:.7}.file-zoom-dialog .kv-desc-hide:focus{opacity:.9}.file-input-ajax-new .no-browse .form-control,.file-input-new .no-browse .form-control{border-top-right-radius:4px;border-bottom-right-radius:4px}.file-caption{width:100%;position:relative}.file-thumb-loading{background:url(loading.gif) center center no-repeat content-box!important}.file-drop-zone{border:1px dashed #aaa;min-height:260px;border-radius:4px;text-align:center;vertical-align:middle;margin:12px 15px 12px 12px;padding:5px}.file-drop-zone.clickable:hover{border:2px dashed #999}.file-drop-zone.clickable:focus{border:2px solid #5acde2}.file-drop-zone .file-preview-thumbnails{cursor:default}.file-drop-zone-title{color:#aaa;font-size:1.6em;text-align:center;padding:85px 10px;cursor:default}.file-highlighted{border:2px dashed #999!important;background-color:#eee}.file-uploading{background:url(loading-sm.gif) center bottom 10px no-repeat;opacity:.65}.file-zoom-fullscreen .modal-dialog{min-width:100%;margin:0}.file-zoom-fullscreen .modal-content{border-radius:0;box-shadow:none;min-height:100vh}.file-zoom-fullscreen .kv-zoom-body{overflow-y:auto}.floating-buttons{z-index:3000}.floating-buttons .btn-kv{margin-left:3px;z-index:3000}.kv-zoom-actions .btn-kv{margin-left:3px}.file-zoom-content{text-align:center;white-space:nowrap;min-height:300px}.file-zoom-content:hover{background:0 0}.file-zoom-content>*{display:inline-block;vertical-align:middle}.file-zoom-content .kv-spacer{height:100%}.file-zoom-content .file-preview-image,.file-zoom-content .file-preview-video{max-height:100%}.file-zoom-content>.file-object.type-image{height:auto;min-height:inherit}.file-zoom-content>.file-object.type-audio{width:auto;height:30px}@media (min-width:576px){.file-zoom-dialog .modal-dialog{max-width:500px}}@media (min-width:992px){.file-zoom-dialog .modal-lg{max-width:800px}}@media (max-width:767px){.file-preview-thumbnails{display:flex;justify-content:center;align-items:center;flex-direction:column}.file-zoom-dialog .modal-header{flex-direction:column}}@media (max-width:350px){.krajee-default.file-preview-frame:not([data-template=audio]) .kv-file-content{width:160px}}@media (max-width:420px){.krajee-default.file-preview-frame .kv-file-content.kv-pdf-rendered{width:100%}}.file-loading[dir=rtl]:before{background:url(loading.gif) top right no-repeat;padding-left:0;padding-right:20px}.clickable .file-drop-zone-title{cursor:pointer}.file-sortable .file-drag-handle:hover{opacity:.7}.file-sortable .file-drag-handle{cursor:grab;opacity:1}.file-grabbing,.file-grabbing *{cursor:not-allowed!important}.file-grabbing .file-preview-thumbnails *{cursor:grabbing!important}.file-preview-frame.sortable-chosen{background-color:#d9edf7;border-color:#17a2b8;box-shadow:none!important}.file-preview .kv-zoom-cache{display:none} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.js new file mode 100644 index 000000000..f6ef05dde --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/fileinput.min.js @@ -0,0 +1,494 @@ +/*! + * bootstrap-fileinput v5.2.3 + * http://plugins.krajee.com/file-input + * + * Author: Kartik Visweswaran + * Copyright: 2014 - 2021, Kartik Visweswaran, Krajee.com + * + * Licensed under the BSD-3-Clause + * https://github.com/kartik-v/bootstrap-fileinput/blob/master/LICENSE.md + */ +!function(e){"use strict" +"function"==typeof define&&define.amd?define(["jquery"],e):"object"==typeof module&&module.exports?module.exports=e(require("jquery")):e(window.jQuery)}(function(e){"use strict" +e.fn.fileinputLocales={},e.fn.fileinputThemes={},e.fn.fileinputBsVersion||(e.fn.fileinputBsVersion=window.Alert&&window.Alert.VERSION||window.bootstrap&&window.bootstrap.Alert&&bootstrap.Alert.VERSION||"3.x.x"),String.prototype.setTokens=function(e){var t,i,a=""+this +for(t in e)e.hasOwnProperty(t)&&(i=RegExp("{"+t+"}","g"),a=a.replace(i,e[t])) +return a},Array.prototype.flatMap||(Array.prototype.flatMap=function(e){return[].concat(this.map(e))}) +var t,i +t={FRAMES:".kv-preview-thumb",SORT_CSS:"file-sortable",INIT_FLAG:"init-",OBJECT_PARAMS:'\n\n\n\n\n\n',DEFAULT_PREVIEW:'
    \n{previewFileIcon}\n
    ',MODAL_ID:"kvFileinputModal",MODAL_EVENTS:["show","shown","hide","hidden","loaded"],logMessages:{ajaxError:"{status}: {error}. Error Details: {text}.",badDroppedFiles:"Error scanning dropped files!",badExifParser:"Error loading the piexif.js library. {details}",badInputType:'The input "type" must be set to "file" for initializing the "bootstrap-fileinput" plugin.',exifWarning:'To avoid this warning, either set "autoOrientImage" to "false" OR ensure you have loaded the "piexif.js" library correctly on your page before the "fileinput.js" script.',invalidChunkSize:'Invalid upload chunk size: "{chunkSize}". Resumable uploads are disabled.',invalidThumb:'Invalid thumb frame with id: "{id}".',noResumableSupport:"The browser does not support resumable or chunk uploads.",noUploadUrl:'The "uploadUrl" is not set. Ajax uploads and resumable uploads have been disabled.',retryStatus:"Retrying upload for chunk # {chunk} for {filename}... retry # {retry}.",chunkQueueError:"Could not push task to ajax pool for chunk index # {index}.",resumableMaxRetriesReached:"Maximum resumable ajax retries ({n}) reached.",resumableRetryError:"Could not retry the resumable request (try # {n})... aborting.",resumableAborting:"Aborting / cancelling the resumable request.",resumableRequestError:"Error processing resumable request. {msg}"},objUrl:window.URL||window.webkitURL,isBs:function(t){var i=e.trim((e.fn.fileinputBsVersion||"")+"") +return t=parseInt(t,10),i?t===parseInt(i.charAt(0),10):4===t},defaultButtonCss:function(e){return"btn-default btn-"+(e?"":"outline-")+"secondary"},now:function(){return(new Date).getTime()},round:function(e){return e=parseFloat(e),isNaN(e)?0:Math.floor(Math.round(e))},getArray:function(e){var t,i=[],a=e&&e.length||0 +for(t=0;a>t;t++)i.push(e[t]) +return i},getFileRelativePath:function(e){return(e.newPath||e.relativePath||e.webkitRelativePath||t.getFileName(e)||null)+""},getFileId:function(e,i){var a=t.getFileRelativePath(e) +return"function"==typeof i?i(e):e&&a?e.size+"_"+encodeURIComponent(a).replace(/%/g,"_"):null},getFrameSelector:function(e,t){return t=t||"",'[id="'+e+'"]'+t},getZoomSelector:function(e,i){return t.getFrameSelector("zoom-"+e,i)},getFrameElement:function(e,i,a){return e.find(t.getFrameSelector(i,a))},getZoomElement:function(e,i,a){return e.find(t.getZoomSelector(i,a))},getElapsed:function(i){var a=i,r="",n={},o={year:31536e3,month:2592e3,week:604800,day:86400,hour:3600,minute:60,second:1} +return t.getObjectKeys(o).forEach(function(e){n[e]=Math.floor(a/o[e]),a-=n[e]*o[e]}),e.each(n,function(e,t){t>0&&(r+=(r?" ":"")+t+e.substring(0,1))}),r},debounce:function(e,t){var i +return function(){var a=arguments,r=this +clearTimeout(i),i=setTimeout(function(){e.apply(r,a)},t)}},stopEvent:function(e){e.stopPropagation(),e.preventDefault()},getFileName:function(e){return e?e.fileName||e.name||"":""},createObjectURL:function(e){return t.objUrl&&t.objUrl.createObjectURL&&e?t.objUrl.createObjectURL(e):""},revokeObjectURL:function(e){t.objUrl&&t.objUrl.revokeObjectURL&&e&&t.objUrl.revokeObjectURL(e)},compare:function(e,t,i){return void 0!==e&&(i?e===t:e.match(t))},isIE:function(e){var t,i +return"Microsoft Internet Explorer"!==navigator.appName?!1:10===e?RegExp("msie\\s"+e,"i").test(navigator.userAgent):(t=document.createElement("div"),t.innerHTML="",i=t.getElementsByTagName("i").length,document.body.appendChild(t),t.parentNode.removeChild(t),i)},canOrientImage:function(t){var i=e(document.createElement("img")).css({width:"1px",height:"1px"}).insertAfter(t),a=i.css("image-orientation") +return i.remove(),!!a},canAssignFilesToInput:function(){var e=document.createElement("input") +try{return e.type="file",e.files=null,!0}catch(t){return!1}},getDragDropFolders:function(e){var t,i,a=e?e.length:0,r=0 +if(a>0&&e[0].webkitGetAsEntry())for(t=0;a>t;t++)i=e[t].webkitGetAsEntry(),i&&i.isDirectory&&r++ +return r},initModal:function(t){var i=e("body") +i.length&&t.appendTo(i)},isFunction:function(e){return"function"==typeof e},isEmpty:function(i,a){return void 0===i||null===i||""===i?!0:t.isString(i)&&a?""===e.trim(i):t.isArray(i)?0===i.length:e.isPlainObject(i)&&e.isEmptyObject(i)?!0:!1},isArray:function(e){return Array.isArray(e)||"[object Array]"===Object.prototype.toString.call(e)},isString:function(e){return"[object String]"===Object.prototype.toString.call(e)},ifSet:function(e,t,i){return i=i||"",t&&"object"==typeof t&&e in t?t[e]:i},cleanArray:function(e){return e instanceof Array||(e=[]),e.filter(function(e){return void 0!==e&&null!==e})},spliceArray:function(t,i,a){var r,n,o=0,s=[] +if(!(t instanceof Array))return[] +for(n=e.extend(!0,[],t),a&&n.reverse(),r=0;r=0?atob(e.split(",")[1]):decodeURIComponent(e.split(",")[1]),a=new ArrayBuffer(i.length),r=new Uint8Array(a),n=0;ns;)switch(i=n[s++],i>>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:o+=String.fromCharCode(i) +break +case 12:case 13:a=n[s++],o+=String.fromCharCode((31&i)<<6|63&a) +break +case 14:a=n[s++],r=n[s++],o+=String.fromCharCode((15&i)<<12|(63&a)<<6|(63&r)<<0)}return o},isHtml:function(e){var t=document.createElement("div") +t.innerHTML=e +for(var i=t.childNodes,a=i.length;a--;)if(1===i[a].nodeType)return!0 +return!1},isSvg:function(e){return e.match(/^\s*<\?xml/i)&&(e.match(/"+t+""))},uniqId:function(){return((new Date).getTime()+Math.floor(Math.random()*Math.pow(10,15))).toString(36)},cspBuffer:{CSP_ATTRIB:"data-csp-01928735",domElementsStyles:{},stash:function(i){var a=this,r=e.parseHTML("
    "+i+"
    "),n=e(r) +n.find("[style]").each(function(i,r){var n=e(r),o=n[0].style,s=t.uniqId(),l={} +o&&o.length&&(e(o).each(function(){l[this]=o[this]}),a.domElementsStyles[s]=l,n.removeAttr("style").attr(a.CSP_ATTRIB,s))}),n.filter("*").removeAttr("style") +var o=Object.values?Object.values(r):Object.keys(r).map(function(e){return r[e]}) +return o.flatMap(function(e){return e.innerHTML}).join("")},apply:function(t){var i=this,a=e(t) +a.find("["+i.CSP_ATTRIB+"]").each(function(t,a){var r=e(a),n=r.attr(i.CSP_ATTRIB),o=i.domElementsStyles[n] +o&&r.css(o),r.removeAttr(i.CSP_ATTRIB)}),i.domElementsStyles={}}},setHtml:function(e,i){var a=t.cspBuffer +return e.html(a.stash(i)),a.apply(e),e},htmlEncode:function(e,t){return void 0===e?t||null:e.replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")},replaceTags:function(t,i){var a=t +return i?(e.each(i,function(e,t){"function"==typeof t&&(t=t()),a=a.split(e).join(t)}),a):a},cleanMemory:function(e){var i=e.is("img")?e.attr("src"):e.find("source").attr("src") +t.revokeObjectURL(i)},findFileName:function(e){var t=e.lastIndexOf("/") +return-1===t&&(t=e.lastIndexOf("\\")),e.split(e.substring(t,t+1)).pop()},checkFullScreen:function(){return document.fullscreenElement||document.mozFullScreenElement||document.webkitFullscreenElement||document.msFullscreenElement},toggleFullScreen:function(e){var i=document,a=i.documentElement,r=t.checkFullScreen() +a&&e&&!r?a.requestFullscreen?a.requestFullscreen():a.msRequestFullscreen?a.msRequestFullscreen():a.mozRequestFullScreen?a.mozRequestFullScreen():a.webkitRequestFullscreen&&a.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT):r&&(i.exitFullscreen?i.exitFullscreen():i.msExitFullscreen?i.msExitFullscreen():i.mozCancelFullScreen?i.mozCancelFullScreen():i.webkitExitFullscreen&&i.webkitExitFullscreen())},moveArray:function(t,i,a,r){var n=e.extend(!0,[],t) +if(r&&n.reverse(),a>=n.length)for(var o=a-n.length;o--+1;)n.push(void 0) +return n.splice(a,0,n.splice(i,1)[0]),r&&n.reverse(),n},closeButton:function(e){return e=(t.isBs(5)?"btn-close":"close")+(e?" "+e:""),'"},getRotation:function(e){switch(e){case 2:return"rotateY(180deg)" +case 3:return"rotate(180deg)" +case 4:return"rotate(180deg) rotateY(180deg)" +case 5:return"rotate(270deg) rotateY(180deg)" +case 6:return"rotate(90deg)" +case 7:return"rotate(90deg) rotateY(180deg)" +case 8:return"rotate(270deg)" +default:return""}},setTransform:function(e,t){e&&(e.style.transform=t,e.style.webkitTransform=t,e.style["-moz-transform"]=t,e.style["-ms-transform"]=t,e.style["-o-transform"]=t)},getObjectKeys:function(t){var i=[] +return t&&e.each(t,function(e){i.push(e)}),i},getObjectSize:function(e){return t.getObjectKeys(e).length},whenAll:function(i){var a,r,n,o,s,l,d=[].slice,c=1===arguments.length&&t.isArray(i)?i:d.call(arguments),u=e.Deferred(),p=0,f=c.length,g=f +for(n=o=s=Array(f),l=function(e,t,i){return function(){i!==c&&p++,u.notifyWith(t[e]=this,i[e]=d.call(arguments)),--g||u[(p?"reject":"resolve")+"With"](t,i)}},a=0;f>a;a++)(r=c[a])&&e.isFunction(r.promise)?r.promise().done(l(a,s,c)).fail(l(a,n,o)):(u.notifyWith(this,r),--g) +return g||u.resolveWith(s,c),u.promise()}},i=function(i,a){var r=this +r.$element=e(i),r.$parent=r.$element.parent(),r._validate()&&(r.isPreviewable=t.hasFileAPISupport(),r.isIE9=t.isIE(9),r.isIE10=t.isIE(10),(r.isPreviewable||r.isIE9)&&(r._init(a),r._listen()),r.$element.removeClass("file-loading"))},i.prototype={constructor:i,_cleanup:function(){var e=this +e.reader=null,e.clearFileStack(),e.fileBatchCompleted=!0,e.isError=!1,e.isDuplicateError=!1,e.isPersistentError=!1,e.cancelling=!1,e.paused=!1,e.lastProgress=0,e._initAjax()},_isAborted:function(){var e=this +return e.cancelling||e.paused},_initAjax:function(){var i=this,a=i.taskManager={pool:{},addPool:function(e){return a.pool[e]=new a.TasksPool(e)},getPool:function(e){return a.pool[e]},addTask:function(e,t){return new a.Task(e,t)},TasksPool:function(i){var r=this +r.id=i,r.cancelled=!1,r.cancelledDeferrer=e.Deferred(),r.tasks={},r.addTask=function(e,t){return r.tasks[e]=new a.Task(e,t)},r.size=function(){return t.getObjectSize(r.tasks)},r.run=function(i){var a,n,o,s=0,l=!1,d=t.getObjectKeys(r.tasks).map(function(e){return r.tasks[e]}),c=[],u=e.Deferred() +if(r.cancelled)return r.cancelledDeferrer.resolve(),u.reject() +if(!i){var p=t.getObjectKeys(r.tasks).map(function(e){return r.tasks[e].deferred}) +return t.whenAll(p).done(function(){var e=t.getArray(arguments) +r.cancelled?(u.reject.apply(null,e),r.cancelledDeferrer.resolve()):(u.resolve.apply(null,e),r.cancelledDeferrer.reject())}).fail(function(){var e=t.getArray(arguments) +u.reject.apply(null,e),r.cancelled?r.cancelledDeferrer.resolve():r.cancelledDeferrer.reject()}),e.each(r.tasks,function(e){a=r.tasks[e],a.run()}),u}for(n=function(t){e.when(t.deferred).fail(function(){l=!0,o.apply(null,arguments)}).always(o)},o=function(){var e=t.getArray(arguments) +return u.notify(e),c.push(e),r.cancelled?(u.reject.apply(null,c),void r.cancelledDeferrer.resolve()):(c.length===r.size()&&(l?u.reject.apply(null,c):u.resolve.apply(null,c)),void(d.length&&(a=d.shift(),n(a),a.run())))};d.length&&s++0&&l.maxTotalFileCount10?t-10:Math.ceil(t/2),e=t;e>i;e--)r=parseFloat(o.bpsLog[e]),a++ +o.bps=64*(a>0?r/a:0)},u),n={fileId:e,started:s,elapsed:l,loaded:a,total:r,bps:o.bps,bitrate:i._getSize(o.bps,i.bitRateUnits),pendingBytes:c},e?o.stats[e]=n:o.stats=n,n},exists:function(t){return-1!==e.inArray(t,i.fileManager.getIdList())},count:function(){return i.fileManager.getIdList().length},total:function(){var e=i.fileManager +return e.totalFiles||(e.totalFiles=e.count()),e.totalFiles},getTotalSize:function(){var t=i.fileManager +return t.totalSize?t.totalSize:(t.totalSize=0,e.each(i.getFileStack(),function(e,i){var a=parseFloat(i.size) +t.totalSize+=isNaN(a)?0:a}),t.totalSize)},add:function(e,a){a||(a=i.fileManager.getId(e)),a&&(i.fileManager.stack[a]={file:e,name:t.getFileName(e),relativePath:t.getFileRelativePath(e),size:e.size,nameFmt:i._getFileName(e,""),sizeFmt:i._getSize(e.size)})},remove:function(e){var t=i._getThumbFileId(e) +i.fileManager.removeFile(t)},removeFile:function(e){var t=i.fileManager +e&&(delete t.stack[e],delete t.loadedImages[e])},move:function(t,a){var r={},n=i.fileManager.stack;(t||a)&&t!==a&&(e.each(n,function(e,i){e!==t&&(r[e]=i),e===a&&(r[t]=n[t])}),i.fileManager.stack=r)},list:function(){var t=[] +return e.each(i.getFileStack(),function(e,i){i&&i.file&&t.push(i.file)}),t},isPending:function(t){return-1===e.inArray(t,i.fileManager.filesProcessed)&&i.fileManager.exists(t)},isProcessed:function(){var t=!0,a=i.fileManager +return e.each(i.getFileStack(),function(e){a.isPending(e)&&(t=!1)}),t},clear:function(){var e=i.fileManager +i.isDuplicateError=!1,i.isPersistentError=!1,e.totalFiles=null,e.totalSize=null,e.uploadedSize=0,e.stack={},e.errors=[],e.filesProcessed=[],e.stats={},e.bpsLog=[],e.bps=0,e.clearImages()},clearImages:function(){i.fileManager.loadedImages={},i.fileManager.totalImages=0},addImage:function(e,t){i.fileManager.loadedImages[e]=t},removeImage:function(e){delete i.fileManager.loadedImages[e]},getImageIdList:function(){return t.getObjectKeys(i.fileManager.loadedImages)},getImageCount:function(){return i.fileManager.getImageIdList().length},getId:function(e){return i._getFileId(e)},getIndex:function(e){return i.fileManager.getIdList().indexOf(e)},getThumb:function(t){var a=null +return i._getThumbs().each(function(){var r=e(this) +i._getThumbFileId(r)===t&&(a=r)}),a},getThumbIndex:function(e){var t=i._getThumbFileId(e) +return i.fileManager.getIndex(t)},getIdList:function(){return t.getObjectKeys(i.fileManager.stack)},getFile:function(e){return i.fileManager.stack[e]||null},getFileName:function(e,t){var a=i.fileManager.getFile(e) +return a?t?a.nameFmt||"":a.name||"":""},getFirstFile:function(){var e=i.fileManager.getIdList(),t=e&&e.length?e[0]:null +return i.fileManager.getFile(t)},setFile:function(e,t){i.fileManager.getFile(e)?i.fileManager.stack[e].file=t:i.fileManager.add(t,e)},setProcessed:function(e){i.fileManager.filesProcessed.push(e)},getProgress:function(){var e=i.fileManager.total(),t=i.fileManager.filesProcessed.length +return e?Math.ceil(t/e*100):0},setProgress:function(e,t){var a=i.fileManager.getFile(e) +!isNaN(t)&&a&&(a.progress=t)}}},_setUploadData:function(i,a){var r=this +e.each(a,function(e,a){var n=r.uploadParamNames[e]||e +t.isArray(a)?i.append(n,a[0],a[1]):i.append(n,a)})},_initResumableUpload:function(){var i,a=this,r=a.resumableUploadOptions,n=t.logMessages,o=a.fileManager +if(a.enableResumableUpload){if(r.fallback!==!1&&"function"!=typeof r.fallback&&(r.fallback=function(e){e._log(n.noResumableSupport),e.enableResumableUpload=!1}),!t.hasResumableUploadSupport()&&r.fallback!==!1)return void r.fallback(a) +if(!a.uploadUrl&&a.enableResumableUpload)return a._log(n.noUploadUrl),void(a.enableResumableUpload=!1) +if(r.chunkSize=parseFloat(r.chunkSize),r.chunkSize<=0||isNaN(r.chunkSize))return a._log(n.invalidChunkSize,{chunkSize:r.chunkSize}),void(a.enableResumableUpload=!1) +i=a.resumableManager={init:function(e,t,n){i.logs=[],i.stack=[],i.error="",i.id=e,i.file=t.file,i.fileName=t.name,i.fileIndex=n,i.completed=!1,i.lastProgress=0,a.showPreview&&(i.$thumb=o.getThumb(e)||null,i.$progress=i.$btnDelete=null,i.$thumb&&i.$thumb.length&&(i.$progress=i.$thumb.find(".file-thumb-progress"),i.$btnDelete=i.$thumb.find(".kv-file-remove"))),i.chunkSize=r.chunkSize*a.bytesToKB,i.chunkCount=i.getTotalChunks()},setAjaxError:function(e,t,o,s){e.responseJSON&&e.responseJSON.error&&(o=""+e.responseJSON.error),s||(i.error=o),r.showErrorLog&&a._log(n.ajaxError,{status:e.status,error:o,text:e.responseText||""})},reset:function(){i.stack=[],i.chunksProcessed={}},setProcessed:function(t){var n,s,l=i.id,d=i.$thumb,c=i.$progress,u=d&&d.length,p={id:u?d.attr("id"):"",index:o.getIndex(l),fileId:l},f=a.resumableUploadOptions.skipErrorsAndProceed +i.completed=!0,i.lastProgress=0,u&&d.removeClass("file-uploading"),"success"===t?(o.uploadedSize+=i.file.size,a.showPreview&&(a._setProgress(101,c),a._setThumbStatus(d,"Success"),a._initUploadSuccess(i.chunksProcessed[l].data,d)),o.removeFile(l),delete i.chunksProcessed[l],a._raise("fileuploaded",[p.id,p.index,p.fileId]),o.isProcessed()&&a._setProgress(101)):"cancel"!==t&&(a.showPreview&&(a._setThumbStatus(d,"Error"),a._setPreviewError(d,!0),a._setProgress(101,c,a.msgProgressError),a._setProgress(101,a.$progress,a.msgProgressError),a.cancelling=!f),a.$errorContainer.find('li[data-file-id="'+p.fileId+'"]').length||(s={file:i.fileName,max:r.maxRetries,error:i.error},n=a.msgResumableUploadRetriesExceeded.setTokens(s),e.extend(p,s),a._showFileError(n,p,"filemaxretries"),f&&(o.removeFile(l),delete i.chunksProcessed[l],o.isProcessed()&&a._setProgress(101)))),o.isProcessed()&&i.reset()},check:function(){var t=!0 +e.each(i.logs,function(e,i){return i?void 0:(t=!1,!1)})},processedResumables:function(){var e,t=i.logs,a=0 +if(!t||!t.length)return 0 +for(e=0;ei.file.size?i.file.size:e},getTotalChunks:function(){var e=parseFloat(i.chunkSize) +return!isNaN(e)&&e>0?Math.ceil(i.file.size/e):0},getProgress:function(){var e=i.processedResumables(),t=i.chunkCount +return 0===t?0:Math.ceil(e/t*100)},checkAborted:function(e){a._isAborted()&&(clearInterval(e),a.unlock())},upload:function(){var e,r=o.getIdList(),n="new" +e=setInterval(function(){var s +if(i.checkAborted(e),"new"===n&&(a.lock(),n="processing",s=r.shift(),o.initStats(s),o.stack[s]&&(i.init(s,o.stack[s],o.getIndex(s)),i.processUpload())),!o.isPending(s)&&i.completed&&(n="new"),o.isProcessed()){var l=a.$preview.find(".file-preview-initial") +l.length&&(t.addCss(l,t.SORT_CSS),a._initSortable()),clearInterval(e),a._clearFileInput(),a.unlock(),setTimeout(function(){var e=a.previewCache.data +e&&(a.initialPreview=e.content,a.initialPreviewConfig=e.config,a.initialPreviewThumbTags=e.tags),a._raise("filebatchuploadcomplete",[a.initialPreview,a.initialPreviewConfig,a.initialPreviewThumbTags,a._getExtraData()])},a.processDelay)}},a.processDelay)},uploadResumable:function(){var e,t,n=a.taskManager,o=i.chunkCount +for(t=n.addPool(i.id),e=0;o>e;e++)i.logs[e]=!(!i.chunksProcessed[i.id]||!i.chunksProcessed[i.id][e]),i.logs[e]||i.pushAjax(e,0) +t.run(r.maxThreads).done(function(){i.setProcessed("success")}).fail(function(){i.setProcessed(t.cancelled?"cancel":"error")})},processUpload:function(){var n,s,l,d,c,u,p,f=i.id +return r.testUrl?(n=new FormData,s=o.stack[f],a._setUploadData(n,{fileId:f,fileName:s.fileName,fileSize:s.size,fileRelativePath:s.relativePath,chunkSize:i.chunkSize,chunkCount:i.chunkCount}),l=function(e){p=a._getOutData(n,e),a._raise("filetestbeforesend",[f,o,i,p])},d=function(r,s,l){p=a._getOutData(n,l,r) +var d=a.uploadParamNames,c=d.chunksUploaded||"chunksUploaded",u=[f,o,i,p] +r[c]&&t.isArray(r[c])?(i.chunksProcessed[f]||(i.chunksProcessed[f]={}),e.each(r[c],function(e,t){i.logs[t]=!0,i.chunksProcessed[f][t]=!0}),i.chunksProcessed[f].data=r,a._raise("filetestsuccess",u)):a._raise("filetesterror",u),i.uploadResumable()},c=function(e,t,r){p=a._getOutData(n,e),a._raise("filetestajaxerror",[f,o,i,p]),i.setAjaxError(e,t,r,!0),i.uploadResumable()},u=function(){a._raise("filetestcomplete",[f,o,i,a._getOutData(n)])},void a._ajaxSubmit(l,d,u,c,n,f,i.fileIndex,r.testUrl)):void i.uploadResumable()},pushAjax:function(e,t){var r=a.taskManager,o=r.getPool(i.id) +o.addTask(o.size()+1,function(e){var t,r=i.stack.shift() +t=r[0],i.chunksProcessed[i.id]&&i.chunksProcessed[i.id][t]?a._log(n.chunkQueueError,{index:t}):i.sendAjax(t,r[1],e)}),i.stack.push([e,t])},sendAjax:function(e,s,l){var d,c=i.chunkSize,u=i.id,p=i.file,f=i.$thumb,g=t.logMessages,m=i.$btnDelete,h=function(e,t){t&&(e=e.setTokens(t)),e=g.resumableRequestError.setTokens({msg:e}),a._log(e),l.reject(e)} +if(!i.chunksProcessed[u]||!i.chunksProcessed[u][e]){if(s>r.maxRetries)return h(g.resumableMaxRetriesReached,{n:r.maxRetries}),void i.setProcessed("error") +var v,w,b,_,C,y,x=p.slice?"slice":p.mozSlice?"mozSlice":p.webkitSlice?"webkitSlice":"slice",T=p[x](c*e,c*(e+1)) +v=new FormData,d=o.stack[u],a._setUploadData(v,{chunkCount:i.chunkCount,chunkIndex:e,chunkSize:c,chunkSizeStart:c*e,fileBlob:[T,i.fileName],fileId:u,fileName:i.fileName,fileRelativePath:d.relativePath,fileSize:p.size,retryCount:s}),i.$progress&&i.$progress.length&&i.$progress.show(),b=function(r){w=a._getOutData(v,r),a.showPreview&&(f.hasClass("file-preview-success")||(a._setThumbStatus(f,"Loading"),t.addCss(f,"file-uploading")),m.attr("disabled",!0)),a._raise("filechunkbeforesend",[u,e,s,o,i,w])},_=function(t,d,c){if(a._isAborted())return void h(g.resumableAborting) +w=a._getOutData(v,c,t) +var p=a.uploadParamNames,f=p.chunkIndex||"chunkIndex",m=[u,e,s,o,i,w] +t.error?(r.showErrorLog&&a._log(n.retryStatus,{retry:s+1,filename:i.fileName,chunk:e}),a._raise("filechunkerror",m),i.pushAjax(e,s+1),i.error=t.error,h(t.error)):(i.logs[t[f]]=!0,i.chunksProcessed[u]||(i.chunksProcessed[u]={}),i.chunksProcessed[u][t[f]]=!0,i.chunksProcessed[u].data=t,l.resolve.call(null,t),a._raise("filechunksuccess",m),i.check())},C=function(t,r,n){return a._isAborted()?void h(g.resumableAborting):(w=a._getOutData(v,t),i.setAjaxError(t,r,n),a._raise("filechunkajaxerror",[u,e,s,o,i,w]),i.pushAjax(e,s+1),void h(g.resumableRetryError,{n:s-1}))},y=function(){a._isAborted()||a._raise("filechunkcomplete",[u,e,s,o,i,a._getOutData(v)])},a._ajaxSubmit(b,_,y,C,v,u,i.fileIndex)}}},i.reset()}},_initTemplateDefaults:function(){var i,a,r,n,o,s,l,d,c,u,p,f,g,m,h,v,w,b,_,C,y,x,T,P,F,k,E,S,I,A,z,D,U,j,$,M,B,R,O,L,N,Z,H,W=this,K=function(e,i){return'\n"+t.DEFAULT_PREVIEW+"\n\n"},q="btn btn-sm btn-kv "+t.defaultButtonCss() +i='{preview}\n
    \n
    \n
    \n {caption}\n\n'+(t.isBs(5)?"":'
    \n')+" {remove}\n {cancel}\n {pause}\n {upload}\n {browse}\n"+(t.isBs(5)?"":"
    \n")+"
    ",a='{preview}\n
    \n
    \n{remove}\n{cancel}\n{upload}\n{browse}\n',r='
    \n {close}
    \n
    \n
    \n
    \n
    \n
    \n
    ',o=t.closeButton("fileinput-remove"),n='',s='\n',l='',d='{icon} {label}',c='
    {icon} {label}
    ',Z=t.MODAL_ID+"Label",u='',p='\n',H='',f='
    \n
    \n {status}\n
    \n
    {stats}',N='
    {pendingTime} {uploadSpeed}
    ',g=" ({sizeText})",m='',h='
    \n \n
    \n{drag}\n
    ',v='\n',w='',b='{downloadIcon}',_='',C='{dragIcon}',y='
    {indicator}
    ',x='
    \n',P=x+' title="{caption}">
    \n',F="
    {footer}\n{zoomCache}
    \n",k="{content}\n",R=" {style}",E=K("html","text/html"),I=K("text","text/plain;charset=UTF-8"),M=K("pdf","application/pdf"),S='{alt}\n",A='",z='",D='\n",U='\n",j='\n",$='\n\n'+t.OBJECT_PARAMS+" "+t.DEFAULT_PREVIEW+"\n\n",B='
    \n"+t.DEFAULT_PREVIEW+"\n
    \n",O='
    {zoomContent}
    ',L={width:"100%",height:"100%","min-height":"480px"},W._isPdfRendered()&&(M=W.pdfRendererTemplate.replace("{renderer}",W._encodeURI(W.pdfRendererUrl))),W.defaults={layoutTemplates:{main1:i,main2:a,preview:r,close:o,fileIcon:n,caption:s,modalMain:u,modal:p,descriptionClose:H,progress:f,stats:N,size:g,footer:m,indicator:y,actions:h,actionDelete:v,actionUpload:w,actionDownload:b,actionZoom:_,actionDrag:C,btnDefault:l,btnLink:d,btnBrowse:c,zoomCache:O},previewMarkupTags:{tagBefore1:T,tagBefore2:P,tagAfter:F},previewContentTemplates:{generic:k,html:E,image:S,text:I,office:A,gdocs:z,video:D,audio:U,flash:j,object:$,pdf:M,other:B},allowedPreviewTypes:["image","html","text","video","audio","flash","pdf","object"],previewTemplates:{},previewSettings:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:{width:"213px",height:"160px"},text:{width:"213px",height:"160px"},office:{width:"213px",height:"160px"},gdocs:{width:"213px",height:"160px"},video:{width:"213px",height:"160px"},audio:{width:"100%",height:"30px"},flash:{width:"213px",height:"160px"},object:{width:"213px",height:"160px"},pdf:{width:"100%",height:"160px",position:"relative"},other:{width:"213px",height:"160px"}},previewSettingsSmall:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:{width:"100%",height:"160px"},text:{width:"100%",height:"160px"},office:{width:"100%",height:"160px"},gdocs:{width:"100%",height:"160px"},video:{width:"100%",height:"auto"},audio:{width:"100%",height:"30px"},flash:{width:"100%",height:"auto"},object:{width:"100%",height:"auto"},pdf:{width:"100%",height:"160px"},other:{width:"100%",height:"160px"}},previewZoomSettings:{image:{width:"auto",height:"auto","max-width":"100%","max-height":"100%"},html:L,text:L,office:{width:"100%",height:"100%","max-width":"100%","min-height":"480px"},gdocs:{width:"100%",height:"100%","max-width":"100%","min-height":"480px"},video:{width:"auto",height:"100%","max-width":"100%"},audio:{width:"100%",height:"30px"},flash:{width:"auto",height:"480px"},object:{width:"auto",height:"100%","max-width":"100%","min-height":"480px"},pdf:L,other:{width:"auto",height:"100%","min-height":"480px"}},mimeTypeAliases:{"video/quicktime":"video/mp4"},fileTypeSettings:{image:function(e,i){return t.compare(e,"image.*")&&!t.compare(e,/(tiff?|wmf)$/i)||t.compare(i,/\.(gif|png|jpe?g)$/i)},html:function(e,i){return t.compare(e,"text/html")||t.compare(i,/\.(htm|html)$/i)},office:function(e,i){return t.compare(e,/(word|excel|powerpoint|office)$/i)||t.compare(i,/\.(docx?|xlsx?|pptx?|pps|potx?)$/i)},gdocs:function(e,i){return t.compare(e,/(word|excel|powerpoint|office|iwork-pages|tiff?)$/i)||t.compare(i,/\.(docx?|xlsx?|pptx?|pps|potx?|rtf|ods|odt|pages|ai|dxf|ttf|tiff?|wmf|e?ps)$/i)},text:function(e,i){return t.compare(e,"text.*")||t.compare(i,/\.(xml|javascript)$/i)||t.compare(i,/\.(txt|md|nfo|ini|json|php|js|css)$/i)},video:function(e,i){return t.compare(e,"video.*")&&(t.compare(e,/(ogg|mp4|mp?g|mov|webm|3gp)$/i)||t.compare(i,/\.(og?|mp4|webm|mp?g|mov|3gp)$/i))},audio:function(e,i){return t.compare(e,"audio.*")&&(t.compare(i,/(ogg|mp3|mp?g|wav)$/i)||t.compare(i,/\.(og?|mp3|mp?g|wav)$/i))},flash:function(e,i){return t.compare(e,"application/x-shockwave-flash",!0)||t.compare(i,/\.(swf)$/i)},pdf:function(e,i){return t.compare(e,"application/pdf",!0)||t.compare(i,/\.(pdf)$/i)},object:function(){return!0},other:function(){return!0}},fileActionSettings:{showRemove:!0,showUpload:!0,showDownload:!0,showZoom:!0,showDrag:!0,removeIcon:'',removeClass:q,removeErrorClass:"btn btn-sm btn-kv btn-danger",removeTitle:"Remove file",uploadIcon:'',uploadClass:q,uploadTitle:"Upload file",uploadRetryIcon:'',uploadRetryTitle:"Retry upload",downloadIcon:'',downloadClass:q,downloadTitle:"Download file",zoomIcon:'',zoomClass:q,zoomTitle:"View Details",dragIcon:'',dragClass:"text-primary",dragTitle:"Move / Rearrange",dragSettings:{},indicatorNew:'',indicatorSuccess:'',indicatorError:'',indicatorLoading:'',indicatorPaused:'',indicatorNewTitle:"Not uploaded yet",indicatorSuccessTitle:"Uploaded",indicatorErrorTitle:"Upload Error",indicatorLoadingTitle:"Uploading …",indicatorPausedTitle:"Upload Paused"}},e.each(W.defaults,function(t,i){return"allowedPreviewTypes"===t?void(void 0===W.allowedPreviewTypes&&(W.allowedPreviewTypes=i)):void(W[t]=e.extend(!0,{},i,W[t]))}),W._initPreviewTemplates()},_initPreviewTemplates:function(){var i,a=this,r=a.previewMarkupTags,n=r.tagAfter +e.each(a.previewContentTemplates,function(e,o){t.isEmpty(a.previewTemplates[e])&&(i=r.tagBefore2,("generic"===e||"image"===e)&&(i=r.tagBefore1),a._isPdfRendered()&&"pdf"===e&&(i=i.replace("kv-file-content","kv-file-content kv-pdf-rendered")),a.previewTemplates[e]=i+o+n)})},_initPreviewCache:function(){var i=this +i.previewCache={data:{},init:function(){var e=i.initialPreview +e.length>0&&!t.isArray(e)&&(e=e.split(i.initialPreviewDelimiter)),i.previewCache.data={content:e,config:i.initialPreviewConfig,tags:i.initialPreviewThumbTags}},count:function(e){if(!i.previewCache.data||!i.previewCache.data.content)return 0 +if(e){var t=i.previewCache.data.content.filter(function(e){return null!==e}) +return t.length}return i.previewCache.data.content.length},get:function(e,a){var r,n,o,s,l,d,c,u=t.INIT_FLAG+e,p=i.previewCache.data,f=p.config[e],g=p.content[e],m=t.ifSet("previewAsData",f,i.initialPreviewAsData),h=f?{title:f.title||null,alt:f.alt||null}:{title:null,alt:null},v=function(e,a,r,n,o,s,l,d){var c=" file-preview-initial "+t.SORT_CSS+(l?" "+l:""),u=i.previewInitId+"-"+s,p=f&&f.fileId||u +return i._generatePreviewTemplate(e,a,r,n,u,p,!1,null,c,o,s,d,h,f&&f.zoomData||a)} +return g&&g.length?(a=void 0===a?!0:a,o=t.ifSet("type",f,i.initialPreviewFileType||"generic"),l=t.ifSet("filename",f,t.ifSet("caption",f)),d=t.ifSet("filetype",f,o),s=i.previewCache.footer(e,a,f&&f.size||null),c=t.ifSet("frameClass",f),r=m?v(o,g,l,d,s,u,c):v("generic",g,l,d,s,u,c,o).setTokens({content:p.content[e]}),p.tags.length&&p.tags[e]&&(r=t.replaceTags(r,p.tags[e])),t.isEmpty(f)||t.isEmpty(f.frameAttr)||(n=t.createElement(r),n.find(".file-preview-initial").attr(f.frameAttr),r=n.html(),n.remove()),r):""},clean:function(e){e.content=t.cleanArray(e.content),e.config=t.cleanArray(e.config),e.tags=t.cleanArray(e.tags),i.previewCache.data=e},add:function(e,a,r,n){var o,s=i.previewCache.data +return e&&e.length?(o=e.length-1,t.isArray(e)||(e=e.split(i.initialPreviewDelimiter)),n&&s.content?(o=s.content.push(e[0])-1,s.config[o]=a,s.tags[o]=r):(s.content=e,s.config=a,s.tags=r),i.previewCache.clean(s),o):0},set:function(e,a,r,n){var o,s,l=i.previewCache.data +if(e&&e.length&&(t.isArray(e)||(e=e.split(i.initialPreviewDelimiter)),s=e.filter(function(e){return null!==e}),s.length)){if(void 0===l.content&&(l.content=[]),void 0===l.config&&(l.config=[]),void 0===l.tags&&(l.tags=[]),n){for(o=0;ot;t++)a=i.previewCache.get(t),r=i.reversePreviewOrder?a+r:r+a +return e=i._getMsgSelected(n),{content:r,caption:e}},footer:function(e,a,r){var n=i.previewCache.data||{} +if(t.isEmpty(n.content))return"";(t.isEmpty(n.config)||t.isEmpty(n.config[e]))&&(n.config[e]={}),a=void 0===a?!0:a +var o,s=n.config[e],l=t.ifSet("caption",s),d=t.ifSet("width",s,"auto"),c=t.ifSet("url",s,!1),u=t.ifSet("key",s,null),p=t.ifSet("fileId",s,null),f=i.fileActionSettings,g=i.initialPreviewShowDelete||!1,m=i.initialPreviewDownloadUrl?i.initialPreviewDownloadUrl+"?key="+u+(p?"&fileId="+p:""):"",h=s.downloadUrl||m,v=s.filename||s.caption||"",w=!!h,b=t.ifSet("showRemove",s,g),_=t.ifSet("showDownload",s,t.ifSet("showDownload",f,w)),C=t.ifSet("showZoom",s,t.ifSet("showZoom",f,!0)),y=t.ifSet("showDrag",s,t.ifSet("showDrag",f,!0)),x=c===!1&&a +return _=_&&s.downloadUrl!==!1&&!!h,o=i._renderFileActions(s,!1,_,b,C,y,x,c,u,!0,h,v),i._getLayoutTemplate("footer").setTokens({progress:i._renderThumbProgress(),actions:o,caption:l,size:i._getSize(r),width:d,indicator:""})}},i.previewCache.init()},_isPdfRendered:function(){var e=this,t=e.usePdfRenderer,i="function"==typeof t?t():!!t +return i&&e.pdfRendererUrl},_handler:function(e,t,i){var a=this,r=a.namespace,n=t.split(" ").join(r+" ")+r +e&&e.length&&e.off(n).on(n,i)},_encodeURI:function(e){var t=this +return t.encodeUrl?encodeURI(e):e},_log:function(e,t){var i=this,a=i.$element.attr("id") +i.showConsoleLogs&&(a&&(e='"'+a+'": '+e),e="bootstrap-fileinput: "+e,"object"==typeof t&&(e=e.setTokens(t)),window.console&&void 0!==window.console.log?window.console.log(e):window.alert(e))},_validate:function(){var e=this,i="file"===e.$element.attr("type") +return i||e._log(t.logMessages.badInputType),i},_errorsExist:function(){var i,a=this,r=a.$errorContainer.find("li") +return r.length?!0:(i=t.createElement(a.$errorContainer.html()),i.find(".kv-error-close").remove(),i.find("ul").remove(),!!e.trim(i.text()).length)},_errorHandler:function(e,t){var i=this,a=e.target.error,r=function(e){i._showError(e.replace("{name}",t))} +r(a.code===a.NOT_FOUND_ERR?i.msgFileNotFound:a.code===a.SECURITY_ERR?i.msgFileSecured:a.code===a.NOT_READABLE_ERR?i.msgFileNotReadable:a.code===a.ABORT_ERR?i.msgFilePreviewAborted:i.msgFilePreviewError)},_addError:function(e){var i=this,a=i.$errorContainer +e&&a.length&&(t.setHtml(a,i.errorCloseButton+e),i._handler(a.find(".kv-error-close"),"click",function(){setTimeout(function(){i.showPreview&&!i.getFrames().length&&i.clear(),a.fadeOut("slow")},i.processDelay)}))},_setValidationError:function(e){var i=this +e=(e?e+" ":"")+"has-error",i.$container.removeClass(e).addClass("has-error"),t.addCss(i.$caption,"is-invalid")},_resetErrors:function(e){var t=this,i=t.$errorContainer,a=t.resumableUploadOptions.retainErrorHistory +t.isPersistentError||t.enableResumableUpload&&a||(t.isError=!1,t.$container.removeClass("has-error"),t.$caption.removeClass("is-invalid is-valid file-processing"),i.html(""),e?i.fadeOut("slow"):i.hide())},_showFolderError:function(e){var t,i=this,a=i.$errorContainer +e&&(i.isAjaxUpload||i._clearFileInput(),t=i.msgFoldersNotAllowed.replace("{n}",e),i._addError(t),i._setValidationError(),a.fadeIn(i.fadeDelay),i._raise("filefoldererror",[e,t]))},_showFileError:function(e,t,i){var a=this,r=a.$errorContainer,n=i||"fileuploaderror",o=t&&t.fileId||"",s=t&&t.id?'
  • '+e+"
  • ":"
  • "+e+"
  • " +return 0===r.find("ul").length?a._addError("
      "+s+"
    "):r.find("ul").append(s),r.fadeIn(a.fadeDelay),a._raise(n,[t,e]),a._setValidationError("file-input-new"),!0},_showError:function(e,t,i){var a=this,r=a.$errorContainer,n=i||"fileerror" +return t=t||{},t.reader=a.reader,a._addError(e),r.fadeIn(a.fadeDelay),a._raise(n,[t,e]),a.isAjaxUpload||a._clearFileInput(),a._setValidationError("file-input-new"),a.$btnUpload.attr("disabled",!0),!0},_noFilesError:function(e){var t=this,i=t.minFileCount>1?t.filePlural:t.fileSingle,a=t.msgFilesTooLess.replace("{n}",t.minFileCount).replace("{files}",i),r=t.$errorContainer +a="
  • "+a+"
  • ",0===r.find("ul").length?t._addError("
      "+a+"
    "):r.find("ul").append(a),t.isError=!0,t._updateFileDetails(0),r.fadeIn(t.fadeDelay),t._raise("fileerror",[e,a]),t._clearFileInput(),t._setValidationError()},_parseError:function(t,i,a,r){var n,o,s,l=this,d=e.trim(a+"") +return o=i.responseJSON&&i.responseJSON.error?""+i.responseJSON.error:"",s=o?o:i.responseText,l.cancelling&&l.msgUploadAborted&&(d=l.msgUploadAborted),l.showAjaxErrorDetails&&s&&(o?d=e.trim(o+""):(s=e.trim(s.replace(/\n\s*\n/g,"\n")),n=s.length?"
    "+s+"
    ":"",d+=d?n:s)),d||(d=l.msgAjaxError.replace("{operation}",t)),l.cancelling=!1,r?""+r+": "+d:d},_parseFileType:function(e,i){var a,r,n,o,s=this,l=s.allowedPreviewTypes||[] +if("application/text-plain"===e)return"text" +for(o=0;o-1&&(i=t.split(".").pop(),a.previewFileIconSettings&&(r=a.previewFileIconSettings[i]||a.previewFileIconSettings[i.toLowerCase()]||null),a.previewFileExtSettings&&e.each(a.previewFileExtSettings,function(e,t){return a.previewFileIconSettings[e]&&t(i)?void(r=a.previewFileIconSettings[e]):void 0})),r||a.previewFileIcon},_parseFilePreviewIcon:function(e,t){var i=this,a=i._getPreviewIcon(t),r=e +return r.indexOf("{previewFileIcon}")>-1&&(r=r.setTokens({previewFileIconClass:i.previewFileIconClass,previewFileIcon:a})),r},_raise:function(t,i){var a=this,r=e.Event(t) +void 0!==i?a.$element.trigger(r,i):a.$element.trigger(r) +var n=r.result,o=n===!1 +if(r.isDefaultPrevented()||o)return!1 +if("filebatchpreupload"===r.type&&(n||o))return a.ajaxAborted=n,!1 +switch(t){case"filebatchuploadcomplete":case"filebatchuploadsuccess":case"fileuploaded":case"fileclear":case"filecleared":case"filereset":case"fileerror":case"filefoldererror":case"fileuploaderror":case"filebatchuploaderror":case"filedeleteerror":case"filecustomerror":case"filesuccessremove":break +default:a.ajaxAborted||(a.ajaxAborted=n)}return!0},_listenFullScreen:function(e){var t,i,a=this,r=a.$modal +r&&r.length&&(t=r&&r.find(".btn-kv-fullscreen"),i=r&&r.find(".btn-kv-borderless"),t.length&&i.length&&(t.removeClass("active").attr("aria-pressed","false"),i.removeClass("active").attr("aria-pressed","false"),e?t.addClass("active").attr("aria-pressed","true"):i.addClass("active").attr("aria-pressed","true"),r.hasClass("file-zoom-fullscreen")?a._maximizeZoomDialog():e?a._maximizeZoomDialog():i.removeClass("active").attr("aria-pressed","false")))},_listen:function(){var i,a=this,r=a.$element,n=a.$form,o=a.$container +a._handler(r,"click",function(e){a._initFileSelected(),r.hasClass("file-no-browse")&&(r.data("zoneClicked")?r.data("zoneClicked",!1):e.preventDefault())}),a._handler(r,"change",e.proxy(a._change,a)),a._handler(a.$caption,"paste",e.proxy(a.paste,a)),a.showBrowse&&(a._handler(a.$btnFile,"click",e.proxy(a._browse,a)),a._handler(a.$btnFile,"keypress",function(e){var t=e.keyCode||e.which +13===t&&(r.trigger("click"),a._browse(e))})),a._handler(o.find(".fileinput-remove:not([disabled])"),"click",e.proxy(a.clear,a)),a._handler(o.find(".fileinput-cancel"),"click",e.proxy(a.cancel,a)),a._handler(o.find(".fileinput-pause"),"click",e.proxy(a.pause,a)),a._initDragDrop(),a._handler(n,"reset",e.proxy(a.clear,a)),a.isAjaxUpload||a._handler(n,"submit",e.proxy(a._submitForm,a)),a._handler(a.$container.find(".fileinput-upload"),"click",e.proxy(a._uploadClick,a)),a._handler(e(window),"resize",function(){a._listenFullScreen(screen.width===window.innerWidth&&screen.height===window.innerHeight)}),i="webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange",a._handler(e(document),i,function(){a._listenFullScreen(t.checkFullScreen())}),a.$caption.on("focus",function(){a.$captionContainer.focus()}),a._autoFitContent(),a._initClickable(),a._refreshPreview()},_autoFitContent:function(){var t,i=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,a=this,r=400>i?a.previewSettingsSmall||a.defaults.previewSettingsSmall:a.previewSettings||a.defaults.previewSettings +e.each(r,function(e,i){t=".file-preview-frame .file-preview-"+e,a.$preview.find(t+".kv-preview-data,"+t+" .kv-preview-data").css(i)})},_scanDroppedItems:function(e,i,a){a=a||"" +var r,n,o,s=this,l=function(e){s._log(t.logMessages.badDroppedFiles),s._log(e)} +e.isFile?e.file(function(e){a&&(e.newPath=a+e.name),i.push(e)},l):e.isDirectory&&(n=e.createReader(),(o=function(){n.readEntries(function(t){if(t&&t.length>0){for(r=0;r-1 +return a._zoneDragDropInit(i),a.isDisabled||!n?(r.effectAllowed="none",void(r.dropEffect="none")):(r.dropEffect="copy",void(a._raise("fileDragEnter",{sourceEvent:i,files:r.types.Files})&&t.addCss(a.$dropZone,"file-highlighted")))},_zoneDragLeave:function(e){var t=this +t._zoneDragDropInit(e),t.isDisabled||t._raise("fileDragLeave",{sourceEvent:e})&&t.$dropZone.removeClass("file-highlighted")},_dropFiles:function(e,t){var i=this,a=i.$element +i.isAjaxUpload?i._change(e,t):(i.changeTriggered=!0,a.get(0).files=t,setTimeout(function(){i.changeTriggered=!1,a.trigger("change"+i.namespace)},i.processDelay)),i.$dropZone.removeClass("file-highlighted")},_zoneDrop:function(e){var i,a=this,r=(a.$element,e.originalEvent.dataTransfer),n=r.files,o=r.items,s=t.getDragDropFolders(o) +if(e.preventDefault(),!a.isDisabled&&!t.isEmpty(n)&&a._raise("fileDragDrop",{sourceEvent:e,files:n}))if(s>0){if(!a.isAjaxUpload)return void a._showFolderError(s) +for(n=[],i=0;i0&&n>=l,c=e(i.item) +d&&(n=l-1),o.initialPreview=t.moveArray(o.initialPreview,r,n,u),o.initialPreviewConfig=t.moveArray(o.initialPreviewConfig,r,n,u),o.previewCache.init(),o.getFrames(".file-preview-initial").each(function(){e(this).attr("data-fileindex",t.INIT_FLAG+s),s++}),d&&(a=o.getFrames(":not(.file-preview-initial):first"),a.length&&c.slideUp(function(){c.insertBefore(a).slideDown()})),o._raise("filesorted",{previewId:c.attr("id"),oldIndex:r,newIndex:n,stack:o.initialPreviewConfig})}},e.extend(!0,i,o.fileActionSettings.dragSettings),o.sortable&&o.sortable.destroy(),o.sortable=p.create(s[0],i))},_setPreviewContent:function(e){var i=this +t.setHtml(i.$preview,e),i._autoFitContent()},_initPreviewImageOrientations:function(){var t=this,i=0,a=t.canOrientImage;(t.autoOrientImageInitial||a)&&t.getFrames(".file-preview-initial").each(function(){var r,n,o,s=e(this),l=t.initialPreviewConfig[i] +l&&l.exif&&l.exif.Orientation&&(o=s.attr("id"),r=s.find(">.kv-file-content img"),n=t._getZoom(o," >.kv-file-content img"),a?r.css("image-orientation",t.autoOrientImageInitial?"from-image":"none"):t.setImageOrientation(r,n,l.exif.Orientation,s)),i++})},_initPreview:function(e){var i,a=this,r=a.initialCaption||"" +return a.previewCache.count(!0)?(i=a.previewCache.out(),r=e&&a.initialCaption?a.initialCaption:i.caption,a._setPreviewContent(i.content),a._setInitThumbAttr(),a._setCaption(r),a._initSortable(),t.isEmpty(i.content)||a.$container.removeClass("file-input-new"),void a._initPreviewImageOrientations()):(a._clearPreview(),void(e?a._setCaption(r):a._initCaption()))},_getZoomButton:function(e){var i=this,a=i.previewZoomButtonIcons[e],r=i.previewZoomButtonClasses[e],n=' title="'+(i.previewZoomButtonTitles[e]||"")+'" ',o=t.isBs(5)?"bs-":"",s=n+("close"===e?" data-"+o+'dismiss="modal" aria-hidden="true"':"") +return("fullscreen"===e||"borderless"===e||"toggleheader"===e)&&(s+=' data-toggle="button" aria-pressed="false" autocomplete="off"'),'"},_getModalContent:function(){var e=this +return e._getLayoutTemplate("modal").setTokens({rtl:e.rtl?" kv-rtl":"",zoomFrameClass:e.frameClass,prev:e._getZoomButton("prev"),next:e._getZoomButton("next"),toggleheader:e._getZoomButton("toggleheader"),fullscreen:e._getZoomButton("fullscreen"),borderless:e._getZoomButton("borderless"),close:e._getZoomButton("close")})},_listenModalEvent:function(e){var i=this,a=i.$modal,r=function(e){return{sourceEvent:e,previewId:a.data("previewId"),modal:a}} +a.on(e+".bs.modal",function(n){if("bs.modal"===n.namespace){var o=a.find(".btn-fullscreen"),s=a.find(".btn-borderless") +a.data("fileinputPluginId")===i.$element.attr("id")&&i._raise("filezoom"+e,r(n)),"shown"===e&&(s.removeClass("active").attr("aria-pressed","false"),o.removeClass("active").attr("aria-pressed","false"),a.hasClass("file-zoom-fullscreen")&&(i._maximizeZoomDialog(),t.checkFullScreen()?o.addClass("active").attr("aria-pressed","true"):s.addClass("active").attr("aria-pressed","true")))}})},_initZoom:function(){var i,a=this,r=a._getLayoutTemplate("modalMain"),n="#"+t.MODAL_ID +r=a._setTabIndex("modal",r),a.showPreview&&(a.$modal=e(n),a.$modal&&a.$modal.length||(i=t.createElement(t.cspBuffer.stash(r)).insertAfter(a.$container),a.$modal=e(n).insertBefore(i),t.cspBuffer.apply(a.$modal),i.remove()),t.initModal(a.$modal),a.$modal.html(t.cspBuffer.stash(a._getModalContent())),t.cspBuffer.apply(a.$modal),e.each(t.MODAL_EVENTS,function(e,t){a._listenModalEvent(t)}))},_initZoomButtons:function(){var t,i,a=this,r=a.$modal.data("previewId")||"",n=a.getFrames().toArray(),o=n.length,s=a.$modal.find(".btn-kv-prev"),l=a.$modal.find(".btn-kv-next") +return n.length<2?(s.hide(),void l.hide()):(s.show(),l.show(),void(o&&(t=e(n[0]),i=e(n[o-1]),s.removeAttr("disabled"),l.removeAttr("disabled"),a.reversePreviewOrder&&([s,l]=[l,s]),t.length&&t.attr("id")===r&&s.attr("disabled",!0),i.length&&i.attr("id")===r&&l.attr("disabled",!0))))},_maximizeZoomDialog:function(){var t=this,i=t.$modal,a=i.find(".modal-header:visible"),r=i.find(".modal-footer:visible"),n=i.find(".kv-zoom-body"),o=e(window).height(),s=0 +i.addClass("file-zoom-fullscreen"),a&&a.length&&(o-=a.outerHeight(!0)),r&&r.length&&(o-=r.outerHeight(!0)),n&&n.length&&(s=n.outerHeight(!0)-n.height(),o-=s),i.find(".kv-zoom-body").height(o)},_resizeZoomDialog:function(e){var i=this,a=i.$modal,r=a.find(".btn-kv-fullscreen"),n=a.find(".btn-kv-borderless") +if(a.hasClass("file-zoom-fullscreen"))t.toggleFullScreen(!1),e?r.hasClass("active")||(a.removeClass("file-zoom-fullscreen"),i._resizeZoomDialog(!0),n.hasClass("active")&&n.removeClass("active").attr("aria-pressed","false")):r.hasClass("active")?r.removeClass("active").attr("aria-pressed","false"):(a.removeClass("file-zoom-fullscreen"),i.$modal.find(".kv-zoom-body").css("height",i.zoomModalHeight)) +else{if(!e)return void i._maximizeZoomDialog() +t.toggleFullScreen(!0)}a.focus()},_setZoomContent:function(i,a){var r,n,o,s,l,d,c,u,p,f,g,m,h=this,v=i.attr("id"),w=h._getZoom(v),b=h.$modal,_=b.find(".btn-kv-fullscreen"),C=b.find(".btn-kv-borderless"),y=b.find(".btn-kv-toggleheader"),x=i.data("zoom") +x&&(x=decodeURIComponent(x),m=w.html().setTokens({zoomData:x}),w.html(m),i.data("zoom",""),w.attr("data-zoom",x)),n=w.attr("data-template")||"generic",r=w.find(".kv-file-content"),o=r.length?'\n'+r.html():"",f=i.data("caption")||h.msgZoomModalHeading,g=i.data("size")||"",u=i.data("description")||"",b.find(".kv-zoom-caption").attr("title",f).html(f),b.find(".kv-zoom-size").html(g),p=b.find(".kv-zoom-description").hide(),u&&(h.showDescriptionClose&&(u=h._getLayoutTemplate("descriptionClose").setTokens({closeIcon:h.previewZoomButtonIcons.close})+""+u),p.show().html(u),h.showDescriptionClose&&h._handler(b.find(".kv-desc-hide"),"click",function(){e(this).parent().fadeOut("fast",function(){b.focus()})})),s=b.find(".kv-zoom-body"),b.removeClass("kv-single-content"),a?(c=s.addClass("file-thumb-loading").clone().insertAfter(s),t.setHtml(s,o).hide(),c.fadeOut("fast",function(){s.fadeIn("fast",function(){s.removeClass("file-thumb-loading")}),c.remove()})):t.setHtml(s,o),d=h.previewZoomSettings[n],d&&(l=s.find(".kv-preview-data"),t.addCss(l,"file-zoom-detail"),e.each(d,function(e,t){l.css(e,t),(l.attr("width")&&"width"===e||l.attr("height")&&"height"===e)&&l.removeAttr(e)})),b.data("previewId",v),h._handler(b.find(".btn-kv-prev"),"click",function(){h._zoomSlideShow("prev",v)}),h._handler(b.find(".btn-kv-next"),"click",function(){h._zoomSlideShow("next",v)}),h._handler(_,"click",function(){h._resizeZoomDialog(!0)}),h._handler(C,"click",function(){h._resizeZoomDialog(!1)}),h._handler(y,"click",function(){var e,t=b.find(".modal-header"),i=b.find(".floating-buttons"),a=t.find(".kv-zoom-actions"),r=function(e){var i=h.$modal.find(".kv-zoom-body"),a=h.zoomModalHeight +b.hasClass("file-zoom-fullscreen")&&(a=i.outerHeight(!0),e||(a-=t.outerHeight(!0))),i.css("height",e?a+e:a)} +t.is(":visible")?(e=t.outerHeight(!0),t.slideUp("slow",function(){a.find(".btn").appendTo(i),r(e)})):(i.find(".btn").appendTo(a),t.slideDown("slow",function(){r()})),b.focus()}),h._handler(b,"keydown",function(t){var i,a,r=t.which||t.keyCode,n=h.processDelay+1,o=e(this).find(".btn-kv-prev"),s=e(this).find(".btn-kv-next"),l=e(this).data("previewId");[i,a]=h.rtl?[39,37]:[37,39],e.each({prev:[o,i],next:[s,a]},function(e,t){var i=t[0],a=t[1] +r===a&&i.length&&(b.focus(),i.attr("disabled")||(i.focus(),h._zoomSlideShow(e,l),setTimeout(function(){i.attr("disabled")&&b.focus()},n)))})})},_showModal:function(e){var i=this,a=i.$modal +e&&e.length&&(t.initModal(a),t.setHtml(a,i._getModalContent()),i._setZoomContent(e),a.data({backdrop:!1}),a.modal("show"),i._initZoomButtons())},_zoomPreview:function(e){var i,a=this +if(!e.length)throw"Cannot zoom to detailed preview!" +i=e.closest(t.FRAMES),a._showModal(i)},_zoomSlideShow:function(t,i){var a,r,n,o,s=this,l=s.$modal.find(".kv-zoom-actions .btn-kv-"+t),d=s.getFrames().toArray(),c=[],u=d.length +if(s.reversePreviewOrder&&(t="prev"===t?"next":"prev"),!l.attr("disabled")){for(r=0;u>r;r++)n=e(d[r]),n&&n.length&&n.find(".kv-file-zoom:visible").length&&c.push(d[r]) +for(u=c.length,r=0;u>r;r++)if(e(c[r]).attr("id")===i){o="prev"===t?r-1:r+1 +break}0>o||o>=u||!c[o]||(a=e(c[o]),a.length&&s._setZoomContent(a,t),s._initZoomButtons(),s._raise("filezoom"+t,{previewId:i,modal:s.$modal}))}},_initZoomButton:function(){var t=this +t.$preview.find(".kv-file-zoom").each(function(){var i=e(this) +t._handler(i,"click",function(){t._zoomPreview(i)})})},_inputFileCount:function(){return this.$element[0].files.length},_refreshPreview:function(){var t,i=this;(i._inputFileCount()||i.isAjaxUpload)&&i.showPreview&&i.isPreviewable&&(i.isAjaxUpload&&i.fileManager.count()>0?(t=e.extend(!0,{},i.getFileList()),i.fileManager.clear(),i._clearFileInput()):t=i.$element[0].files,t&&t.length&&(i.readFiles(t),i._setFileDropZoneTitle()))},_clearObjects:function(t){t.find("video audio").each(function(){this.pause(),e(this).remove()}),t.find("img object div").each(function(){e(this).remove()})},_clearFileInput:function(){var t,i,a,r=this,n=r.$element +r._inputFileCount()&&(t=n.closest("form"),i=e(document.createElement("form")),a=e(document.createElement("div")),n.before(a),t.length?t.after(i):a.after(i),i.append(n).trigger("reset"),a.before(n).remove(),i.remove())},_resetUpload:function(){var e=this +e.uploadStartTime=t.now(),e.uploadCache=[],e.$btnUpload.removeAttr("disabled"),e._setProgress(0),e._hideProgress(),e._resetErrors(!1),e._initAjax(),e.fileManager.clearImages(),e._resetCanvas(),e.overwriteInitial&&(e.initialPreview=[],e.initialPreviewConfig=[],e.initialPreviewThumbTags=[],e.previewCache.data={content:[],config:[],tags:[]})},_resetCanvas:function(){var e=this +e.imageCanvas&&e.imageCanvasContext&&e.imageCanvasContext.clearRect(0,0,e.imageCanvas.width,e.imageCanvas.height)},_hasInitialPreview:function(){var e=this +return!e.overwriteInitial&&e.previewCache.count(!0)},_resetPreview:function(){var i,a,r,n=this,o=n.showUploadedThumbs,s=!n.removeFromPreviewOnError,l=(o||s)&&n.isDuplicateError +n.previewCache.count(!0)?(i=n.previewCache.out(),l&&(r=t.createElement("").insertAfter(n.$container),n.getFrames().each(function(){var t=e(this);(o&&t.hasClass("file-preview-success")||s&&t.hasClass("file-preview-error"))&&r.append(t)})),n._setPreviewContent(i.content),n._setInitThumbAttr(),a=n.initialCaption?n.initialCaption:i.caption,n._setCaption(a),l&&(r.contents().appendTo(n.$preview),r.remove())):(n._clearPreview(),n._initCaption()),n.showPreview&&(n._initZoom(),n._initSortable()),n.isDuplicateError=!1},_clearDefaultPreview:function(){var e=this +e.$preview.find(".file-default-preview").remove()},_validateDefaultPreview:function(){var e=this +e.showPreview&&!t.isEmpty(e.defaultPreviewContent)&&(e._setPreviewContent('
    '+e.defaultPreviewContent+"
    "),e.$container.removeClass("file-input-new"),e._initClickable())},_resetPreviewThumbs:function(e){var t,i=this +return e?(i._clearPreview(),void i.clearFileStack()):void(i._hasInitialPreview()?(t=i.previewCache.out(),i._setPreviewContent(t.content),i._setInitThumbAttr(),i._setCaption(t.caption),i._initPreviewActions()):i._clearPreview())},_getLayoutTemplate:function(e){var i=this,a=i.layoutTemplates[e] +return t.isEmpty(i.customLayoutTags)?a:t.replaceTags(a,i.customLayoutTags)},_getPreviewTemplate:function(e){var i=this,a=i.previewTemplates,r=a[e]||a.other +return t.isEmpty(i.customPreviewTags)?r:t.replaceTags(r,i.customPreviewTags)},_getOutData:function(e,t,i,a){var r=this +return t=t||{},i=i||{},a=a||r.fileManager.list(),{formdata:e,files:a,filenames:r.filenames,filescount:r.getFilesCount(),extra:r._getExtraData(),response:i,reader:r.reader,jqXHR:t}},_getMsgSelected:function(e,t){var i=this,a=1===e?i.fileSingle:i.filePlural +return e>0?i.msgSelected.replace("{n}",e).replace("{files}",a):t?i.msgProcessing:i.msgNoFilesSelected},_getFrame:function(e,i){var a=this,r=t.getFrameElement(a.$preview,e) +return!a.showPreview||i||r.length||a._log(t.logMessages.invalidThumb,{id:e}),r},_getZoom:function(e,i){var a=this,r=t.getZoomElement(a.$preview,e,i) +return a.showPreview&&!r.length&&a._log(t.logMessages.invalidThumb,{id:e}),r},_getThumbs:function(e){return e=e||"",this.getFrames(":not(.file-preview-initial)"+e)},_getThumbId:function(e){var t=this +return t.previewInitId+"-"+e},_getExtraData:function(e,t){var i=this,a=i.uploadExtraData +return"function"==typeof i.uploadExtraData&&(a=i.uploadExtraData(e,t)),a},_initXhr:function(e,i){var a=this,r=a.fileManager,n=function(e){var n=0,o=e.total,s=e.loaded||e.position,l=r.getUploadStats(i,s,o) +e.lengthComputable&&!a.enableResumableUpload&&(n=t.round(s/o*100)),i?a._setFileUploadStats(i,n,l):a._setProgress(n,null,null,a._getStats(l)),a._raise("fileajaxprogress",[l])} +return e.upload&&(a.progressDelay&&(n=t.debounce(n,a.progressDelay)),e.upload.addEventListener("progress",n,!1)),e},_initAjaxSettings:function(){var t=this +t._ajaxSettings=e.extend(!0,{},t.ajaxSettings),t._ajaxDeleteSettings=e.extend(!0,{},t.ajaxDeleteSettings)},_mergeAjaxCallback:function(e,t,i){var a,r=this,n=r._ajaxSettings,o=r.mergeAjaxCallbacks +"delete"===i&&(n=r._ajaxDeleteSettings,o=r.mergeAjaxDeleteCallbacks),a=n[e],o&&"function"==typeof a?"before"===o?n[e]=function(){a.apply(this,arguments),t.apply(this,arguments)}:n[e]=function(){t.apply(this,arguments),a.apply(this,arguments)}:n[e]=t},_ajaxSubmit:function(t,i,a,r,n,o,s,l){var d,c,u,p,f=this +f._raise("filepreajax",[n,o,s])&&(n.append("initialPreview",JSON.stringify(f.initialPreview)),n.append("initialPreviewConfig",JSON.stringify(f.initialPreviewConfig)),n.append("initialPreviewThumbTags",JSON.stringify(f.initialPreviewThumbTags)),f._initAjaxSettings(),f._mergeAjaxCallback("beforeSend",t),f._mergeAjaxCallback("success",i),f._mergeAjaxCallback("complete",a),f._mergeAjaxCallback("error",r),l=l||f.uploadUrlThumb||f.uploadUrl,"function"==typeof l&&(l=l()),u=f._getExtraData(o,s)||{},"object"==typeof u&&e.each(u,function(e,t){n.append(e,t)}),c={xhr:function(){var t=e.ajaxSettings.xhr() +return f._initXhr(t,o)},url:f._encodeURI(l),type:"POST",dataType:"json",data:n,cache:!1,processData:!1,contentType:!1},d=e.extend(!0,{},c,f._ajaxSettings),p=f.taskManager.addTask(o+"-"+s,function(){var t,i,a=this.self +t=a.ajaxQueue.shift(),i=e.ajax(t),a.ajaxRequests.push(i)}),f.ajaxQueue.push(d),p.runWithContext({self:f}))},_mergeArray:function(e,i){var a=this,r=t.cleanArray(a[e]),n=t.cleanArray(i) +a[e]=r.concat(n)},_initUploadSuccess:function(i,a,r){var n,o,s,l,d,c,u,p,f,g=this +return!g.showPreview||"object"!=typeof i||e.isEmptyObject(i)?void g._resetCaption():(void 0!==i.initialPreview&&i.initialPreview.length>0&&(g.hasInitData=!0,d=i.initialPreview||[],c=i.initialPreviewConfig||[],u=i.initialPreviewThumbTags||[],n=void 0===i.append||i.append,d.length>0&&!t.isArray(d)&&(d=d.split(g.initialPreviewDelimiter)),d.length&&(g._mergeArray("initialPreview",d),g._mergeArray("initialPreviewConfig",c),g._mergeArray("initialPreviewThumbTags",u)),void 0!==a?r?(p=a.attr("id"),f=g._getUploadCacheIndex(p),null!==f&&(g.uploadCache[f]={id:p,content:d[0],config:c[0]||[],tags:u[0]||[],append:n})):(s=g.previewCache.add(d[0],c[0],u[0],n),o=g.previewCache.get(s,!1),l=t.createElement(o).hide().appendTo(a),a.fadeOut("slow",function(){var e=l.find("> .file-preview-frame") +e&&e.length&&e.insertBefore(a).fadeIn("slow").css("display:inline-block"),g._initPreviewActions(),g._clearFileInput(),a.remove(),l.remove(),g._initSortable()})):(g.previewCache.set(d,c,u,n),g._initPreview(),g._initPreviewActions())),void g._resetCaption())},_getUploadCacheIndex:function(e){var t,i,a=this,r=a.uploadCache.length +for(t=0;r>t;t++)if(i=a.uploadCache[t],i.id===e)return t +return null},_initSuccessThumbs:function(){var i=this +i.showPreview&&setTimeout(function(){i._getThumbs(t.FRAMES+".file-preview-success").each(function(){var a=e(this),r=a.find(".kv-file-remove") +r.removeAttr("disabled"),i._handler(r,"click",function(){var e=a.attr("id"),r=i._raise("filesuccessremove",[e,a.attr("data-fileindex")]) +t.cleanMemory(a),r!==!1&&(i.$caption.attr("title",""),a.fadeOut("slow",function(){i.fileManager +a.remove(),i.getFrames().length||i.reset()}))})})},i.processDelay)},_updateInitialPreview:function(){var t=this,i=t.uploadCache +t.showPreview&&(e.each(i,function(e,i){t.previewCache.add(i.content,i.config,i.tags,i.append)}),t.hasInitData&&(t._initPreview(),t._initPreviewActions()))},_getThumbFileId:function(e){var t=this +return t.showPreview&&void 0!==e?e.attr("data-fileid"):null},_getThumbFile:function(e){var t=this,i=t._getThumbFileId(e) +return i?t.fileManager.getFile(i):null},_uploadSingle:function(i,a,r){var n,o,s,l,d,c,u,p,f,g,m,h,v,w=this,b=w.fileManager,_=b.count(),C=new FormData,y=w._getThumbId(a),x=_>0||!e.isEmptyObject(w.uploadExtraData),T=w.ajaxOperations.uploadThumb,P=b.getFile(a),F={id:y,index:i,fileId:a},k=w.fileManager.getFileName(a,!0) +w.enableResumableUpload||(w.showPreview&&(o=b.getThumb(a),u=o.find(".file-thumb-progress"),l=o.find(".kv-file-upload"),d=o.find(".kv-file-remove"),u.show()),0===_||!x||w.showPreview&&l&&l.hasClass("disabled")||w._abort(F)||(v=function(){c?b.errors.push(a):b.removeFile(a),b.setProcessed(a),b.isProcessed()&&(w.fileBatchCompleted=!0,s())},s=function(){var e +w.fileBatchCompleted&&setTimeout(function(){var i=0===b.count(),a=b.errors.length +w._updateInitialPreview(),w.unlock(i),i&&w._clearFileInput(),e=w.$preview.find(".file-preview-initial"),w.uploadAsync&&e.length&&(t.addCss(e,t.SORT_CSS),w._initSortable()),w._raise("filebatchuploadcomplete",[b.stack,w._getExtraData()]),w.retryErrorUploads&&0!==a||b.clear(),w._setProgress(101),w.ajaxAborted=!1},w.processDelay)},p=function(s){n=w._getOutData(C,s),b.initStats(a),w.fileBatchCompleted=!1,r||(w.ajaxAborted=!1),w.showPreview&&(o.hasClass("file-preview-success")||(w._setThumbStatus(o,"Loading"),t.addCss(o,"file-uploading")),l.attr("disabled",!0),d.attr("disabled",!0)),r||w.lock(),-1!==b.errors.indexOf(a)&&delete b.errors[a],w._raise("filepreupload",[n,y,i,w._getThumbFileId(o)]),e.extend(!0,F,n),w._abort(F)&&(s.abort(),r||(w._setThumbStatus(o,"New"),o.removeClass("file-uploading"),l.removeAttr("disabled"),d.removeAttr("disabled")),w._setProgressCancelled())},g=function(s,d,p){var g=w.showPreview&&o.attr("id")?o.attr("id"):y +n=w._getOutData(C,p,s),e.extend(!0,F,n),setTimeout(function(){t.isEmpty(s)||t.isEmpty(s.error)?(w.showPreview&&(w._setThumbStatus(o,"Success"),l.hide(),w._initUploadSuccess(s,o,r),w._setProgress(101,u)),w._raise("fileuploaded",[n,g,i,w._getThumbFileId(o)]),r?v():w.fileManager.remove(o)):(c=!0,f=w._parseError(T,p,w.msgUploadError,w.fileManager.getFileName(a)),w._showFileError(f,F),w._setPreviewError(o,!0),w.retryErrorUploads||l.hide(),r&&v(),w._setProgress(101,w._getFrame(g).find(".file-thumb-progress"),w.msgUploadError))},w.processDelay)},m=function(){w.showPreview&&(l.removeAttr("disabled"),d.removeAttr("disabled"),o.removeClass("file-uploading")),r?s():(w.unlock(!1),w._clearFileInput()),w._initSuccessThumbs()},h=function(t,i,n){f=w._parseError(T,t,n,w.fileManager.getFileName(a)),c=!0,setTimeout(function(){var i +r&&v(),w.fileManager.setProgress(a,100),w._setPreviewError(o,!0),w.retryErrorUploads||l.hide(),e.extend(!0,F,w._getOutData(C,t)),w._setProgress(101,w.$progress,w.msgAjaxProgressError.replace("{operation}",T)),i=w.showPreview&&o?o.find(".file-thumb-progress"):"",w._setProgress(101,i,w.msgUploadError),w._showFileError(f,F)},w.processDelay)},w._setFileData(C,P.file,k,a),w._setUploadData(C,{fileId:a}),w._ajaxSubmit(p,g,m,h,C,a,i)))},_setFileData:function(e,t,i,a){var r=this,n=r.preProcessUpload +n&&"function"==typeof n?e.append(r.uploadFileAttr,n(a,t)):e.append(r.uploadFileAttr,t,i)},_checkBatchPreupload:function(t,i){var a=this,r=a._raise("filebatchpreupload",[t]) +return r?!0:(a._abort(t),i&&i.abort(),a._getThumbs().each(function(){var t=e(this),i=t.find(".kv-file-upload"),r=t.find(".kv-file-remove") +t.hasClass("file-preview-loading")&&(a._setThumbStatus(t,"New"),t.removeClass("file-uploading")),i.removeAttr("disabled"),r.removeAttr("disabled")}),a._setProgressCancelled(),!1)},_uploadBatch:function(){var i,a,r,n,o,s,l=this,d=l.fileManager,c=d.total(),u={},p=c>0||!e.isEmptyObject(l.uploadExtraData),f=new FormData,g=l.ajaxOperations.uploadBatch +if(0!==c&&p&&!l._abort(u)){s=function(){l.fileManager.clear(),l._clearFileInput()},i=function(i){l.lock(),d.initStats() +var a=l._getOutData(f,i) +l.ajaxAborted=!1,l.showPreview&&l._getThumbs().each(function(){var i=e(this),a=i.find(".kv-file-upload"),r=i.find(".kv-file-remove") +i.hasClass("file-preview-success")||(l._setThumbStatus(i,"Loading"),t.addCss(i,"file-uploading")),a.attr("disabled",!0),r.attr("disabled",!0)}),l._checkBatchPreupload(a,i)},a=function(i,a,r){var n=l._getOutData(f,r,i),d=0,c=l._getThumbs(":not(.file-preview-success)"),u=t.isEmpty(i)||t.isEmpty(i.errorkeys)?[]:i.errorkeys +t.isEmpty(i)||t.isEmpty(i.error)?(l._raise("filebatchuploadsuccess",[n]),s(),l.showPreview?(c.each(function(){var t=e(this) +l._setThumbStatus(t,"Success"),t.removeClass("file-uploading"),t.find(".kv-file-upload").hide().removeAttr("disabled")}),l._initUploadSuccess(i)):l.reset(),l._setProgress(101)):(l.showPreview&&(c.each(function(){var t=e(this) +t.removeClass("file-uploading"),t.find(".kv-file-upload").removeAttr("disabled"),t.find(".kv-file-remove").removeAttr("disabled"),0===u.length||-1!==e.inArray(d,u)?(l._setPreviewError(t,!0),l.retryErrorUploads||(t.find(".kv-file-upload").hide(),l.fileManager.remove(t))):(t.find(".kv-file-upload").hide(),l._setThumbStatus(t,"Success"),l.fileManager.remove(t)),(!t.hasClass("file-preview-error")||l.retryErrorUploads)&&d++}),l._initUploadSuccess(i)),o=l._parseError(g,r,l.msgUploadError),l._showFileError(o,n,"filebatchuploaderror"),l._setProgress(101,l.$progress,l.msgUploadError))},n=function(){l.unlock(),l._initSuccessThumbs(),l._clearFileInput(),l._raise("filebatchuploadcomplete",[l.fileManager.stack,l._getExtraData()])},r=function(t,i,a){var r=l._getOutData(f,t) +o=l._parseError(g,t,a),l._showFileError(o,r,"filebatchuploaderror"),l.uploadFileCount=c-1,l.showPreview&&(l._getThumbs().each(function(){var t=e(this) +t.removeClass("file-uploading"),l._getThumbFile(t)&&l._setPreviewError(t)}),l._getThumbs().removeClass("file-uploading"),l._getThumbs(" .kv-file-upload").removeAttr("disabled"),l._getThumbs(" .kv-file-delete").removeAttr("disabled"),l._setProgress(101,l.$progress,l.msgAjaxProgressError.replace("{operation}",g)))} +var m=0 +e.each(l.fileManager.stack,function(e,i){t.isEmpty(i.file)||l._setFileData(f,i.file,i.nameFmt||"untitled_"+m,e),m++}),l._ajaxSubmit(i,a,n,r,f)}},_uploadExtraOnly:function(){var e,i,a,r,n,o=this,s={},l=new FormData,d=o.ajaxOperations.uploadExtra +e=function(e){o.lock() +var t=o._getOutData(l,e) +o._setProgress(50),s.data=t,s.xhr=e,o._checkBatchPreupload(t,e)},i=function(e,i,a){var r=o._getOutData(l,a,e) +t.isEmpty(e)||t.isEmpty(e.error)?(o._raise("filebatchuploadsuccess",[r]),o._clearFileInput(),o._initUploadSuccess(e),o._setProgress(101)):(n=o._parseError(d,a,o.msgUploadError),o._showFileError(n,r,"filebatchuploaderror"))},a=function(){o.unlock(),o._clearFileInput(),o._raise("filebatchuploadcomplete",[o.fileManager.stack,o._getExtraData()])},r=function(e,t,i){var a=o._getOutData(l,e) +n=o._parseError(d,e,i),s.data=a,o._showFileError(n,a,"filebatchuploaderror"),o._setProgress(101,o.$progress,o.msgAjaxProgressError.replace("{operation}",d))},o._ajaxSubmit(e,i,a,r,l)},_deleteFileIndex:function(i){var a=this,r=i.attr("data-fileindex"),n=a.reversePreviewOrder +r.substring(0,5)===t.INIT_FLAG&&(r=parseInt(r.replace(t.INIT_FLAG,"")),a.initialPreview=t.spliceArray(a.initialPreview,r,n),a.initialPreviewConfig=t.spliceArray(a.initialPreviewConfig,r,n),a.initialPreviewThumbTags=t.spliceArray(a.initialPreviewThumbTags,r,n),a.getFrames().each(function(){var i=e(this),a=i.attr("data-fileindex") +a.substring(0,5)===t.INIT_FLAG&&(a=parseInt(a.replace(t.INIT_FLAG,"")),a>r&&(a--,i.attr("data-fileindex",t.INIT_FLAG+a)))}))},_resetCaption:function(){var e=this +setTimeout(function(){var t,i,a,r="",n=e.previewCache.count(!0),o=e.fileManager.count(),s=":not(.file-preview-success):not(.file-preview-error)",l=e.showPreview&&e.getFrames(s).length +0!==o||0!==n||l?(t=n+o,t>1?r=e._getMsgSelected(t):0===o?(a=e.initialPreviewConfig[0],r="",a&&(r=a.caption||a.filename||""),r||(r=e._getMsgSelected(t))):(i=e.fileManager.getFirstFile(),r=i?i.nameFmt:"_"),e._setCaption(r)):e.reset()},e.processDelay)},_initFileActions:function(){var i=this +i.showPreview&&(i._initZoomButton(),i.getFrames(" .kv-file-remove").each(function(){var a,r,n=e(this),o=n.closest(t.FRAMES),s=o.attr("id"),l=o.attr("data-fileindex") +i.fileManager +i._handler(n,"click",function(){return r=i._raise("filepreremove",[s,l]),r!==!1&&i._validateMinCount()?(a=o.hasClass("file-preview-error"),t.cleanMemory(o),void o.fadeOut("slow",function(){i.fileManager.remove(o),i._clearObjects(o),o.remove(),s&&a&&i.$errorContainer.find('li[data-thumb-id="'+s+'"]').fadeOut("fast",function(){e(this).remove(),i._errorsExist()||i._resetErrors()}),i._clearFileInput(),i._resetCaption(),i._raise("fileremoved",[s,l])})):!1})}),i.getFrames(" .kv-file-upload").each(function(){var a=e(this) +i._handler(a,"click",function(){var e=a.closest(t.FRAMES),r=i._getThumbFileId(e) +i._hideProgress(),(!e.hasClass("file-preview-error")||i.retryErrorUploads)&&i._uploadSingle(i.fileManager.getIndex(r),r,!1)})}))},_initPreviewActions:function(){var i=this,a=i.$preview,r=i.deleteExtraData||{},n=t.FRAMES+" .kv-file-remove",o=i.fileActionSettings,s=o.removeClass,l=o.removeErrorClass,d=function(){var e=i.isAjaxUpload?i.previewCache.count(!0):i._inputFileCount() +i.getFrames().length||e?i._resetCaption():(i._setCaption(""),i.reset(),i.initialCaption="")} +i._initZoomButton(),a.find(n).each(function(){var a,n,o,c,u=e(this),p=u.data("url")||i.deleteUrl,f=u.data("key"),g=i.ajaxOperations.deleteThumb +if(!t.isEmpty(p)&&void 0!==f){"function"==typeof p&&(p=p()) +var m,h,v,w,b,_=u.closest(t.FRAMES),C=i.previewCache.data,y=_.attr("data-fileindex") +y=parseInt(y.replace(t.INIT_FLAG,"")),v=t.isEmpty(C.config)&&t.isEmpty(C.config[y])?null:C.config[y],b=t.isEmpty(v)||t.isEmpty(v.extra)?r:v.extra,w=v&&(v.filename||v.caption)||"","function"==typeof b&&(b=b()),h={id:u.attr("id"),key:f,extra:b},n=function(e){i.ajaxAborted=!1,i._raise("filepredelete",[f,e,b]),i._abort()?e.abort():(u.removeClass(l),t.addCss(_,"file-uploading"),t.addCss(u,"disabled "+s))},o=function(e,r,n){var o,c +return t.isEmpty(e)||t.isEmpty(e.error)?(_.removeClass("file-uploading").addClass("file-deleted"),void _.fadeOut("slow",function(){y=parseInt(_.attr("data-fileindex").replace(t.INIT_FLAG,"")),i.previewCache.unset(y),i._deleteFileIndex(_),o=i.previewCache.count(!0),c=o>0?i._getMsgSelected(o):"",i._setCaption(c),i._raise("filedeleted",[f,n,b]),i._clearObjects(_),_.remove(),d()})):(h.jqXHR=n,h.response=e,a=i._parseError(g,n,i.msgDeleteError,w),i._showFileError(a,h,"filedeleteerror"),_.removeClass("file-uploading"),u.removeClass("disabled "+s).addClass(l),void d())},c=function(e,t,a){var r=i._parseError(g,e,a,w) +h.jqXHR=e,h.response={},i._showFileError(r,h,"filedeleteerror"),_.removeClass("file-uploading"),u.removeClass("disabled "+s).addClass(l),d()},i._initAjaxSettings(),i._mergeAjaxCallback("beforeSend",n,"delete"),i._mergeAjaxCallback("success",o,"delete"),i._mergeAjaxCallback("error",c,"delete"),m=e.extend(!0,{},{url:i._encodeURI(p),type:"POST",dataType:"json",data:e.extend(!0,{},{key:f},b)},i._ajaxDeleteSettings),i._handler(u,"click",function(){return i._validateMinCount()?(i.ajaxAborted=!1,i._raise("filebeforedelete",[f,b]),void(i.ajaxAborted instanceof Promise?i.ajaxAborted.then(function(t){t||e.ajax(m)}):i.ajaxAborted||e.ajax(m))):!1})}})},_hideFileIcon:function(){var e=this +e.overwriteInitial&&e.$captionContainer.removeClass("icon-visible")},_showFileIcon:function(){var e=this +t.addCss(e.$captionContainer,"icon-visible")},_getSize:function(t,i){var a,r,n=this,o=parseFloat(t),s=n.fileSizeGetter +return e.isNumeric(t)&&e.isNumeric(o)?("function"==typeof s?r=s(o):0===o?r="0.00 B":(i||(i=n.sizeUnits),a=Math.floor(Math.log(o)/Math.log(n.bytesToKB)),r=(o/Math.pow(n.bytesToKB,a)).toFixed(2)+" "+i[a]),n._getLayoutTemplate("size").replace("{sizeText}",r)):""},_getFileType:function(e){var t=this +return t.mimeTypeAliases[e]||e},_generatePreviewTemplate:function(i,a,r,n,o,s,l,d,c,u,p,f,g,m){var h,v,w,b=this,_=b.slug(r),C="",y="",x=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,T=_,P=_,F="type-default",k=u||b._renderFileFooter(i,_,d,"auto",l),E=b.preferIconicPreview,S=b.preferIconicZoomPreview,I=E?"other":i +return v=400>x?b.previewSettingsSmall[I]||b.defaults.previewSettingsSmall[I]:b.previewSettings[I]||b.defaults.previewSettings[I],v&&e.each(v,function(e,t){y+=e+":"+t+";"}),w=function(a,l,d,u,m){var h,v=d?"zoom-"+o:o,w=b._getPreviewTemplate(a),C=(c||"")+" "+u +return b.frameClass&&(C=b.frameClass+" "+C),d&&(C=C.replace(" "+t.SORT_CSS,"")),w=b._parseFilePreviewIcon(w,r),"object"!==i||n||e.each(b.defaults.fileTypeSettings,function(e,t){"object"!==e&&"other"!==e&&t(r,n)&&(F="type-"+e)}),t.isEmpty(g)||(void 0!==g.title&&null!==g.title&&(T=g.title),void 0!==g.alt&&null!==g.alt&&(T=g.alt)),h={previewId:v,caption:_,title:T,alt:P,frameClass:C,type:b._getFileType(n),fileindex:p,fileid:s||"",typeCss:F,footer:k,data:d&&m?"{zoomData}":l,template:f||i,style:y?'style="'+y+'"':"",zoomData:m?encodeURIComponent(m):""},d&&(h.zoomCache="",h.zoomData="{zoomData}"),w.setTokens(h)},p=p||o.slice(o.lastIndexOf("-")+1),b.fileActionSettings.showZoom&&(C=w(S?"other":i,a,!0,"kv-zoom-thumb",m)),C="\n"+b._getLayoutTemplate("zoomCache").replace("{zoomContent}",C),"function"==typeof b.sanitizeZoomCache&&(C=b.sanitizeZoomCache(C)),h=w(E?"other":i,a,!1,"kv-preview-thumb",m),h.setTokens({zoomCache:C})},_addToPreview:function(e,i){var a,r=this +return i=t.cspBuffer.stash(i),a=r.reversePreviewOrder?e.prepend(i):e.append(i),t.cspBuffer.apply(e),a},_previewDefault:function(e,i){var a=this,r=a.$preview +if(a.showPreview){var n,o=t.getFileName(e),s=e?e.type:"",l=e.size||0,d=a._getFileName(e,""),c=i===!0&&!a.isAjaxUpload,u=t.createObjectURL(e),p=a.fileManager.getId(e),f=a._getThumbId(p) +a._clearDefaultPreview(),n=a._generatePreviewTemplate("other",u,o,s,f,p,c,l),a._addToPreview(r,n),a._setThumbAttr(f,d,l),i===!0&&a.isAjaxUpload&&a._setThumbStatus(a._getFrame(f),"Error")}},_previewFile:function(e,i,a,r,n){if(this.showPreview){var o,s=this,l=t.getFileName(i),d=n.type,c=n.name,u=s._parseFileType(d,l),p=s.$preview,f=i.size||0,g="image"===u?a.target.result:r,m=s.fileManager,h=m.getId(i),v=s._getThumbId(h) +o=s._generatePreviewTemplate(u,g,l,d,v,h,!1,f),s._clearDefaultPreview(),s._addToPreview(p,o) +var w=s._getFrame(v) +s._validateImageOrientation(w.find("img"),i,v,h,c,d,f,g),s._setThumbAttr(v,c,f),s._initSortable()}},_setThumbAttr:function(e,t,i,a){var r=this,n=r._getFrame(e) +n.length&&(i=i&&i>0?r._getSize(i):"",n.data({caption:t,size:i,description:a||""}))},_setInitThumbAttr:function(){var e,i,a,r,n,o=this,s=o.previewCache.data,l=o.previewCache.count(!0) +if(0!==l)for(var d=0;l>d;d++)e=s.config[d],n=o.previewInitId+"-"+t.INIT_FLAG+d,i=t.ifSet("caption",e,t.ifSet("filename",e)),a=t.ifSet("size",e),r=t.ifSet("description",e),o._setThumbAttr(n,i,a,r)},_slugDefault:function(e){return t.isEmpty(e,!0)?"":(e+"").replace(/[\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g,"_")},_updateFileDetails:function(e){var i,a,r,n,o,s=this,l=s.$element,d=t.isIE(9)&&t.findFileName(l.val())||l[0].files[0]&&l[0].files[0].name +!d&&s.fileManager.count()>0?(o=s.fileManager.getFirstFile(),i=o.nameFmt):i=d?s.slug(d):"_",a=s.isAjaxUpload?s.fileManager.count():e,n=s.previewCache.count(!0)+a,r=1===a?i:s._getMsgSelected(n,!s.isAjaxUpload&&!s.isError),s.isError?(s.$previewContainer.removeClass("file-thumb-loading"),s._initCapStatus(),s.$previewStatus.html(""),s.$captionContainer.removeClass("icon-visible")):s._showFileIcon(),s._setCaption(r,s.isError),s.$container.removeClass("file-input-new file-input-ajax-new"),s._raise("fileselect",[e,i]),s.previewCache.count(!0)&&s._initPreviewActions()},_setThumbStatus:function(e,i){var a=this +if(a.showPreview){var r="indicator"+i,n=r+"Title",o="file-preview-"+i.toLowerCase(),s=e.find(".file-upload-indicator"),l=a.fileActionSettings +e.removeClass("file-preview-success file-preview-error file-preview-paused file-preview-loading"),"Success"===i&&e.find(".file-drag-handle").remove(),t.setHtml(s,l[r]),s.attr("title",l[n]),e.addClass(o),"Error"!==i||a.retryErrorUploads||e.find(".kv-file-upload").attr("disabled",!0)}},_setProgressCancelled:function(){var e=this +e._setProgress(101,e.$progress,e.msgCancelled)},_setProgress:function(e,i,a,r){var n=this +if(i=i||n.$progress,i.length){var o,s=Math.min(e,100),l=n.progressUploadThreshold,d=100>=e?n.progressTemplate:n.progressCompleteTemplate,c=100>s?n.progressTemplate:a?n.paused?n.progressPauseTemplate:n.progressErrorTemplate:d +e>=100&&(r=""),t.isEmpty(c)||(o=l&&s>l&&100>=e?c.setTokens({percent:l,status:n.msgUploadThreshold}):c.setTokens({percent:s,status:e>100?n.msgUploadEnd:s+"%"}),r=r||"",o=o.setTokens({stats:r}),t.setHtml(i,o),a&&t.setHtml(i.find('[role="progressbar"]'),a))}},_hasFiles:function(){var e=this.$element[0] +return!!(e&&e.files&&e.files.length)},_setFileDropZoneTitle:function(){var e,i=this,a=i.$container.find(".file-drop-zone"),r=i.dropZoneTitle +i.isClickable&&(e=t.isEmpty(i.$element.attr("multiple"))?i.fileSingle:i.filePlural,r+=i.dropZoneClickTitle.replace("{files}",e)),a.find("."+i.dropZoneTitleClass).remove(),!i.showPreview||0===a.length||i.fileManager.count()>0||!i.dropZoneEnabled||i.previewCache.count()>0||!i.isAjaxUpload&&i._hasFiles()||(0===a.find(t.FRAMES).length&&t.isEmpty(i.defaultPreviewContent)&&a.prepend('
    '+r+"
    "),i.$container.removeClass("file-input-new"),t.addCss(i.$container,"file-input-ajax-new"))},_getStats:function(e){var i,a,r=this +return r.showUploadStats&&e&&e.bitrate?(a=r._getLayoutTemplate("stats"),i=e.elapsed&&e.bps?r.msgPendingTime.setTokens({time:t.getElapsed(Math.ceil(e.pendingBytes/e.bps))}):r.msgCalculatingTime,a.setTokens({uploadSpeed:e.bitrate,pendingTime:i})):""},_setResumableProgress:function(e,t,i){var a=this,r=a.resumableManager,n=i?r:a,o=i?i.find(".file-thumb-progress"):null +0===n.lastProgress&&(n.lastProgress=e),e0&&e._getFileCount(t-1)=g:g>=d,u||(l=p["msgImage"+o+i].setTokens({name:n,size:g}),p._showFileError(l,s),p._setPreviewError(r)))},_getExifObj:function(e){var i,a=this,r=t.logMessages.exifWarning +if("data:image/jpeg;base64,"!==e.slice(0,23)&&"data:image/jpg;base64,"!==e.slice(0,22))return void(i=null) +try{i=window.piexif?window.piexif.load(e):null}catch(n){i=null,r=n&&n.message||""}return i||a._log(t.logMessages.badExifParser,{details:r}),i},setImageOrientation:function(i,a,r,n){var o,s,l,d=this,c=!i||!i.length,u=!a||!a.length,p=!1,f=c&&n&&"image"===n.attr("data-template") +c&&u||(l="load.fileinputimageorient",f?(i=a,a=null,i.css(d.previewSettings.image),s=e(document.createElement("div")).appendTo(n.find(".kv-file-content")),o=e(document.createElement("span")).insertBefore(i),i.css("visibility","hidden").removeClass("file-zoom-detail").appendTo(s)):p=!i.is(":visible"),i.off(l).on(l,function(){p&&(d.$preview.removeClass("hide-content"),n.find(".kv-file-content").css("visibility","hidden")) +var e=i[0],l=a&&a.length?a[0]:null,c=e.offsetHeight,u=e.offsetWidth,g=t.getRotation(r) +if(p&&(n.find(".kv-file-content").css("visibility","visible"),d.$preview.addClass("hide-content")),i.data("orientation",r),l&&a.data("orientation",r),5>r)return t.setTransform(e,g),void t.setTransform(l,g) +var m=Math.atan(u/c),h=Math.sqrt(Math.pow(c,2)+Math.pow(u,2)),v=h?c/Math.cos(Math.PI/2+m)/h:1,w=" scale("+Math.abs(v)+")" +t.setTransform(e,g+w),t.setTransform(l,g+w),f&&(i.css("visibility","visible").insertAfter(o).addClass("file-zoom-detail"),o.remove(),s.remove())}))},_validateImageOrientation:function(i,a,r,n,o,s,l,d){var c,u,p=this,f=null,g=p.autoOrientImage +return p.canOrientImage?(i.css("image-orientation",g?"from-image":"none"),void p._validateImage(r,n,o,s,l,d,f)):(u=t.getZoomSelector(r," img"),f=g?p._getExifObj(d):null,(c=f?f["0th"][piexif.ImageIFD.Orientation]:null)?(p.setImageOrientation(i,e(u),c,p._getFrame(r)),p._raise("fileimageoriented",{$img:i,file:a}),void p._validateImage(r,n,o,s,l,d,f)):void p._validateImage(r,n,o,s,l,d,f))},_validateImage:function(e,t,i,a,r,n,o){var s,l,d,c=this,u=c.$preview,p=c._getFrame(e),f=p.attr("data-fileindex"),g=p.find("img") +i=i||"Untitled",g.one("load",function(){l=p.width(),d=u.width(),l>d&&g.css("width","100%"),s={ind:f,id:e,fileId:t},c._checkDimensions(f,"Small",g,p,i,"Width",s),c._checkDimensions(f,"Small",g,p,i,"Height",s),c.resizeImage||(c._checkDimensions(f,"Large",g,p,i,"Width",s),c._checkDimensions(f,"Large",g,p,i,"Height",s)),c._raise("fileimageloaded",[e]),c.fileManager.addImage(t,{ind:f,img:g,thumb:p,pid:e,typ:a,siz:r,validated:!1,imgData:n,exifObj:o}),p.data("exif",o),c._validateAllImages()}).one("error",function(){c._raise("fileimageloaderror",[e])})},_validateAllImages:function(){var t,i=this,a={val:0},r=i.fileManager.getImageCount(),n=i.resizeIfSizeMoreThan +r===i.fileManager.totalImages&&(i._raise("fileimagesloaded"),i.resizeImage&&e.each(i.fileManager.loadedImages,function(e,o){o.validated||(t=o.siz,t&&t>n*i.bytesToKB&&i._getResizedImage(e,o,a,r),o.validated=!0)}))},_getResizedImage:function(i,a,r,n){var o,s,l,d,c,u,p,f,g,m,h=this,v=e(a.img)[0],w=v.naturalWidth,b=v.naturalHeight,_=1,C=h.maxImageWidth||w,y=h.maxImageHeight||b,x=!(!w||!b),T=h.imageCanvas,P=h.imageCanvasContext,F=a.typ,k=a.pid,E=a.ind,S=a.thumb,I=a.exifObj +if(c=function(e,t,i){h.isAjaxUpload?h._showFileError(e,t,i):h._showError(e,t,i),h._setPreviewError(S)},f=h.fileManager.getFile(i),g={id:k,index:E,fileId:i},m=[i,k,E],(!f||!x||C>=w&&y>=b)&&(x&&f&&h._raise("fileimageresized",m),r.val++,r.val===n&&h._raise("fileimagesresized"),!x))return void c(h.msgImageResizeError,g,"fileimageresizeerror") +F=F||h.resizeDefaultImageType,s=w>C,l=b>y,_="width"===h.resizePreference?s?C/w:l?y/b:1:l?y/b:s?C/w:1,h._resetCanvas(),w*=_,b*=_,T.width=w,T.height=b +try{P.drawImage(v,0,0,w,b),d=T.toDataURL(F,h.resizeQuality),I&&(p=window.piexif.dump(I),d=window.piexif.insert(p,d)),o=t.dataURI2Blob(d),h.fileManager.setFile(i,o),h._raise("fileimageresized",m),r.val++,r.val===n&&h._raise("fileimagesresized",[void 0,void 0]),o instanceof Blob||c(h.msgImageResizeError,g,"fileimageresizeerror")}catch(A){r.val++,r.val===n&&h._raise("fileimagesresized",[void 0,void 0]),u=h.msgImageResizeException.replace("{errors}",A.message),c(u,g,"fileimageresizeexception")}},_showProgress:function(){var e=this +e.$progress&&e.$progress.length&&e.$progress.show()},_hideProgress:function(){var e=this +e.$progress&&e.$progress.length&&e.$progress.hide()},_initBrowse:function(e){var i=this,a=i.$element +i.showBrowse?i.$btnFile=e.find(".btn-file").append(a):(a.appendTo(e).attr("tabindex",-1),t.addCss(a,"file-no-browse"))},_initClickable:function(){var i,a,r=this +r.isClickable&&(i=r.$dropZone,r.isAjaxUpload||(a=r.$preview.find(".file-default-preview"),a.length&&(i=a)),t.addCss(i,"clickable"),i.attr("tabindex",-1),r._handler(i,"click",function(t){var a=e(t.target) +r.$errorContainer.is(":visible")||a.parents(".file-preview-thumbnails").length&&!a.parents(".file-default-preview").length||(r.$element.data("zoneClicked",!0).trigger("click"),i.blur())}))},_initCaption:function(){var e=this,i=e.initialCaption||"" +return e.overwriteInitial||t.isEmpty(i)?(e.$caption.val(""),!1):(e._setCaption(i),!0)},_setCaption:function(i,a){var r,n,o,s,l,d,c=this +if(c.$caption.length){if(c.$captionContainer.removeClass("icon-visible"),a)r=e("
    "+c.msgValidationError+"
    ").text(),s=c.fileManager.count(),s?(d=c.fileManager.getFirstFile(),l=1===s&&d?d.nameFmt:c._getMsgSelected(s)):l=c._getMsgSelected(c.msgNo),n=t.isEmpty(i)?l:i,o=''+c.msgValidationErrorIcon+"" +else{if(t.isEmpty(i))return void c.$caption.attr("title","") +r=e("
    "+i+"
    ").text(),n=r,o=c._getLayoutTemplate("fileIcon")}c.$captionContainer.addClass("icon-visible"),c.$caption.attr("title",r).val(n),t.setHtml(c.$captionIcon,o)}},_createContainer:function(){var e=this,i={"class":"file-input file-input-new"+(e.rtl?" kv-rtl":"")},a=t.createElement(t.cspBuffer.stash(e._renderMain())) +return t.cspBuffer.apply(a),a.insertBefore(e.$element).attr(i),e._initBrowse(a),e.theme&&a.addClass("theme-"+e.theme),a},_refreshContainer:function(){var e=this,i=e.$container,a=e.$element +a.insertAfter(i),t.setHtml(i,e._renderMain()),e._initBrowse(i),e._validateDisabled()},_validateDisabled:function(){var e=this +e.$caption.attr({readonly:e.isDisabled})},_setTabIndex:function(e,t){var i=this,a=i.tabIndexConfig[e] +return t.setTokens({tabIndexConfig:void 0===a||null===a?"":'tabindex="'+a+'"'})},_renderMain:function(){var e=this,t=e.dropZoneEnabled?" file-drop-zone":"file-drop-disabled",i=e.showClose?e._getLayoutTemplate("close"):"",a=e.showPreview?e._getLayoutTemplate("preview").setTokens({"class":e.previewClass,dropClass:t}):"",r=e.isDisabled?e.captionClass+" file-caption-disabled":e.captionClass,n=e.captionTemplate.setTokens({"class":r+" kv-fileinput-caption"}) +return n=e._setTabIndex("caption",n),e.mainTemplate.setTokens({"class":e.mainClass+(!e.showBrowse&&e.showCaption?" no-browse":""),inputGroupClass:e.inputGroupClass,preview:a,close:i,caption:n,upload:e._renderButton("upload"),remove:e._renderButton("remove"),cancel:e._renderButton("cancel"),pause:e._renderButton("pause"),browse:e._renderButton("browse")})},_renderButton:function(e){var i=this,a=i._getLayoutTemplate("btnDefault"),r=i[e+"Class"],n=i[e+"Title"],o=i[e+"Icon"],s=i[e+"Label"],l=i.isDisabled?" disabled":"",d="button" +switch(e){case"remove":if(!i.showRemove)return"" +break +case"cancel":if(!i.showCancel)return"" +r+=" kv-hidden" +break +case"pause":if(!i.showPause)return"" +r+=" kv-hidden" +break +case"upload":if(!i.showUpload)return"" +i.isAjaxUpload&&!i.isDisabled?a=i._getLayoutTemplate("btnLink").replace("{href}",i.uploadUrl):d="submit" +break +case"browse":if(!i.showBrowse)return"" +a=i._getLayoutTemplate("btnBrowse") +break +default:return""}return a=i._setTabIndex(e,a),r+="browse"===e?" btn-file":" fileinput-"+e+" fileinput-"+e+"-button",t.isEmpty(s)||(s=' '+s+""),a.setTokens({type:d,css:r,title:n,status:l,icon:o,label:s})},_renderThumbProgress:function(){var e=this +return'
    '+e.progressInfoTemplate.setTokens({percent:101,status:e.msgUploadBegin,stats:""})+"
    "},_renderFileFooter:function(e,i,a,r,n){var o,s,l=this,d=l.fileActionSettings,c=d.showRemove,u=d.showDrag,p=d.showUpload,f=d.showZoom,g=l._getLayoutTemplate("footer"),m=l._getLayoutTemplate("indicator"),h=n?d.indicatorError:d.indicatorNew,v=n?d.indicatorErrorTitle:d.indicatorNewTitle,w=m.setTokens({indicator:h,indicatorTitle:v}) +return a=l._getSize(a),s={type:e,caption:i,size:a,width:r,progress:"",indicator:w},l.isAjaxUpload?(s.progress=l._renderThumbProgress(),s.actions=l._renderFileActions(s,p,!1,c,f,u,!1,!1,!1)):s.actions=l._renderFileActions(s,!1,!1,!1,f,u,!1,!1,!1),o=g.setTokens(s),o=t.replaceTags(o,l.previewThumbTags)},_renderFileActions:function(e,t,i,a,r,n,o,s,l,d,c,u){var p=this +if(!e.type&&d&&(e.type="image"),p.enableResumableUpload?t=!1:"function"==typeof t&&(t=t(e)),"function"==typeof i&&(i=i(e)),"function"==typeof a&&(a=a(e)),"function"==typeof r&&(r=r(e)),"function"==typeof n&&(n=n(e)),!(t||i||a||r||n))return"" +var f,g=s===!1?"":' data-url="'+s+'"',m="",h="",v=l===!1?"":' data-key="'+l+'"',w="",b="",_="",C=p._getLayoutTemplate("actions"),y=p.fileActionSettings,x=p.otherActionButtons.setTokens({dataKey:v,key:l}),T=o?y.removeClass+" disabled":y.removeClass +return a&&(w=p._getLayoutTemplate("actionDelete").setTokens({removeClass:T,removeIcon:y.removeIcon,removeTitle:y.removeTitle,dataUrl:g,dataKey:v,key:l})),t&&(b=p._getLayoutTemplate("actionUpload").setTokens({uploadClass:y.uploadClass,uploadIcon:y.uploadIcon,uploadTitle:y.uploadTitle})),i&&(_=p._getLayoutTemplate("actionDownload").setTokens({downloadClass:y.downloadClass,downloadIcon:y.downloadIcon,downloadTitle:y.downloadTitle,downloadUrl:c||p.initialPreviewDownloadUrl}),_=_.setTokens({filename:u,key:l})),r&&(m=p._getLayoutTemplate("actionZoom").setTokens({zoomClass:y.zoomClass,zoomIcon:y.zoomIcon,zoomTitle:y.zoomTitle})),n&&d&&(f="drag-handle-init "+y.dragClass,h=p._getLayoutTemplate("actionDrag").setTokens({dragClass:f,dragTitle:y.dragTitle,dragIcon:y.dragIcon})),C.setTokens({"delete":w,upload:b,download:_,zoom:m,drag:h,other:x})},_browse:function(e){var t=this +e&&e.isDefaultPrevented()||!t._raise("filebrowse")||(t.isError&&!t.isAjaxUpload&&t.clear(),t.focusCaptionOnBrowse&&t.$captionContainer.focus())},_change:function(i){var a=this +if(e(document.body).off("focusin.fileinput focusout.fileinput"),!a.changeTriggered){a._setLoading("show") +var r,n,o,s,l=a.$element,d=arguments.length>1,c=a.isAjaxUpload,u=d?arguments[1]:l[0].files,p=a.fileManager.count(),f=t.isEmpty(l.attr("multiple")),g=!c&&f?1:a.maxFileCount,m=a.maxTotalFileCount,h=m>0&&m>g,v=f&&p>0,w=function(t,i,r,n){var o=e.extend(!0,{},a._getOutData(null,{},{},u),{id:r,index:n}),s={id:r,index:n,file:i,files:u} +return a.isPersistentError=!0,a._setLoading("hide"),c?a._showFileError(t,o):a._showError(t,s)},b=function(e,t,i){var r=i?a.msgTotalFilesTooMany:a.msgFilesTooMany +r=r.replace("{m}",t).replace("{n}",e),a.isError=w(r,null,null,null),a.$captionContainer.removeClass("icon-visible"),a._setCaption("",!0),a.$container.removeClass("file-input-new file-input-ajax-new")} +if(a.reader=null,a._resetUpload(),a._hideFileIcon(),a.dropZoneEnabled&&a.$container.find(".file-drop-zone ."+a.dropZoneTitleClass).remove(),c||(u=i.target&&void 0===i.target.files?i.target.value?[{name:i.target.value.replace(/^.+\\/,"")}]:[]:i.target.files||{}),r=u,t.isEmpty(r)||0===r.length)return c||a.clear(),void a._raise("fileselectnone") +if(a._resetErrors(),s=r.length,o=c?a.fileManager.count()+s:s,n=a._getFileCount(o,h?!1:void 0),g>0&&n>g){if(!a.autoReplace||s>g)return void b(a.autoReplace&&s>g?s:n,g) +n>g&&a._resetPreviewThumbs(c)}else{if(h&&(n=a._getFileCount(o,!0),m>0&&n>m)){if(!a.autoReplace||s>g)return void b(a.autoReplace&&s>m?s:n,m,!0) +n>g&&a._resetPreviewThumbs(c)}!c||v?(a._resetPreviewThumbs(!1),v&&a.clearFileStack()):!c||0!==p||a.previewCache.count(!0)&&!a.overwriteInitial||a._resetPreviewThumbs(!0)}a.readFiles(r),a._setLoading("hide")}},_abort:function(t){var i,a=this +return a.ajaxAborted&&"object"==typeof a.ajaxAborted&&void 0!==a.ajaxAborted.message?(i=e.extend(!0,{},a._getOutData(null),t),i.abortData=a.ajaxAborted.data||{},i.abortMessage=a.ajaxAborted.message,a._setProgress(101,a.$progress,a.msgCancelled),a._showFileError(a.ajaxAborted.message,i,"filecustomerror"),a.cancel(),a.unlock(),!0):!!a.ajaxAborted},_resetFileStack:function(){var t=this,i=0 +t._getThumbs().each(function(){var a=e(this),r=a.attr("data-fileindex"),n=a.attr("id") +"-1"!==r&&-1!==r&&(t._getThumbFile(a)?a.attr({"data-fileindex":"-1"}):(a.attr({"data-fileindex":i}),i++),t._getZoom(n).attr({"data-fileindex":a.attr("data-fileindex")}))})},_isFileSelectionValid:function(e){var t=this +return e=e||0,t.required&&!t.getFilesCount()?(t.$errorContainer.html(""),t._showFileError(t.msgFileRequired),!1):t.minFileCount>0&&t._getFileCount(e)v,!o&&(a||r||n)},addToStack:function(e,t){this.fileManager.add(e,t)},clearFileStack:function(){var e=this +return e.fileManager.clear(),e._initResumableUpload(),e.enableResumableUpload?(null===e.showPause&&(e.showPause=!0),null===e.showCancel&&(e.showCancel=!1)):(e.showPause=!1,null===e.showCancel&&(e.showCancel=!0)),e.$element},getFileStack:function(){return this.fileManager.stack},getFileList:function(){return this.fileManager.list()},getFilesSize:function(){return this.fileManager.getTotalSize()},getFilesCount:function(e){var t=this,i=t.isAjaxUpload?t.fileManager.count():t._inputFileCount() +return e&&(i+=t.previewCache.count(!0)),t._getFileCount(i)},_initCapStatus:function(e){var t=this,i=t.$caption +i.removeClass("is-valid file-processing"),e&&("processing"===e?i.addClass("file-processing"):i.addClass("is-valid"))},_setLoading:function(e){var t=this +t.$previewStatus.html("hide"===e?"":t.msgProcessing),t.$container.removeClass("file-thumb-loading"),t._initCapStatus("hide"===e?"":"processing"),"hide"!==e&&(t.dropZoneEnabled&&t.$container.find(".file-drop-zone ."+t.dropZoneTitleClass).remove(),t.$container.addClass("file-thumb-loading"))},_initFileSelected:function(){var t=this,i=t.$element,a=e(document.body),r="focusin.fileinput focusout.fileinput" +a.length?a.off(r).on("focusout.fileinput",function(){t._setLoading("show")}).on("focusin.fileinput",function(){setTimeout(function(){i.val()||(t._setLoading("hide"),t._setFileDropZoneTitle()),a.off(r)},2500)}):t._setLoading("hide")},readFiles:function(i){this.reader=new FileReader +var a,r=this,n=r.reader,o=r.$previewContainer,s=r.$previewStatus,l=r.msgLoading,d=r.msgProgress,c=r.previewInitId,u=i.length,p=r.fileTypeSettings,f=r.allowedFileTypes,g=f?f.length:0,m=r.allowedFileExtensions,h=t.isEmpty(m)?"":m.join(", "),v=function(t,n,o,s,l){var d,c=e.extend(!0,{},r._getOutData(null,{},{},i),{id:o,index:s,fileId:l}),p={id:o,index:s,fileId:l,file:n,files:i} +r._previewDefault(n,!0),d=r._getFrame(o,!0),r._setLoading("hide"),r.isAjaxUpload?setTimeout(function(){a(s+1)},r.processDelay):(r.unlock(),u=0),r.removeFromPreviewOnError&&d.length?d.remove():(r._initFileActions(),d.find(".kv-file-upload").remove()),r.isPersistentError=!0,r.isError=r.isAjaxUpload?r._showFileError(t,c):r._showError(t,p),r._updateFileDetails(u)} +r.fileManager.clearImages(),e.each(i,function(e,t){var i=r.fileTypeSettings.image +i&&i(t.type)&&r.fileManager.totalImages++}),a=function(w){var b,_=r.$errorContainer,C=r.fileManager +if(w>=u)return r.unlock(),r.duplicateErrors.length&&(b="
  • "+r.duplicateErrors.join("
  • ")+"
  • ",0===_.find("ul").length?t.setHtml(_,r.errorCloseButton+"
      "+b+"
    "):_.find("ul").append(b),_.fadeIn(r.fadeDelay),r._handler(_.find(".kv-error-close"),"click",function(){_.fadeOut(r.fadeDelay)}),r.duplicateErrors=[]),r.isAjaxUpload?(r._raise("filebatchselected",[C.stack]),0!==C.count()||r.isError||r.reset()):r._raise("filebatchselected",[i]),o.removeClass("file-thumb-loading"),r._initCapStatus("valid"),void s.html("") +r.lock(!0) +var y,x,T,P,F,k,E,S,I,A,z,D,U=i[w],j=r._getFileId(U),$=c+"-"+j,M=p.image,B=r._getFileName(U,""),R=(U&&U.size||0)/r.bytesToKB,O="",L=t.createObjectURL(U),N=0,Z="",H=!1,W=0,K=function(){var e=!!C.loadedImages[j],t=d.setTokens({index:w+1,files:u,percent:50,name:B}) +setTimeout(function(){s.html(t),r._updateFileDetails(u),a(w+1)},r.processDelay),r._raise("fileloaded",[U,$,j,w,n])&&r.isAjaxUpload?e||C.add(U):e&&C.removeFile(j)} +if(U){if(S=C.getId(U),g>0)for(x=0;g>x;x++)k=f[x],E=r.msgFileTypes[k]||k,Z+=0===x?E:", "+E +if(B===!1)return void a(w+1) +if(0===B.length)return T=r.msgInvalidFileName.replace("{name}",t.htmlEncode(t.getFileName(U),"[unknown]")),void v(T,U,$,w,S) +if(t.isEmpty(m)||(O=RegExp("\\.("+m.join("|")+")$","i")),y=R.toFixed(2),r.isAjaxUpload&&C.exists(S)||r._getFrame($,!0).length){var q={id:$,index:w,fileId:S,file:U,files:i} +return T=r.msgDuplicateFile.setTokens({name:B,size:y}),void(r.isAjaxUpload?(r.duplicateErrors.push(T),r.isDuplicateError=!0,r._raise("fileduplicateerror",[U,S,B,y,$,w]),a(w+1),r._updateFileDetails(u)):(r._showError(T,q),r.unlock(),u=0,r._clearFileInput(),r.reset(),r._updateFileDetails(u)))}if(r.maxFileSize>0&&R>r.maxFileSize)return T=r.msgSizeTooLarge.setTokens({name:B,size:y,maxSize:r.maxFileSize}),void v(T,U,$,w,S) +if(null!==r.minFileSize&&R<=t.getNum(r.minFileSize))return T=r.msgSizeTooSmall.setTokens({name:B,size:y,minSize:r.minFileSize}),void v(T,U,$,w,S) +if(!t.isEmpty(f)&&t.isArray(f)){for(x=0;x0)for(t=0;n>t;t+=1)i.paused=!0,r[t].abort() +return i.showPreview&&i._getThumbs().each(function(){var t,a=e(this),r=i._getLayoutTemplate("stats"),n=a.find(".file-upload-indicator") +a.removeClass("file-uploading"),n.attr("title")===s.indicatorLoadingTitle&&(i._setThumbStatus(a,"Paused"),t=r.setTokens({pendingTime:i.msgPaused,uploadSpeed:""}),i.paused=!0,i._setProgress(o,a.find(".file-thumb-progress"),o+"%",t)),i._getThumbFile(a)||a.find(".kv-file-remove").removeClass("disabled").removeAttr("disabled")}),i._setProgress(101,i.$progress,i.msgPaused),i.$element},cancel:function(){var t,i=this,a=i.ajaxRequests,r=i.resumableManager,n=i.taskManager,o=r?n.getPool(r.id):void 0,s=a.length +if(i.enableResumableUpload&&o?(o.cancel().done(function(){i._setProgressCancelled()}),r.reset(),i._raise("fileuploadcancelled",[i.fileManager,r])):i._raise("fileuploadcancelled",[i.fileManager]),i._initAjax(),s>0)for(t=0;s>t;t+=1)i.cancelling=!0,a[t].abort() +return i._getThumbs().each(function(){var t=e(this),a=t.find(".file-thumb-progress") +t.removeClass("file-uploading"),i._setProgress(0,a),a.hide(),i._getThumbFile(t)||(t.find(".kv-file-upload").removeClass("disabled").removeAttr("disabled"),t.find(".kv-file-remove").removeClass("disabled").removeAttr("disabled")),i.unlock()}),setTimeout(function(){i._setProgressCancelled()},i.processDelay),i.$element},clear:function(){var i,a=this +if(a._raise("fileclear"))return a.$btnUpload.removeAttr("disabled"),a._getThumbs().find("video,audio,img").each(function(){t.cleanMemory(e(this))}),a._clearFileInput(),a._resetUpload(),a.clearFileStack(),a.isDuplicateError=!1,a.isPersistentError=!1,a._resetErrors(!0),a._hasInitialPreview()?(a._showFileIcon(),a._resetPreview(),a._initPreviewActions(),a.$container.removeClass("file-input-new")):(a._getThumbs().each(function(){a._clearObjects(e(this))}),a.isAjaxUpload&&(a.previewCache.data={}),a.$preview.html(""),i=!a.overwriteInitial&&a.initialCaption.length>0?a.initialCaption:"",a.$caption.attr("title","").val(i),t.addCss(a.$container,"file-input-new"),a._validateDefaultPreview()),0===a.$container.find(t.FRAMES).length&&(a._initCaption()||a.$captionContainer.removeClass("icon-visible")),a._hideFileIcon(),a.focusCaptionOnClear&&a.$captionContainer.focus(),a._setFileDropZoneTitle(),a._raise("filecleared"),a.$element},reset:function(){var e=this +if(e._raise("filereset"))return e.lastProgress=0,e._resetPreview(),e.$container.find(".fileinput-filename").text(""),t.addCss(e.$container,"file-input-new"),e.getFrames().length&&e.$container.removeClass("file-input-new"),e.clearFileStack(),e._setFileDropZoneTitle(),e.$element},disable:function(){var e=this,i=e.$container +return e.isDisabled=!0,e._raise("filedisabled"),e.$element.attr("disabled","disabled"),i.addClass("is-locked"),t.addCss(i.find(".btn-file"),"disabled"),i.find(".kv-fileinput-caption").addClass("file-caption-disabled"),i.find(".fileinput-remove, .fileinput-upload, .file-preview-frame button").attr("disabled",!0),e._initDragDrop(),e.$element},enable:function(){var e=this,t=e.$container +return e.isDisabled=!1,e._raise("fileenabled"),e.$element.removeAttr("disabled"),t.removeClass("is-locked"),t.find(".kv-fileinput-caption").removeClass("file-caption-disabled"),t.find(".fileinput-remove, .fileinput-upload, .file-preview-frame button").removeAttr("disabled"),t.find(".btn-file").removeClass("disabled"),e._initDragDrop(),e.$element},upload:function(){var i,a,r=this,n=r.fileManager,o=n.count(),s=!e.isEmptyObject(r._getExtraData()) +if(n.bpsLog=[],n.bps=0,r.isAjaxUpload&&!r.isDisabled&&r._isFileSelectionValid(o)){if(r.lastProgress=0,r._resetUpload(),0===o&&!s)return void r._showFileError(r.msgUploadEmpty) +if(r.cancelling=!1,r._showProgress(),r.lock(),0===o&&s)return r._setProgress(2),void r._uploadExtraOnly() +if(r.enableResumableUpload)return r.resume() +if(r.uploadAsync||r.enableResumableUpload){if(a=r._getOutData(null),!r._checkBatchPreupload(a))return +r.fileBatchCompleted=!1,r.uploadCache=[],e.each(r.getFileStack(),function(e){var t=r._getThumbId(e) +r.uploadCache.push({id:t,content:null,config:null,tags:null,append:!0})}),r.$preview.find(".file-preview-initial").removeClass(t.SORT_CSS),r._initSortable()}return r._setProgress(2),r.hasInitData=!1,r.uploadAsync?(i=0,void e.each(r.getFileStack(),function(e){r._uploadSingle(i,e,!0),i++})):(r._uploadBatch(),r.$element)}},destroy:function(){var t=this,i=t.$form,a=t.$container,r=t.$element,n=t.namespace +return e(document).off(n),e(window).off(n),i&&i.length&&i.off(n),t.isAjaxUpload&&t._clearFileInput(),t._cleanup(),t._initPreviewCache(),r.insertBefore(a).off(n).removeData(),a.off().remove(),r},refresh:function(i){var a=this,r=a.$element +return i="object"!=typeof i||t.isEmpty(i)?a.options:e.extend(!0,{},a.options,i),a._init(i,!0),a._listen(),r},zoom:function(e){var t=this,i=t._getFrame(e) +t._showModal(i)},getExif:function(e){var t=this,i=t._getFrame(e) +return i&&i.data("exif")||null},getFrames:function(i){var a,r=this +return i=i||"",a=r.$preview.find(t.FRAMES+i),r.reversePreviewOrder&&(a=e(a.get().reverse())),a},getPreview:function(){var e=this +return{content:e.initialPreview,config:e.initialPreviewConfig,tags:e.initialPreviewThumbTags}}},e.fn.fileinput=function(a){if(t.hasFileAPISupport()||t.isIE(9)){var r=Array.apply(null,arguments),n=[] +switch(r.shift(),this.each(function(){var o,s=e(this),l=s.data("fileinput"),d="object"==typeof a&&a,c=d.theme||s.data("theme"),u={},p={},f=d.language||s.data("language")||e.fn.fileinput.defaults.language||"en" +l||(c&&(p=e.fn.fileinputThemes[c]||{}),"en"===f||t.isEmpty(e.fn.fileinputLocales[f])||(u=e.fn.fileinputLocales[f]||{}),o=e.extend(!0,{},e.fn.fileinput.defaults,p,e.fn.fileinputLocales.en,u,d,s.data()),l=new i(this,o),s.data("fileinput",l)),"string"==typeof a&&n.push(l[a].apply(l,r))}),n.length){case 0:return this +case 1:return n[0] +default:return n}}} +var a='class="kv-preview-data file-preview-pdf" src="{renderer}?file={data}" {style}',r="btn btn-sm btn-kv "+t.defaultButtonCss(),n="btn "+t.defaultButtonCss(!0) +e.fn.fileinput.defaults={language:"zh",bytesToKB:1024,showCaption:!0,showBrowse:!0,showPreview:!0,showRemove:!0,showUpload:!0,showUploadStats:!0,showCancel:null,showPause:null,showClose:!0,showUploadedThumbs:!0,showConsoleLogs:!1,browseOnZoneClick:!1,autoReplace:!1,showDescriptionClose:!0,autoOrientImage:function(){var e=window.navigator.userAgent,t=!!e.match(/WebKit/i),i=!!e.match(/iP(od|ad|hone)/i),a=i&&t&&!e.match(/CriOS/i) +return!a},autoOrientImageInitial:!0,required:!1,rtl:!1,hideThumbnailContent:!1,encodeUrl:!0,focusCaptionOnBrowse:!0,focusCaptionOnClear:!0,generateFileId:null,previewClass:"",captionClass:"",frameClass:"krajee-default",mainClass:"",inputGroupClass:"",mainTemplate:null,fileSizeGetter:null,initialCaption:"",initialPreview:[],initialPreviewDelimiter:"*$$*",initialPreviewAsData:!1,initialPreviewFileType:"image",initialPreviewConfig:[],initialPreviewThumbTags:[],previewThumbTags:{},initialPreviewShowDelete:!0,initialPreviewDownloadUrl:"",removeFromPreviewOnError:!1,deleteUrl:"",deleteExtraData:{},overwriteInitial:!0,sanitizeZoomCache:function(e){var i=t.createElement(e) +return i.find("input,textarea,select,datalist,form,.file-thumbnail-footer").remove(),i.html()},previewZoomButtonIcons:{prev:'',next:'',toggleheader:'',fullscreen:'',borderless:'',close:''},previewZoomButtonClasses:{prev:"btn btn-default btn-outline-secondary btn-navigate",next:"btn btn-default btn-outline-secondary btn-navigate",toggleheader:r,fullscreen:r,borderless:r,close:r},previewTemplates:{},previewContentTemplates:{},preferIconicPreview:!1,preferIconicZoomPreview:!1,allowedFileTypes:null,allowedFileExtensions:null,allowedPreviewTypes:void 0,allowedPreviewMimeTypes:null,allowedPreviewExtensions:null,disabledPreviewTypes:void 0,disabledPreviewExtensions:["msi","exe","com","zip","rar","app","vb","scr"],disabledPreviewMimeTypes:null,defaultPreviewContent:null,customLayoutTags:{},customPreviewTags:{},previewFileIcon:'',previewFileIconClass:"file-other-icon",previewFileIconSettings:{},previewFileExtSettings:{},buttonLabelClass:"hidden-xs",browseIcon:' ',browseClass:"btn btn-primary",removeIcon:'',removeClass:n,cancelIcon:'',cancelClass:n,pauseIcon:'',pauseClass:n,uploadIcon:'',uploadClass:n,uploadUrl:null,uploadUrlThumb:null,uploadAsync:!0,uploadParamNames:{chunkCount:"chunkCount",chunkIndex:"chunkIndex",chunkSize:"chunkSize",chunkSizeStart:"chunkSizeStart",chunksUploaded:"chunksUploaded",fileBlob:"fileBlob",fileId:"fileId",fileName:"fileName",fileRelativePath:"fileRelativePath",fileSize:"fileSize",retryCount:"retryCount"},maxAjaxThreads:5,fadeDelay:800,processDelay:100,bitrateUpdateDelay:500,queueDelay:10,progressDelay:0,enableResumableUpload:!1,resumableUploadOptions:{fallback:null,testUrl:null,chunkSize:2048,maxThreads:4,maxRetries:3,showErrorLog:!0,retainErrorHistory:!0,skipErrorsAndProceed:!1},uploadExtraData:{},zoomModalHeight:480,minImageWidth:null,minImageHeight:null,maxImageWidth:null,maxImageHeight:null,resizeImage:!1,resizePreference:"width",resizeQuality:.92,resizeDefaultImageType:"image/jpeg",resizeIfSizeMoreThan:0,minFileSize:-1,maxFileSize:0,maxFilePreviewSize:25600,minFileCount:0,maxFileCount:0,maxTotalFileCount:0,validateInitialCount:!1,msgValidationErrorClass:"text-danger",msgValidationErrorIcon:' ',msgErrorClass:"file-error-message",progressThumbClass:"progress-bar progress-bar-striped active progress-bar-animated",progressClass:"progress-bar bg-success progress-bar-success progress-bar-striped active progress-bar-animated",progressInfoClass:"progress-bar bg-info progress-bar-info progress-bar-striped active progress-bar-animated",progressCompleteClass:"progress-bar bg-success progress-bar-success",progressPauseClass:"progress-bar bg-primary progress-bar-primary progress-bar-striped active progress-bar-animated",progressErrorClass:"progress-bar bg-danger progress-bar-danger",progressUploadThreshold:99,previewFileType:"image",elCaptionContainer:null,elCaptionText:null,elPreviewContainer:null,elPreviewImage:null,elPreviewStatus:null,elErrorContainer:null,errorCloseButton:void 0,slugCallback:null,dropZoneEnabled:!0,dropZoneTitleClass:"file-drop-zone-title",fileActionSettings:{},otherActionButtons:"",textEncoding:"UTF-8",preProcessUpload:null,ajaxSettings:{},ajaxDeleteSettings:{},showAjaxErrorDetails:!0,mergeAjaxCallbacks:!1,mergeAjaxDeleteCallbacks:!1,retryErrorUploads:!0,reversePreviewOrder:!1,usePdfRenderer:function(){var e=!!window.MSInputMethodContext&&!!document.documentMode +return!!navigator.userAgent.match(/(iPod|iPhone|iPad|Android)/i)||e},pdfRendererUrl:"",pdfRendererTemplate:"",tabIndexConfig:{browse:500,remove:500,upload:500,cancel:null,pause:null,modal:-1}},e.fn.fileinputLocales.en={sizeUnits:["B","KB","MB","GB","TB","PB","EB","ZB","YB"],bitRateUnits:["B/s","KB/s","MB/s","GB/s","TB/s","PB/s","EB/s","ZB/s","YB/s"],fileSingle:"file",filePlural:"files",browseLabel:"Browse …",removeLabel:"Remove",removeTitle:"Clear all unprocessed files",cancelLabel:"Cancel",cancelTitle:"Abort ongoing upload",pauseLabel:"Pause",pauseTitle:"Pause ongoing upload",uploadLabel:"Upload",uploadTitle:"Upload selected files",msgNo:"No",msgNoFilesSelected:"No files selected",msgCancelled:"Cancelled",msgPaused:"Paused",msgPlaceholder:"Select {files} ...",msgZoomModalHeading:"Detailed Preview",msgFileRequired:"You must select a file to upload.",msgSizeTooSmall:'File "{name}" ({size} KB) is too small and must be larger than {minSize} KB.',msgSizeTooLarge:'File "{name}" ({size} KB) exceeds maximum allowed upload size of {maxSize} KB.',msgFilesTooLess:"You must select at least {n} {files} to upload.",msgFilesTooMany:"Number of files selected for upload ({n}) exceeds maximum allowed limit of {m}.",msgTotalFilesTooMany:"You can upload a maximum of {m} files ({n} files detected).",msgFileNotFound:'File "{name}" not found!',msgFileSecured:'Security restrictions prevent reading the file "{name}".',msgFileNotReadable:'File "{name}" is not readable.',msgFilePreviewAborted:'File preview aborted for "{name}".',msgFilePreviewError:'An error occurred while reading the file "{name}".',msgInvalidFileName:'Invalid or unsupported characters in file name "{name}".',msgInvalidFileType:'Invalid type for file "{name}". Only "{types}" files are supported.',msgInvalidFileExtension:'Invalid extension for file "{name}". Only "{extensions}" files are supported.',msgFileTypes:{image:"image",html:"HTML",text:"text",video:"video",audio:"audio",flash:"flash",pdf:"PDF",object:"object"},msgUploadAborted:"The file upload was aborted",msgUploadThreshold:"Processing …",msgUploadBegin:"Initializing …",msgUploadEnd:"Done",msgUploadResume:"Resuming upload …",msgUploadEmpty:"No valid data available for upload.",msgUploadError:"Upload Error",msgDeleteError:"Delete Error",msgProgressError:"Error",msgValidationError:"Validation Error",msgLoading:"Loading file {index} of {files} …",msgProgress:"Loading file {index} of {files} - {name} - {percent}% completed.",msgSelected:"{n} {files} selected",msgProcessing:"Processing ...",msgFoldersNotAllowed:"Drag & drop files only! {n} folder(s) dropped were skipped.",msgImageWidthSmall:'Width of image file "{name}" must be at least {size} px.',msgImageHeightSmall:'Height of image file "{name}" must be at least {size} px.',msgImageWidthLarge:'Width of image file "{name}" cannot exceed {size} px.',msgImageHeightLarge:'Height of image file "{name}" cannot exceed {size} px.',msgImageResizeError:"Could not get the image dimensions to resize.",msgImageResizeException:"Error while resizing the image.
    {errors}
    ",msgAjaxError:"Something went wrong with the {operation} operation. Please try again later!",msgAjaxProgressError:"{operation} failed",msgDuplicateFile:'File "{name}" of same size "{size} KB" has already been selected earlier. Skipping duplicate selection.',msgResumableUploadRetriesExceeded:"Upload aborted beyond {max} retries for file {file}! Error Details:
    {error}
    ",msgPendingTime:"{time} remaining",msgCalculatingTime:"calculating time remaining",ajaxOperations:{deleteThumb:"file delete",uploadThumb:"file upload",uploadBatch:"batch file upload",uploadExtra:"form data upload"},dropZoneTitle:"Drag & drop files here …",dropZoneClickTitle:"
    (or click to select {files})",previewZoomButtonTitles:{prev:"View previous file",next:"View next file",toggleheader:"Toggle header",fullscreen:"Toggle full screen",borderless:"Toggle borderless mode",close:"Close detailed preview"}},e.fn.fileinputLocales.zh={sizeUnits:["B","KB","MB","GB","TB","PB","EB","ZB","YB"],bitRateUnits:["B/s","KB/s","MB/s","GB/s","TB/s","PB/s","EB/s","ZB/s","YB/s"],fileSingle:"文件",filePlural:"个文件",browseLabel:"选择 …",removeLabel:"移除",removeTitle:"清除选中文件",cancelLabel:"取消",cancelTitle:"取消进行中的上传",pauseLabel:"暂停",pauseTitle:"暂停上传",uploadLabel:"上传",uploadTitle:"上传选中文件",msgNo:"没有",msgNoFilesSelected:"未选择文件",msgPaused:"已暂停",msgCancelled:"取消",msgPlaceholder:"选择 {files} ...",msgZoomModalHeading:"详细预览",msgFileRequired:"必须选择一个文件上传.",msgSizeTooSmall:'文件 "{name}" ({size} KB) 必须大于限定大小 {minSize} KB.',msgSizeTooLarge:'文件 "{name}" ({size} KB) 超过了允许大小 {maxSize} KB.',msgFilesTooLess:"你必须选择最少 {n} {files} 来上传. ",msgFilesTooMany:"选择的上传文件个数 ({n}) 超出最大文件的限制个数 {m}.",msgTotalFilesTooMany:"你最多可以上传 {m} 个文件 (当前有{n} 个文件).",msgFileNotFound:'文件 "{name}" 未找到!',msgFileSecured:'安全限制,为了防止读取文件 "{name}".',msgFileNotReadable:'文件 "{name}" 不可读.',msgFilePreviewAborted:'取消 "{name}" 的预览.',msgFilePreviewError:'读取 "{name}" 时出现了一个错误.',msgInvalidFileName:'文件名 "{name}" 包含非法字符.',msgInvalidFileType:'不正确的类型 "{name}". 只支持 "{types}" 类型的文件.',msgInvalidFileExtension:'不正确的文件扩展名 "{name}". 只支持 "{extensions}" 的文件扩展名.',msgFileTypes:{image:"image",html:"HTML",text:"text",video:"video",audio:"audio",flash:"flash",pdf:"PDF",object:"object"},msgUploadAborted:"该文件上传被中止",msgUploadThreshold:"处理中 …",msgUploadBegin:"正在初始化 …",msgUploadEnd:"完成",msgUploadResume:"继续上传 …",msgUploadEmpty:"无效的文件上传.",msgUploadError:"上传出错",msgDeleteError:"删除出错",msgProgressError:"上传出错",msgValidationError:"验证错误",msgLoading:"加载第 {index} 文件 共 {files} …",msgProgress:"加载第 {index} 文件 共 {files} - {name} - {percent}% 完成.",msgSelected:"{n} {files} 选中",msgProcessing:"处理中 ...",msgFoldersNotAllowed:"只支持拖拽文件! 跳过 {n} 拖拽的文件夹.",msgImageWidthSmall:'图像文件的"{name}"的宽度必须是至少{size}像素.',msgImageHeightSmall:'图像文件的"{name}"的高度必须至少为{size}像素.',msgImageWidthLarge:'图像文件"{name}"的宽度不能超过{size}像素.',msgImageHeightLarge:'图像文件"{name}"的高度不能超过{size}像素.',msgImageResizeError:"无法获取的图像尺寸调整。",msgImageResizeException:"调整图像大小时发生错误。
    {errors}
    ",msgAjaxError:"{operation} 发生错误. 请重试!",msgAjaxProgressError:"{operation} 失败",msgDuplicateFile:'文件 "{name}",大小 "{size} KB" 已经被选中.忽略相同的文件.',msgResumableUploadRetriesExceeded:"文件 {file} 上传失败超过 {max} 次重试 ! 错误详情:
    {error}
    ",msgPendingTime:"{time} 剩余",msgCalculatingTime:"计算剩余时间",ajaxOperations:{deleteThumb:"删除文件",uploadThumb:"上传文件",uploadBatch:"批量上传",uploadExtra:"表单数据上传"},dropZoneTitle:"拖拽文件到这里 …
    支持多文件同时上传",dropZoneClickTitle:"
    (或点击{files}按钮选择文件)",fileActionSettings:{removeTitle:"删除文件",uploadTitle:"上传文件",downloadTitle:"下载文件",uploadRetryTitle:"重试",zoomTitle:"查看详情",dragTitle:"移动 / 重置",indicatorNewTitle:"没有上传",indicatorSuccessTitle:"上传",indicatorErrorTitle:"上传错误",indicatorPausedTitle:"上传已暂停",indicatorLoadingTitle:"上传 …"},previewZoomButtonTitles:{prev:"预览上一个文件",next:"预览下一个文件",toggleheader:"缩放",fullscreen:"全屏",borderless:"无边界模式",close:"关闭当前预览"}},e.fn.fileinput.Constructor=i,e(document).ready(function(){var t=e("input.file[type=file]") +t.length&&t.fileinput()})}) diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading-sm.gif b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading-sm.gif new file mode 100644 index 000000000..44e3b7a0f Binary files /dev/null and b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading-sm.gif differ diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading.gif b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading.gif new file mode 100644 index 000000000..0ea146c02 Binary files /dev/null and b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-fileinput/loading.gif differ diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.css new file mode 100644 index 000000000..b07486c31 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.css @@ -0,0 +1,429 @@ +/*! + * Bootstrap-select v1.13.10 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +select.bs-select-hidden, +.bootstrap-select > select.bs-select-hidden, +select.selectpicker { + display: none !important; +} +.bootstrap-select { + width: 220px \0; + /*IE9 and below*/ + vertical-align: middle; +} +.bootstrap-select > .dropdown-toggle { + position: relative; + width: 100%; + text-align: right; + white-space: nowrap; + display: -webkit-inline-box; + display: -webkit-inline-flex; + display: -ms-inline-flexbox; + display: inline-flex; + -webkit-box-align: center; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + -webkit-box-pack: justify; + -webkit-justify-content: space-between; + -ms-flex-pack: justify; + justify-content: space-between; +} +.bootstrap-select > .dropdown-toggle:after { + margin-top: -1px; +} +.bootstrap-select > .dropdown-toggle.bs-placeholder, +.bootstrap-select > .dropdown-toggle.bs-placeholder:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder:active { + color: #999; +} +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active, +.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active { + color: rgba(255, 255, 255, 0.5); +} +.bootstrap-select > select { + position: absolute !important; + bottom: 0; + left: 50%; + display: block !important; + width: 0.5px !important; + height: 100% !important; + padding: 0 !important; + opacity: 0 !important; + border: none; + z-index: 0 !important; +} +.bootstrap-select > select.mobile-device { + top: 0; + left: 0; + display: block !important; + width: 100% !important; + z-index: 2 !important; +} +.has-error .bootstrap-select .dropdown-toggle, +.error .bootstrap-select .dropdown-toggle, +.bootstrap-select.is-invalid .dropdown-toggle, +.was-validated .bootstrap-select .selectpicker:invalid + .dropdown-toggle { + border-color: #b94a48; +} +.bootstrap-select.is-valid .dropdown-toggle, +.was-validated .bootstrap-select .selectpicker:valid + .dropdown-toggle { + border-color: #28a745; +} +.bootstrap-select.fit-width { + width: auto !important; +} +.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) { + width: 220px; +} +.bootstrap-select > select.mobile-device:focus + .dropdown-toggle, +.bootstrap-select .dropdown-toggle:focus { + outline: thin dotted #333333 !important; + outline: 5px auto -webkit-focus-ring-color !important; + outline-offset: -2px; +} +.bootstrap-select.form-control { + margin-bottom: 0; + padding: 0; + border: none; + height: auto; +} +:not(.input-group) > .bootstrap-select.form-control:not([class*="col-"]) { + width: 100%; +} +.bootstrap-select.form-control.input-group-btn { + float: none; + z-index: auto; +} +.form-inline .bootstrap-select, +.form-inline .bootstrap-select.form-control:not([class*="col-"]) { + width: auto; +} +.bootstrap-select:not(.input-group-btn), +.bootstrap-select[class*="col-"] { + float: none; + display: inline-block; + margin-left: 0; +} +.bootstrap-select.dropdown-menu-right, +.bootstrap-select[class*="col-"].dropdown-menu-right, +.row .bootstrap-select[class*="col-"].dropdown-menu-right { + float: right; +} +.form-inline .bootstrap-select, +.form-horizontal .bootstrap-select, +.form-group .bootstrap-select { + margin-bottom: 0; +} +.form-group-lg .bootstrap-select.form-control, +.form-group-sm .bootstrap-select.form-control { + padding: 0; +} +.form-group-lg .bootstrap-select.form-control .dropdown-toggle, +.form-group-sm .bootstrap-select.form-control .dropdown-toggle { + height: 100%; + font-size: inherit; + line-height: inherit; + border-radius: inherit; +} +.bootstrap-select.form-control-sm .dropdown-toggle, +.bootstrap-select.form-control-lg .dropdown-toggle { + font-size: inherit; + line-height: inherit; + border-radius: inherit; +} +.bootstrap-select.form-control-sm .dropdown-toggle { + padding: 0.25rem 0.5rem; +} +.bootstrap-select.form-control-lg .dropdown-toggle { + padding: 0.5rem 1rem; +} +.form-inline .bootstrap-select .form-control { + width: 100%; +} +.bootstrap-select.disabled, +.bootstrap-select > .disabled { + cursor: not-allowed; +} +.bootstrap-select.disabled:focus, +.bootstrap-select > .disabled:focus { + outline: none !important; +} +.bootstrap-select.bs-container { + position: absolute; + top: 0; + left: 0; + height: 0 !important; + padding: 0 !important; +} +.bootstrap-select.bs-container .dropdown-menu { + z-index: 1060; +} +.bootstrap-select .dropdown-toggle .filter-option { + position: static; + top: 0; + left: 0; + float: left; + height: 100%; + width: 100%; + text-align: left; + overflow: hidden; + -webkit-box-flex: 0; + -webkit-flex: 0 1 auto; + -ms-flex: 0 1 auto; + flex: 0 1 auto; +} +.bs3.bootstrap-select .dropdown-toggle .filter-option { + padding-right: inherit; +} +.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option { + position: absolute; + padding-top: inherit; + padding-bottom: inherit; + padding-left: inherit; + float: none; +} +.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner { + padding-right: inherit; +} +.bootstrap-select .dropdown-toggle .filter-option-inner-inner { + overflow: hidden; +} +.bootstrap-select .dropdown-toggle .filter-expand { + width: 0 !important; + float: left; + opacity: 0 !important; + overflow: hidden; +} +.bootstrap-select .dropdown-toggle .caret { + position: absolute; + top: 50%; + right: 12px; + margin-top: -2px; + vertical-align: middle; +} +.input-group .bootstrap-select.form-control .dropdown-toggle { + border-radius: inherit; +} +.bootstrap-select[class*="col-"] .dropdown-toggle { + width: 100%; +} +.bootstrap-select .dropdown-menu { + min-width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bootstrap-select .dropdown-menu > .inner:focus { + outline: none !important; +} +.bootstrap-select .dropdown-menu.inner { + position: static; + float: none; + border: 0; + padding: 0; + margin: 0; + border-radius: 0; + -webkit-box-shadow: none; + box-shadow: none; +} +.bootstrap-select .dropdown-menu li { + position: relative; +} +.bootstrap-select .dropdown-menu li.active small { + color: rgba(255, 255, 255, 0.5) !important; +} +.bootstrap-select .dropdown-menu li.disabled a { + cursor: not-allowed; +} +.bootstrap-select .dropdown-menu li a { + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.bootstrap-select .dropdown-menu li a.opt { + position: relative; + padding-left: 2.25em; +} +.bootstrap-select .dropdown-menu li a span.check-mark { + display: none; +} +.bootstrap-select .dropdown-menu li a span.text { + display: inline-block; +} +.bootstrap-select .dropdown-menu li small { + padding-left: 0.5em; +} +.bootstrap-select .dropdown-menu .notify { + position: absolute; + bottom: 5px; + width: 96%; + margin: 0 2%; + min-height: 26px; + padding: 3px 5px; + background: #f5f5f5; + border: 1px solid #e3e3e3; + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); + pointer-events: none; + opacity: 0.9; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bootstrap-select .no-results { + padding: 3px; + background: #f5f5f5; + margin: 0 5px; + white-space: nowrap; +} +.bootstrap-select.fit-width .dropdown-toggle .filter-option { + position: static; + display: inline; + padding: 0; +} +.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner, +.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner { + display: inline; +} +.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before { + content: '\00a0'; +} +.bootstrap-select.fit-width .dropdown-toggle .caret { + position: static; + top: auto; + margin-top: -1px; +} +.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark { + position: absolute; + display: inline-block; + right: 15px; + top: 5px; +} +.bootstrap-select.show-tick .dropdown-menu li a span.text { + margin-right: 34px; +} +.bootstrap-select .bs-ok-default:after { + content: ''; + display: block; + width: 0.5em; + height: 1em; + border-style: solid; + border-width: 0 0.26em 0.26em 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); +} +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle { + z-index: 1061; +} +.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before { + content: ''; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid rgba(204, 204, 204, 0.2); + position: absolute; + bottom: -4px; + left: 9px; + display: none; +} +.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after { + content: ''; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid white; + position: absolute; + bottom: -4px; + left: 10px; + display: none; +} +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before { + bottom: auto; + top: -4px; + border-top: 7px solid rgba(204, 204, 204, 0.2); + border-bottom: 0; +} +.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after { + bottom: auto; + top: -4px; + border-top: 6px solid white; + border-bottom: 0; +} +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before { + right: 12px; + left: auto; +} +.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after { + right: 13px; + left: auto; +} +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before, +.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after, +.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after { + display: block; +} +.bs-searchbox, +.bs-actionsbox, +.bs-donebutton { + padding: 4px 8px; +} +.bs-actionsbox { + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bs-actionsbox .btn-group button { + width: 50%; +} +.bs-donebutton { + float: left; + width: 100%; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} +.bs-donebutton .btn-group button { + width: 100%; +} +.bs-searchbox + .bs-actionsbox { + padding: 0 8px 4px; +} +.bs-searchbox .form-control { + margin-bottom: 0; + width: 100%; + float: none; +} +/*# sourceMappingURL=bootstrap-select.css.map */ \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.js new file mode 100644 index 000000000..dc2983334 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.js @@ -0,0 +1,3138 @@ +/*! + * Bootstrap-select v1.13.10 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +(function (root, factory) { + if (root === undefined && window !== undefined) root = window; + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module unless amdModuleId is set + define(["jquery"], function (a0) { + return (factory(a0)); + }); + } else if (typeof module === 'object' && module.exports) { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(require("jquery")); + } else { + factory(root["jQuery"]); + } +}(this, function (jQuery) { + +(function ($) { + 'use strict'; + + var DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']; + + var uriAttrs = [ + 'background', + 'cite', + 'href', + 'itemtype', + 'longdesc', + 'poster', + 'src', + 'xlink:href' + ]; + + var ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + + var DefaultWhitelist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', 'tabindex', 'style', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + div: [], + em: [], + hr: [], + h1: [], + h2: [], + h3: [], + h4: [], + h5: [], + h6: [], + i: [], + img: ['src', 'alt', 'title', 'width', 'height'], + li: [], + ol: [], + p: [], + pre: [], + s: [], + small: [], + span: [], + sub: [], + sup: [], + strong: [], + u: [], + ul: [] + } + + /** + * A pattern that recognizes a commonly useful subset of URLs that are safe. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + var SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi; + + /** + * A pattern that matches safe data URLs. Only matches image, video and audio types. + * + * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts + */ + var DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i; + + function allowedAttribute (attr, allowedAttributeList) { + var attrName = attr.nodeName.toLowerCase() + + if ($.inArray(attrName, allowedAttributeList) !== -1) { + if ($.inArray(attrName, uriAttrs) !== -1) { + return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)) + } + + return true + } + + var regExp = $(allowedAttributeList).filter(function (index, value) { + return value instanceof RegExp + }) + + // Check if a regular expression validates the attribute. + for (var i = 0, l = regExp.length; i < l; i++) { + if (attrName.match(regExp[i])) { + return true + } + } + + return false + } + + function sanitizeHtml (unsafeElements, whiteList, sanitizeFn) { + if (sanitizeFn && typeof sanitizeFn === 'function') { + return sanitizeFn(unsafeElements); + } + + var whitelistKeys = Object.keys(whiteList); + + for (var i = 0, len = unsafeElements.length; i < len; i++) { + var elements = unsafeElements[i].querySelectorAll('*'); + + for (var j = 0, len2 = elements.length; j < len2; j++) { + var el = elements[j]; + var elName = el.nodeName.toLowerCase(); + + if (whitelistKeys.indexOf(elName) === -1) { + el.parentNode.removeChild(el); + + continue; + } + + var attributeList = [].slice.call(el.attributes); + var whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []); + + for (var k = 0, len3 = attributeList.length; k < len3; k++) { + var attr = attributeList[k]; + + if (!allowedAttribute(attr, whitelistedAttributes)) { + el.removeAttribute(attr.nodeName); + } + } + } + } + } + + // Polyfill for browsers with no classList support + // Remove in v2 + if (!('classList' in document.createElement('_'))) { + (function (view) { + if (!('Element' in view)) return; + + var classListProp = 'classList', + protoProp = 'prototype', + elemCtrProto = view.Element[protoProp], + objCtr = Object, + classListGetter = function () { + var $elem = $(this); + + return { + add: function (classes) { + classes = Array.prototype.slice.call(arguments).join(' '); + return $elem.addClass(classes); + }, + remove: function (classes) { + classes = Array.prototype.slice.call(arguments).join(' '); + return $elem.removeClass(classes); + }, + toggle: function (classes, force) { + return $elem.toggleClass(classes, force); + }, + contains: function (classes) { + return $elem.hasClass(classes); + } + } + }; + + if (objCtr.defineProperty) { + var classListPropDesc = { + get: classListGetter, + enumerable: true, + configurable: true + }; + try { + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } catch (ex) { // IE 8 doesn't support enumerable:true + // adding undefined to fight this issue https://github.com/eligrey/classList.js/issues/36 + // modernie IE8-MSW7 machine has IE8 8.0.6001.18702 and is affected + if (ex.number === undefined || ex.number === -0x7FF5EC54) { + classListPropDesc.enumerable = false; + objCtr.defineProperty(elemCtrProto, classListProp, classListPropDesc); + } + } + } else if (objCtr[protoProp].__defineGetter__) { + elemCtrProto.__defineGetter__(classListProp, classListGetter); + } + }(window)); + } + + var testElement = document.createElement('_'); + + testElement.classList.add('c1', 'c2'); + + if (!testElement.classList.contains('c2')) { + var _add = DOMTokenList.prototype.add, + _remove = DOMTokenList.prototype.remove; + + DOMTokenList.prototype.add = function () { + Array.prototype.forEach.call(arguments, _add.bind(this)); + } + + DOMTokenList.prototype.remove = function () { + Array.prototype.forEach.call(arguments, _remove.bind(this)); + } + } + + testElement.classList.toggle('c3', false); + + // Polyfill for IE 10 and Firefox <24, where classList.toggle does not + // support the second argument. + if (testElement.classList.contains('c3')) { + var _toggle = DOMTokenList.prototype.toggle; + + DOMTokenList.prototype.toggle = function (token, force) { + if (1 in arguments && !this.contains(token) === !force) { + return force; + } else { + return _toggle.call(this, token); + } + }; + } + + testElement = null; + + // shallow array comparison + function isEqual (array1, array2) { + return array1.length === array2.length && array1.every(function (element, index) { + return element === array2[index]; + }); + }; + + // + if (!String.prototype.startsWith) { + (function () { + 'use strict'; // needed to support `apply`/`call` with `undefined`/`null` + var defineProperty = (function () { + // IE 8 only supports `Object.defineProperty` on DOM elements + try { + var object = {}; + var $defineProperty = Object.defineProperty; + var result = $defineProperty(object, object, object) && $defineProperty; + } catch (error) { + } + return result; + }()); + var toString = {}.toString; + var startsWith = function (search) { + if (this == null) { + throw new TypeError(); + } + var string = String(this); + if (search && toString.call(search) == '[object RegExp]') { + throw new TypeError(); + } + var stringLength = string.length; + var searchString = String(search); + var searchLength = searchString.length; + var position = arguments.length > 1 ? arguments[1] : undefined; + // `ToInteger` + var pos = position ? Number(position) : 0; + if (pos != pos) { // better `isNaN` + pos = 0; + } + var start = Math.min(Math.max(pos, 0), stringLength); + // Avoid the `indexOf` call if no match is possible + if (searchLength + start > stringLength) { + return false; + } + var index = -1; + while (++index < searchLength) { + if (string.charCodeAt(start + index) != searchString.charCodeAt(index)) { + return false; + } + } + return true; + }; + if (defineProperty) { + defineProperty(String.prototype, 'startsWith', { + 'value': startsWith, + 'configurable': true, + 'writable': true + }); + } else { + String.prototype.startsWith = startsWith; + } + }()); + } + + if (!Object.keys) { + Object.keys = function ( + o, // object + k, // key + r // result array + ) { + // initialize object and result + r = []; + // iterate over object keys + for (k in o) { + // fill result array with non-prototypical keys + r.hasOwnProperty.call(o, k) && r.push(k); + } + // return result + return r; + }; + } + + if (HTMLSelectElement && !HTMLSelectElement.prototype.hasOwnProperty('selectedOptions')) { + Object.defineProperty(HTMLSelectElement.prototype, 'selectedOptions', { + get: function () { + return this.querySelectorAll(':checked'); + } + }); + } + + function getSelectedOptions (select, ignoreDisabled) { + var selectedOptions = select.selectedOptions, + options = [], + opt; + + if (ignoreDisabled) { + for (var i = 0, len = selectedOptions.length; i < len; i++) { + opt = selectedOptions[i]; + + if (!(opt.disabled || opt.parentNode.tagName === 'OPTGROUP' && opt.parentNode.disabled)) { + options.push(opt); + } + } + + return options; + } + + return selectedOptions; + } + + // much faster than $.val() + function getSelectValues (select, selectedOptions) { + var value = [], + options = selectedOptions || select.selectedOptions, + opt; + + for (var i = 0, len = options.length; i < len; i++) { + opt = options[i]; + + if (!(opt.disabled || opt.parentNode.tagName === 'OPTGROUP' && opt.parentNode.disabled)) { + value.push(opt.value || opt.text); + } + } + + if (!select.multiple) { + return !value.length ? null : value[0]; + } + + return value; + } + + // set data-selected on select element if the value has been programmatically selected + // prior to initialization of bootstrap-select + // * consider removing or replacing an alternative method * + var valHooks = { + useDefault: false, + _set: $.valHooks.select.set + }; + + $.valHooks.select.set = function (elem, value) { + if (value && !valHooks.useDefault) $(elem).data('selected', true); + + return valHooks._set.apply(this, arguments); + }; + + var changedArguments = null; + + var EventIsSupported = (function () { + try { + new Event('change'); + return true; + } catch (e) { + return false; + } + })(); + + $.fn.triggerNative = function (eventName) { + var el = this[0], + event; + + if (el.dispatchEvent) { // for modern browsers & IE9+ + if (EventIsSupported) { + // For modern browsers + event = new Event(eventName, { + bubbles: true + }); + } else { + // For IE since it doesn't support Event constructor + event = document.createEvent('Event'); + event.initEvent(eventName, true, false); + } + + el.dispatchEvent(event); + } else if (el.fireEvent) { // for IE8 + event = document.createEventObject(); + event.eventType = eventName; + el.fireEvent('on' + eventName, event); + } else { + // fall back to jQuery.trigger + this.trigger(eventName); + } + }; + // + + function stringSearch (li, searchString, method, normalize) { + var stringTypes = [ + 'display', + 'subtext', + 'tokens' + ], + searchSuccess = false; + + for (var i = 0; i < stringTypes.length; i++) { + var stringType = stringTypes[i], + string = li[stringType]; + + if (string) { + string = string.toString(); + + // Strip HTML tags. This isn't perfect, but it's much faster than any other method + if (stringType === 'display') { + string = string.replace(/<[^>]+>/g, ''); + } + + if (normalize) string = normalizeToBase(string); + string = string.toUpperCase(); + + if (method === 'contains') { + searchSuccess = string.indexOf(searchString) >= 0; + } else { + searchSuccess = string.startsWith(searchString); + } + + if (searchSuccess) break; + } + } + + return searchSuccess; + } + + function toInteger (value) { + return parseInt(value, 10) || 0; + } + + // Borrowed from Lodash (_.deburr) + /** Used to map Latin Unicode letters to basic Latin letters. */ + var deburredLetters = { + // Latin-1 Supplement block. + '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', + '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', + '\xc7': 'C', '\xe7': 'c', + '\xd0': 'D', '\xf0': 'd', + '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', + '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', + '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', + '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', + '\xd1': 'N', '\xf1': 'n', + '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', + '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', + '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', + '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', + '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', + '\xc6': 'Ae', '\xe6': 'ae', + '\xde': 'Th', '\xfe': 'th', + '\xdf': 'ss', + // Latin Extended-A block. + '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', + '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', + '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', + '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', + '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', + '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', + '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', + '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', + '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', + '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', + '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', + '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', + '\u0134': 'J', '\u0135': 'j', + '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', + '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', + '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', + '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', + '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', + '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', + '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', + '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', + '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', + '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', + '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', + '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', + '\u0163': 't', '\u0165': 't', '\u0167': 't', + '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', + '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', + '\u0174': 'W', '\u0175': 'w', + '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', + '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', + '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', + '\u0132': 'IJ', '\u0133': 'ij', + '\u0152': 'Oe', '\u0153': 'oe', + '\u0149': "'n", '\u017f': 's' + }; + + /** Used to match Latin Unicode letters (excluding mathematical operators). */ + var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + + /** Used to compose unicode character classes. */ + var rsComboMarksRange = '\\u0300-\\u036f', + reComboHalfMarksRange = '\\ufe20-\\ufe2f', + rsComboSymbolsRange = '\\u20d0-\\u20ff', + rsComboMarksExtendedRange = '\\u1ab0-\\u1aff', + rsComboMarksSupplementRange = '\\u1dc0-\\u1dff', + rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange; + + /** Used to compose unicode capture groups. */ + var rsCombo = '[' + rsComboRange + ']'; + + /** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ + var reComboMark = RegExp(rsCombo, 'g'); + + function deburrLetter (key) { + return deburredLetters[key]; + }; + + function normalizeToBase (string) { + string = string.toString(); + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); + } + + // List of HTML entities for escaping. + var escapeMap = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', + '`': '`' + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + var createEscaper = function (map) { + var escaper = function (match) { + return map[match]; + }; + // Regexes for identifying a key that needs to be escaped. + var source = '(?:' + Object.keys(map).join('|') + ')'; + var testRegexp = RegExp(source); + var replaceRegexp = RegExp(source, 'g'); + return function (string) { + string = string == null ? '' : '' + string; + return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; + }; + }; + + var htmlEscape = createEscaper(escapeMap); + + /** + * ------------------------------------------------------------------------ + * Constants + * ------------------------------------------------------------------------ + */ + + var keyCodeMap = { + 32: ' ', + 48: '0', + 49: '1', + 50: '2', + 51: '3', + 52: '4', + 53: '5', + 54: '6', + 55: '7', + 56: '8', + 57: '9', + 59: ';', + 65: 'A', + 66: 'B', + 67: 'C', + 68: 'D', + 69: 'E', + 70: 'F', + 71: 'G', + 72: 'H', + 73: 'I', + 74: 'J', + 75: 'K', + 76: 'L', + 77: 'M', + 78: 'N', + 79: 'O', + 80: 'P', + 81: 'Q', + 82: 'R', + 83: 'S', + 84: 'T', + 85: 'U', + 86: 'V', + 87: 'W', + 88: 'X', + 89: 'Y', + 90: 'Z', + 96: '0', + 97: '1', + 98: '2', + 99: '3', + 100: '4', + 101: '5', + 102: '6', + 103: '7', + 104: '8', + 105: '9' + }; + + var keyCodes = { + ESCAPE: 27, // KeyboardEvent.which value for Escape (Esc) key + ENTER: 13, // KeyboardEvent.which value for Enter key + SPACE: 32, // KeyboardEvent.which value for space key + TAB: 9, // KeyboardEvent.which value for tab key + ARROW_UP: 38, // KeyboardEvent.which value for up arrow key + ARROW_DOWN: 40 // KeyboardEvent.which value for down arrow key + } + + var version = { + success: false, + major: '3' + }; + + try { + version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); + version.major = version.full[0]; + version.success = true; + } catch (err) { + // do nothing + } + + var selectId = 0; + + var EVENT_KEY = '.bs.select'; + + var classNames = { + DISABLED: 'disabled', + DIVIDER: 'divider', + SHOW: 'open', + DROPUP: 'dropup', + MENU: 'dropdown-menu', + MENURIGHT: 'dropdown-menu-right', + MENULEFT: 'dropdown-menu-left', + // to-do: replace with more advanced template/customization options + BUTTONCLASS: 'btn-default', + POPOVERHEADER: 'popover-title', + ICONBASE: 'glyphicon', + TICKICON: 'glyphicon-ok' + } + + var Selector = { + MENU: '.' + classNames.MENU + } + + var elementTemplates = { + span: document.createElement('span'), + i: document.createElement('i'), + subtext: document.createElement('small'), + a: document.createElement('a'), + li: document.createElement('li'), + whitespace: document.createTextNode('\u00A0'), + fragment: document.createDocumentFragment() + } + + elementTemplates.a.setAttribute('role', 'option'); + elementTemplates.subtext.className = 'text-muted'; + + elementTemplates.text = elementTemplates.span.cloneNode(false); + elementTemplates.text.className = 'text'; + + elementTemplates.checkMark = elementTemplates.span.cloneNode(false); + + var REGEXP_ARROW = new RegExp(keyCodes.ARROW_UP + '|' + keyCodes.ARROW_DOWN); + var REGEXP_TAB_OR_ESCAPE = new RegExp('^' + keyCodes.TAB + '$|' + keyCodes.ESCAPE); + + var generateOption = { + li: function (content, classes, optgroup) { + var li = elementTemplates.li.cloneNode(false); + + if (content) { + if (content.nodeType === 1 || content.nodeType === 11) { + li.appendChild(content); + } else { + li.innerHTML = content; + } + } + + if (typeof classes !== 'undefined' && classes !== '') li.className = classes; + if (typeof optgroup !== 'undefined' && optgroup !== null) li.classList.add('optgroup-' + optgroup); + + return li; + }, + + a: function (text, classes, inline) { + var a = elementTemplates.a.cloneNode(true); + + if (text) { + if (text.nodeType === 11) { + a.appendChild(text); + } else { + a.insertAdjacentHTML('beforeend', text); + } + } + + if (typeof classes !== 'undefined' && classes !== '') a.className = classes; + if (version.major === '4') a.classList.add('dropdown-item'); + if (inline) a.setAttribute('style', inline); + + return a; + }, + + text: function (options, useFragment) { + var textElement = elementTemplates.text.cloneNode(false), + subtextElement, + iconElement; + + if (options.content) { + textElement.innerHTML = options.content; + } else { + textElement.textContent = options.text; + + if (options.icon) { + var whitespace = elementTemplates.whitespace.cloneNode(false); + + // need to use for icons in the button to prevent a breaking change + // note: switch to span in next major release + iconElement = (useFragment === true ? elementTemplates.i : elementTemplates.span).cloneNode(false); + iconElement.className = options.iconBase + ' ' + options.icon; + + elementTemplates.fragment.appendChild(iconElement); + elementTemplates.fragment.appendChild(whitespace); + } + + if (options.subtext) { + subtextElement = elementTemplates.subtext.cloneNode(false); + subtextElement.textContent = options.subtext; + textElement.appendChild(subtextElement); + } + } + + if (useFragment === true) { + while (textElement.childNodes.length > 0) { + elementTemplates.fragment.appendChild(textElement.childNodes[0]); + } + } else { + elementTemplates.fragment.appendChild(textElement); + } + + return elementTemplates.fragment; + }, + + label: function (options) { + var textElement = elementTemplates.text.cloneNode(false), + subtextElement, + iconElement; + + textElement.innerHTML = options.label; + + if (options.icon) { + var whitespace = elementTemplates.whitespace.cloneNode(false); + + iconElement = elementTemplates.span.cloneNode(false); + iconElement.className = options.iconBase + ' ' + options.icon; + + elementTemplates.fragment.appendChild(iconElement); + elementTemplates.fragment.appendChild(whitespace); + } + + if (options.subtext) { + subtextElement = elementTemplates.subtext.cloneNode(false); + subtextElement.textContent = options.subtext; + textElement.appendChild(subtextElement); + } + + elementTemplates.fragment.appendChild(textElement); + + return elementTemplates.fragment; + } + } + + var Selectpicker = function (element, options) { + var that = this; + + // bootstrap-select has been initialized - revert valHooks.select.set back to its original function + if (!valHooks.useDefault) { + $.valHooks.select.set = valHooks._set; + valHooks.useDefault = true; + } + + this.$element = $(element); + this.$newElement = null; + this.$button = null; + this.$menu = null; + this.options = options; + this.selectpicker = { + main: {}, + search: {}, + current: {}, // current changes if a search is in progress + view: {}, + keydown: { + keyHistory: '', + resetKeyHistory: { + start: function () { + return setTimeout(function () { + that.selectpicker.keydown.keyHistory = ''; + }, 800); + } + } + } + }; + // If we have no title yet, try to pull it from the html title attribute (jQuery doesnt' pick it up as it's not a + // data-attribute) + if (this.options.title === null) { + this.options.title = this.$element.attr('title'); + } + + // Format window padding + var winPad = this.options.windowPadding; + if (typeof winPad === 'number') { + this.options.windowPadding = [winPad, winPad, winPad, winPad]; + } + + // Expose public methods + this.val = Selectpicker.prototype.val; + this.render = Selectpicker.prototype.render; + this.refresh = Selectpicker.prototype.refresh; + this.setStyle = Selectpicker.prototype.setStyle; + this.selectAll = Selectpicker.prototype.selectAll; + this.deselectAll = Selectpicker.prototype.deselectAll; + this.destroy = Selectpicker.prototype.destroy; + this.remove = Selectpicker.prototype.remove; + this.show = Selectpicker.prototype.show; + this.hide = Selectpicker.prototype.hide; + + this.init(); + }; + + Selectpicker.VERSION = '1.13.10'; + + // part of this is duplicated in i18n/defaults-en_US.js. Make sure to update both. + Selectpicker.DEFAULTS = { + noneSelectedText: 'Nothing selected', + noneResultsText: 'No results matched {0}', + countSelectedText: function (numSelected, numTotal) { + return (numSelected == 1) ? '{0} item selected' : '{0} items selected'; + }, + maxOptionsText: function (numAll, numGroup) { + return [ + (numAll == 1) ? 'Limit reached ({n} item max)' : 'Limit reached ({n} items max)', + (numGroup == 1) ? 'Group limit reached ({n} item max)' : 'Group limit reached ({n} items max)' + ]; + }, + selectAllText: 'Select All', + deselectAllText: 'Deselect All', + doneButton: false, + doneButtonText: 'Close', + multipleSeparator: ', ', + styleBase: 'btn', + style: classNames.BUTTONCLASS, + size: 'auto', + title: null, + selectedTextFormat: 'values', + width: false, + container: false, + hideDisabled: false, + showSubtext: false, + showIcon: true, + showContent: true, + dropupAuto: true, + header: false, + liveSearch: false, + liveSearchPlaceholder: null, + liveSearchNormalize: false, + liveSearchStyle: 'contains', + actionsBox: false, + iconBase: classNames.ICONBASE, + tickIcon: classNames.TICKICON, + showTick: false, + template: { + caret: '' + }, + maxOptions: false, + mobile: false, + selectOnTab: false, + dropdownAlignRight: false, + windowPadding: 0, + virtualScroll: 600, + display: false, + sanitize: true, + sanitizeFn: null, + whiteList: DefaultWhitelist + }; + + Selectpicker.prototype = { + + constructor: Selectpicker, + + init: function () { + var that = this, + id = this.$element.attr('id'); + + selectId++; + this.selectId = 'bs-select-' + selectId; + + this.$element[0].classList.add('bs-select-hidden'); + + this.multiple = this.$element.prop('multiple'); + this.autofocus = this.$element.prop('autofocus'); + + if (this.$element[0].classList.contains('show-tick')) { + this.options.showTick = true; + } + + this.$newElement = this.createDropdown(); + this.$element + .after(this.$newElement) + .prependTo(this.$newElement); + + this.$button = this.$newElement.children('button'); + this.$menu = this.$newElement.children(Selector.MENU); + this.$menuInner = this.$menu.children('.inner'); + this.$searchbox = this.$menu.find('input'); + + this.$element[0].classList.remove('bs-select-hidden'); + + if (this.options.dropdownAlignRight === true) this.$menu[0].classList.add(classNames.MENURIGHT); + + if (typeof id !== 'undefined') { + this.$button.attr('data-id', id); + } + + this.checkDisabled(); + this.clickListener(); + + if (this.options.liveSearch) { + this.liveSearchListener(); + this.focusedParent = this.$searchbox[0]; + } else { + this.focusedParent = this.$menuInner[0]; + } + + this.setStyle(); + this.render(); + this.setWidth(); + if (this.options.container) { + this.selectPosition(); + } else { + this.$element.on('hide' + EVENT_KEY, function () { + if (that.isVirtual()) { + // empty menu on close + var menuInner = that.$menuInner[0], + emptyMenu = menuInner.firstChild.cloneNode(false); + + // replace the existing UL with an empty one - this is faster than $.empty() or innerHTML = '' + menuInner.replaceChild(emptyMenu, menuInner.firstChild); + menuInner.scrollTop = 0; + } + }); + } + this.$menu.data('this', this); + this.$newElement.data('this', this); + if (this.options.mobile) this.mobile(); + + this.$newElement.on({ + 'hide.bs.dropdown': function (e) { + that.$element.trigger('hide' + EVENT_KEY, e); + }, + 'hidden.bs.dropdown': function (e) { + that.$element.trigger('hidden' + EVENT_KEY, e); + }, + 'show.bs.dropdown': function (e) { + that.$element.trigger('show' + EVENT_KEY, e); + }, + 'shown.bs.dropdown': function (e) { + that.$element.trigger('shown' + EVENT_KEY, e); + } + }); + + if (that.$element[0].hasAttribute('required')) { + this.$element.on('invalid' + EVENT_KEY, function () { + that.$button[0].classList.add('bs-invalid'); + + that.$element + .on('shown' + EVENT_KEY + '.invalid', function () { + that.$element + .val(that.$element.val()) // set the value to hide the validation message in Chrome when menu is opened + .off('shown' + EVENT_KEY + '.invalid'); + }) + .on('rendered' + EVENT_KEY, function () { + // if select is no longer invalid, remove the bs-invalid class + if (this.validity.valid) that.$button[0].classList.remove('bs-invalid'); + that.$element.off('rendered' + EVENT_KEY); + }); + + that.$button.on('blur' + EVENT_KEY, function () { + that.$element.trigger('focus').trigger('blur'); + that.$button.off('blur' + EVENT_KEY); + }); + }); + } + + setTimeout(function () { + that.createLi(); + that.$element.trigger('loaded' + EVENT_KEY); + }); + }, + + createDropdown: function () { + // Options + // If we are multiple or showTick option is set, then add the show-tick class + var showTick = (this.multiple || this.options.showTick) ? ' show-tick' : '', + multiselectable = this.multiple ? ' aria-multiselectable="true"' : '', + inputGroup = '', + autofocus = this.autofocus ? ' autofocus' : ''; + + if (version.major < 4 && this.$element.parent().hasClass('input-group')) { + inputGroup = ' input-group-btn'; + } + + // Elements + var drop, + header = '', + searchbox = '', + actionsbox = '', + donebutton = ''; + + if (this.options.header) { + header = + '
    ' + + '' + + this.options.header + + '
    '; + } + + if (this.options.liveSearch) { + searchbox = + ''; + } + + if (this.multiple && this.options.actionsBox) { + actionsbox = + '
    ' + + '
    ' + + '' + + '' + + '
    ' + + '
    '; + } + + if (this.multiple && this.options.doneButton) { + donebutton = + '
    ' + + '
    ' + + '' + + '
    ' + + '
    '; + } + + drop = + ''; + + return $(drop); + }, + + setPositionData: function () { + this.selectpicker.view.canHighlight = []; + this.selectpicker.view.size = 0; + + for (var i = 0; i < this.selectpicker.current.data.length; i++) { + var li = this.selectpicker.current.data[i], + canHighlight = true; + + if (li.type === 'divider') { + canHighlight = false; + li.height = this.sizeInfo.dividerHeight; + } else if (li.type === 'optgroup-label') { + canHighlight = false; + li.height = this.sizeInfo.dropdownHeaderHeight; + } else { + li.height = this.sizeInfo.liHeight; + } + + if (li.disabled) canHighlight = false; + + this.selectpicker.view.canHighlight.push(canHighlight); + + if (canHighlight) { + this.selectpicker.view.size++; + li.posinset = this.selectpicker.view.size; + } + + li.position = (i === 0 ? 0 : this.selectpicker.current.data[i - 1].position) + li.height; + } + }, + + isVirtual: function () { + return (this.options.virtualScroll !== false) && (this.selectpicker.main.elements.length >= this.options.virtualScroll) || this.options.virtualScroll === true; + }, + + createView: function (isSearching, setSize, refresh) { + var that = this, + scrollTop = 0, + active = [], + selected, + prevActive; + + this.selectpicker.current = isSearching ? this.selectpicker.search : this.selectpicker.main; + + this.setPositionData(); + + if (setSize) { + if (refresh) { + scrollTop = this.$menuInner[0].scrollTop; + } else if (!that.multiple) { + var element = that.$element[0], + selectedIndex = (element.options[element.selectedIndex] || {}).liIndex; + + if (typeof selectedIndex === 'number' && that.options.size !== false) { + var selectedData = that.selectpicker.main.data[selectedIndex], + position = selectedData && selectedData.position; + + if (position) { + scrollTop = position - ((that.sizeInfo.menuInnerHeight + that.sizeInfo.liHeight) / 2); + } + } + } + } + + scroll(scrollTop, true); + + this.$menuInner.off('scroll.createView').on('scroll.createView', function (e, updateValue) { + if (!that.noScroll) scroll(this.scrollTop, updateValue); + that.noScroll = false; + }); + + function scroll (scrollTop, init) { + var size = that.selectpicker.current.elements.length, + chunks = [], + chunkSize, + chunkCount, + firstChunk, + lastChunk, + currentChunk, + prevPositions, + positionIsDifferent, + previousElements, + menuIsDifferent = true, + isVirtual = that.isVirtual(); + + that.selectpicker.view.scrollTop = scrollTop; + + if (isVirtual === true) { + // if an option that is encountered that is wider than the current menu width, update the menu width accordingly + if (that.sizeInfo.hasScrollBar && that.$menu[0].offsetWidth > that.sizeInfo.totalMenuWidth) { + that.sizeInfo.menuWidth = that.$menu[0].offsetWidth; + that.sizeInfo.totalMenuWidth = that.sizeInfo.menuWidth + that.sizeInfo.scrollBarWidth; + that.$menu.css('min-width', that.sizeInfo.menuWidth); + } + } + + chunkSize = Math.ceil(that.sizeInfo.menuInnerHeight / that.sizeInfo.liHeight * 1.5); // number of options in a chunk + chunkCount = Math.round(size / chunkSize) || 1; // number of chunks + + for (var i = 0; i < chunkCount; i++) { + var endOfChunk = (i + 1) * chunkSize; + + if (i === chunkCount - 1) { + endOfChunk = size; + } + + chunks[i] = [ + (i) * chunkSize + (!i ? 0 : 1), + endOfChunk + ]; + + if (!size) break; + + if (currentChunk === undefined && scrollTop <= that.selectpicker.current.data[endOfChunk - 1].position - that.sizeInfo.menuInnerHeight) { + currentChunk = i; + } + } + + if (currentChunk === undefined) currentChunk = 0; + + prevPositions = [that.selectpicker.view.position0, that.selectpicker.view.position1]; + + // always display previous, current, and next chunks + firstChunk = Math.max(0, currentChunk - 1); + lastChunk = Math.min(chunkCount - 1, currentChunk + 1); + + that.selectpicker.view.position0 = isVirtual === false ? 0 : (Math.max(0, chunks[firstChunk][0]) || 0); + that.selectpicker.view.position1 = isVirtual === false ? size : (Math.min(size, chunks[lastChunk][1]) || 0); + + positionIsDifferent = prevPositions[0] !== that.selectpicker.view.position0 || prevPositions[1] !== that.selectpicker.view.position1; + + if (that.activeIndex !== undefined) { + prevActive = that.selectpicker.main.elements[that.prevActiveIndex]; + active = that.selectpicker.main.elements[that.activeIndex]; + selected = that.selectpicker.main.elements[that.selectedIndex]; + + if (init) { + if (that.activeIndex !== that.selectedIndex) { + that.defocusItem(active); + } + that.activeIndex = undefined; + } + + if (that.activeIndex && that.activeIndex !== that.selectedIndex) { + that.defocusItem(selected); + } + } + + if (that.prevActiveIndex !== undefined && that.prevActiveIndex !== that.activeIndex && that.prevActiveIndex !== that.selectedIndex) { + that.defocusItem(prevActive); + } + + if (init || positionIsDifferent) { + previousElements = that.selectpicker.view.visibleElements ? that.selectpicker.view.visibleElements.slice() : []; + + if (isVirtual === false) { + that.selectpicker.view.visibleElements = that.selectpicker.current.elements; + } else { + that.selectpicker.view.visibleElements = that.selectpicker.current.elements.slice(that.selectpicker.view.position0, that.selectpicker.view.position1); + } + + that.setOptionStatus(); + + // if searching, check to make sure the list has actually been updated before updating DOM + // this prevents unnecessary repaints + if (isSearching || (isVirtual === false && init)) menuIsDifferent = !isEqual(previousElements, that.selectpicker.view.visibleElements); + + // if virtual scroll is disabled and not searching, + // menu should never need to be updated more than once + if ((init || isVirtual === true) && menuIsDifferent) { + var menuInner = that.$menuInner[0], + menuFragment = document.createDocumentFragment(), + emptyMenu = menuInner.firstChild.cloneNode(false), + marginTop, + marginBottom, + elements = that.selectpicker.view.visibleElements, + toSanitize = []; + + // replace the existing UL with an empty one - this is faster than $.empty() + menuInner.replaceChild(emptyMenu, menuInner.firstChild); + + for (var i = 0, visibleElementsLen = elements.length; i < visibleElementsLen; i++) { + var element = elements[i], + elText, + elementData; + + if (that.options.sanitize) { + elText = element.lastChild; + + if (elText) { + elementData = that.selectpicker.current.data[i + that.selectpicker.view.position0]; + + if (elementData && elementData.content && !elementData.sanitized) { + toSanitize.push(elText); + elementData.sanitized = true; + } + } + } + + menuFragment.appendChild(element); + } + + if (that.options.sanitize && toSanitize.length) { + sanitizeHtml(toSanitize, that.options.whiteList, that.options.sanitizeFn); + } + + if (isVirtual === true) { + marginTop = (that.selectpicker.view.position0 === 0 ? 0 : that.selectpicker.current.data[that.selectpicker.view.position0 - 1].position); + marginBottom = (that.selectpicker.view.position1 > size - 1 ? 0 : that.selectpicker.current.data[size - 1].position - that.selectpicker.current.data[that.selectpicker.view.position1 - 1].position); + + menuInner.firstChild.style.marginTop = marginTop + 'px'; + menuInner.firstChild.style.marginBottom = marginBottom + 'px'; + } else { + menuInner.firstChild.style.marginTop = 0; + menuInner.firstChild.style.marginBottom = 0; + } + + menuInner.firstChild.appendChild(menuFragment); + } + } + + that.prevActiveIndex = that.activeIndex; + + if (!that.options.liveSearch) { + that.$menuInner.trigger('focus'); + } else if (isSearching && init) { + var index = 0, + newActive; + + if (!that.selectpicker.view.canHighlight[index]) { + index = 1 + that.selectpicker.view.canHighlight.slice(1).indexOf(true); + } + + newActive = that.selectpicker.view.visibleElements[index]; + + that.defocusItem(that.selectpicker.view.currentActive); + + that.activeIndex = (that.selectpicker.current.data[index] || {}).index; + + that.focusItem(newActive); + } + } + + $(window) + .off('resize' + EVENT_KEY + '.' + this.selectId + '.createView') + .on('resize' + EVENT_KEY + '.' + this.selectId + '.createView', function () { + var isActive = that.$newElement.hasClass(classNames.SHOW); + + if (isActive) scroll(that.$menuInner[0].scrollTop); + }); + }, + + focusItem: function (li, liData, noStyle) { + if (li) { + liData = liData || this.selectpicker.main.data[this.activeIndex]; + var a = li.firstChild; + + if (a) { + a.setAttribute('aria-setsize', this.selectpicker.view.size); + a.setAttribute('aria-posinset', liData.posinset); + + if (noStyle !== true) { + this.focusedParent.setAttribute('aria-activedescendant', a.id); + li.classList.add('active'); + a.classList.add('active'); + } + } + } + }, + + defocusItem: function (li) { + if (li) { + li.classList.remove('active'); + if (li.firstChild) li.firstChild.classList.remove('active'); + } + }, + + setPlaceholder: function () { + var updateIndex = false; + + if (this.options.title && !this.multiple) { + if (!this.selectpicker.view.titleOption) this.selectpicker.view.titleOption = document.createElement('option'); + + // this option doesn't create a new
  • element, but does add a new option at the start, + // so startIndex should increase to prevent having to check every option for the bs-title-option class + updateIndex = true; + + var element = this.$element[0], + isSelected = false, + titleNotAppended = !this.selectpicker.view.titleOption.parentNode; + + if (titleNotAppended) { + // Use native JS to prepend option (faster) + this.selectpicker.view.titleOption.className = 'bs-title-option'; + this.selectpicker.view.titleOption.value = ''; + + // Check if selected or data-selected attribute is already set on an option. If not, select the titleOption option. + // the selected item may have been changed by user or programmatically before the bootstrap select plugin runs, + // if so, the select will have the data-selected attribute + var $opt = $(element.options[element.selectedIndex]); + isSelected = $opt.attr('selected') === undefined && this.$element.data('selected') === undefined; + } + + if (titleNotAppended || this.selectpicker.view.titleOption.index !== 0) { + element.insertBefore(this.selectpicker.view.titleOption, element.firstChild); + } + + // Set selected *after* appending to select, + // otherwise the option doesn't get selected in IE + // set using selectedIndex, as setting the selected attr to true here doesn't work in IE11 + if (isSelected) element.selectedIndex = 0; + } + + return updateIndex; + }, + + createLi: function () { + var that = this, + iconBase = this.options.iconBase, + optionSelector = ':not([hidden]):not([data-hidden="true"])', + mainElements = [], + mainData = [], + widestOptionLength = 0, + optID = 0, + startIndex = this.setPlaceholder() ? 1 : 0; // append the titleOption if necessary and skip the first option in the loop + + if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; + + if ((that.options.showTick || that.multiple) && !elementTemplates.checkMark.parentNode) { + elementTemplates.checkMark.className = iconBase + ' ' + that.options.tickIcon + ' check-mark'; + elementTemplates.a.appendChild(elementTemplates.checkMark); + } + + var selectOptions = this.$element[0].querySelectorAll('select > *' + optionSelector); + + function addDivider (config) { + var previousData = mainData[mainData.length - 1]; + + // ensure optgroup doesn't create back-to-back dividers + if ( + previousData && + previousData.type === 'divider' && + (previousData.optID || config.optID) + ) { + return; + } + + config = config || {}; + config.type = 'divider'; + + mainElements.push( + generateOption.li( + false, + classNames.DIVIDER, + (config.optID ? config.optID + 'div' : undefined) + ) + ); + + mainData.push(config); + } + + function addOption (option, config) { + config = config || {}; + + config.divider = option.getAttribute('data-divider') === 'true'; + + if (config.divider) { + addDivider({ + optID: config.optID + }); + } else { + var liIndex = mainData.length, + cssText = option.style.cssText, + inlineStyle = cssText ? htmlEscape(cssText) : '', + optionClass = (option.className || '') + (config.optgroupClass || ''); + + if (config.optID) optionClass = 'opt ' + optionClass; + + config.text = option.textContent; + + config.content = option.getAttribute('data-content'); + config.tokens = option.getAttribute('data-tokens'); + config.subtext = option.getAttribute('data-subtext'); + config.icon = option.getAttribute('data-icon'); + config.iconBase = iconBase; + + var textElement = generateOption.text(config); + var liElement = generateOption.li( + generateOption.a( + textElement, + optionClass, + inlineStyle + ), + '', + config.optID + ); + + if (liElement.firstChild) { + liElement.firstChild.id = that.selectId + '-' + liIndex; + } + + mainElements.push(liElement); + + option.liIndex = liIndex; + + config.display = config.content || config.text; + config.type = 'option'; + config.index = liIndex; + config.option = option; + config.disabled = config.disabled || option.disabled; + + mainData.push(config); + + var combinedLength = 0; + + // count the number of characters in the option - not perfect, but should work in most cases + if (config.display) combinedLength += config.display.length; + if (config.subtext) combinedLength += config.subtext.length; + // if there is an icon, ensure this option's width is checked + if (config.icon) combinedLength += 1; + + if (combinedLength > widestOptionLength) { + widestOptionLength = combinedLength; + + // guess which option is the widest + // use this when calculating menu width + // not perfect, but it's fast, and the width will be updating accordingly when scrolling + that.selectpicker.view.widestOption = mainElements[mainElements.length - 1]; + } + } + } + + function addOptgroup (index, selectOptions) { + var optgroup = selectOptions[index], + previous = selectOptions[index - 1], + next = selectOptions[index + 1], + options = optgroup.querySelectorAll('option' + optionSelector); + + if (!options.length) return; + + var config = { + label: htmlEscape(optgroup.label), + subtext: optgroup.getAttribute('data-subtext'), + icon: optgroup.getAttribute('data-icon'), + iconBase: iconBase + }, + optgroupClass = ' ' + (optgroup.className || ''), + headerIndex, + lastIndex; + + optID++; + + if (previous) { + addDivider({ optID: optID }); + } + + var labelElement = generateOption.label(config); + + mainElements.push( + generateOption.li(labelElement, 'dropdown-header' + optgroupClass, optID) + ); + + mainData.push({ + display: config.label, + subtext: config.subtext, + type: 'optgroup-label', + optID: optID + }); + + for (var j = 0, len = options.length; j < len; j++) { + var option = options[j]; + + if (j === 0) { + headerIndex = mainData.length - 1; + lastIndex = headerIndex + len; + } + + addOption(option, { + headerIndex: headerIndex, + lastIndex: lastIndex, + optID: optID, + optgroupClass: optgroupClass, + disabled: optgroup.disabled + }); + } + + if (next) { + addDivider({ optID: optID }); + } + } + + for (var len = selectOptions.length; startIndex < len; startIndex++) { + var item = selectOptions[startIndex]; + + if (item.tagName !== 'OPTGROUP') { + addOption(item, {}); + } else { + addOptgroup(startIndex, selectOptions); + } + } + + this.selectpicker.main.elements = mainElements; + this.selectpicker.main.data = mainData; + + this.selectpicker.current = this.selectpicker.main; + }, + + findLis: function () { + return this.$menuInner.find('.inner > li'); + }, + + render: function () { + // ensure titleOption is appended and selected (if necessary) before getting selectedOptions + this.setPlaceholder(); + + var that = this, + element = this.$element[0], + selectedOptions = getSelectedOptions(element, this.options.hideDisabled), + selectedCount = selectedOptions.length, + button = this.$button[0], + buttonInner = button.querySelector('.filter-option-inner-inner'), + multipleSeparator = document.createTextNode(this.options.multipleSeparator), + titleFragment = elementTemplates.fragment.cloneNode(false), + showCount, + countMax, + hasContent = false; + + button.classList.toggle('bs-placeholder', that.multiple ? !selectedCount : !getSelectValues(element, selectedOptions)); + + this.tabIndex(); + + if (this.options.selectedTextFormat === 'static') { + titleFragment = generateOption.text({ text: this.options.title }, true); + } else { + showCount = this.multiple && this.options.selectedTextFormat.indexOf('count') !== -1 && selectedCount > 1; + + // determine if the number of selected options will be shown (showCount === true) + if (showCount) { + countMax = this.options.selectedTextFormat.split('>'); + showCount = (countMax.length > 1 && selectedCount > countMax[1]) || (countMax.length === 1 && selectedCount >= 2); + } + + // only loop through all selected options if the count won't be shown + if (showCount === false) { + for (var selectedIndex = 0; selectedIndex < selectedCount; selectedIndex++) { + if (selectedIndex < 50) { + var option = selectedOptions[selectedIndex], + titleOptions = {}, + thisData = { + content: option.getAttribute('data-content'), + subtext: option.getAttribute('data-subtext'), + icon: option.getAttribute('data-icon') + }; + + if (this.multiple && selectedIndex > 0) { + titleFragment.appendChild(multipleSeparator.cloneNode(false)); + } + + if (option.title) { + titleOptions.text = option.title; + } else if (thisData.content && that.options.showContent) { + titleOptions.content = thisData.content.toString(); + hasContent = true; + } else { + if (that.options.showIcon) { + titleOptions.icon = thisData.icon; + titleOptions.iconBase = this.options.iconBase; + } + if (that.options.showSubtext && !that.multiple && thisData.subtext) titleOptions.subtext = ' ' + thisData.subtext; + titleOptions.text = option.textContent.trim(); + } + + titleFragment.appendChild(generateOption.text(titleOptions, true)); + } else { + break; + } + } + + // add ellipsis + if (selectedCount > 49) { + titleFragment.appendChild(document.createTextNode('...')); + } + } else { + var optionSelector = ':not([hidden]):not([data-hidden="true"]):not([data-divider="true"])'; + if (this.options.hideDisabled) optionSelector += ':not(:disabled)'; + + // If this is a multiselect, and selectedTextFormat is count, then show 1 of 2 selected, etc. + var totalCount = this.$element[0].querySelectorAll('select > option' + optionSelector + ', optgroup' + optionSelector + ' option' + optionSelector).length, + tr8nText = (typeof this.options.countSelectedText === 'function') ? this.options.countSelectedText(selectedCount, totalCount) : this.options.countSelectedText; + + titleFragment = generateOption.text({ + text: tr8nText.replace('{0}', selectedCount.toString()).replace('{1}', totalCount.toString()) + }, true); + } + } + + if (this.options.title == undefined) { + // use .attr to ensure undefined is returned if title attribute is not set + this.options.title = this.$element.attr('title'); + } + + // If the select doesn't have a title, then use the default, or if nothing is set at all, use noneSelectedText + if (!titleFragment.childNodes.length) { + titleFragment = generateOption.text({ + text: typeof this.options.title !== 'undefined' ? this.options.title : this.options.noneSelectedText + }, true); + } + + // strip all HTML tags and trim the result, then unescape any escaped tags + button.title = titleFragment.textContent.replace(/<[^>]*>?/g, '').trim(); + + if (this.options.sanitize && hasContent) { + sanitizeHtml([titleFragment], that.options.whiteList, that.options.sanitizeFn); + } + + buttonInner.innerHTML = ''; + buttonInner.appendChild(titleFragment); + + if (version.major < 4 && this.$newElement[0].classList.contains('bs3-has-addon')) { + var filterExpand = button.querySelector('.filter-expand'), + clone = buttonInner.cloneNode(true); + + clone.className = 'filter-expand'; + + if (filterExpand) { + button.replaceChild(clone, filterExpand); + } else { + button.appendChild(clone); + } + } + + this.$element.trigger('rendered' + EVENT_KEY); + }, + + /** + * @param [style] + * @param [status] + */ + setStyle: function (newStyle, status) { + var button = this.$button[0], + newElement = this.$newElement[0], + style = this.options.style.trim(), + buttonClass; + + if (this.$element.attr('class')) { + this.$newElement.addClass(this.$element.attr('class').replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi, '')); + } + + if (version.major < 4) { + newElement.classList.add('bs3'); + + if (newElement.parentNode.classList.contains('input-group') && + (newElement.previousElementSibling || newElement.nextElementSibling) && + (newElement.previousElementSibling || newElement.nextElementSibling).classList.contains('input-group-addon') + ) { + newElement.classList.add('bs3-has-addon'); + } + } + + if (newStyle) { + buttonClass = newStyle.trim(); + } else { + buttonClass = style; + } + + if (status == 'add') { + if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); + } else if (status == 'remove') { + if (buttonClass) button.classList.remove.apply(button.classList, buttonClass.split(' ')); + } else { + if (style) button.classList.remove.apply(button.classList, style.split(' ')); + if (buttonClass) button.classList.add.apply(button.classList, buttonClass.split(' ')); + } + }, + + liHeight: function (refresh) { + if (!refresh && (this.options.size === false || this.sizeInfo)) return; + + if (!this.sizeInfo) this.sizeInfo = {}; + + var newElement = document.createElement('div'), + menu = document.createElement('div'), + menuInner = document.createElement('div'), + menuInnerInner = document.createElement('ul'), + divider = document.createElement('li'), + dropdownHeader = document.createElement('li'), + li = document.createElement('li'), + a = document.createElement('a'), + text = document.createElement('span'), + header = this.options.header && this.$menu.find('.' + classNames.POPOVERHEADER).length > 0 ? this.$menu.find('.' + classNames.POPOVERHEADER)[0].cloneNode(true) : null, + search = this.options.liveSearch ? document.createElement('div') : null, + actions = this.options.actionsBox && this.multiple && this.$menu.find('.bs-actionsbox').length > 0 ? this.$menu.find('.bs-actionsbox')[0].cloneNode(true) : null, + doneButton = this.options.doneButton && this.multiple && this.$menu.find('.bs-donebutton').length > 0 ? this.$menu.find('.bs-donebutton')[0].cloneNode(true) : null, + firstOption = this.$element.find('option')[0]; + + this.sizeInfo.selectWidth = this.$newElement[0].offsetWidth; + + text.className = 'text'; + a.className = 'dropdown-item ' + (firstOption ? firstOption.className : ''); + newElement.className = this.$menu[0].parentNode.className + ' ' + classNames.SHOW; + newElement.style.width = this.sizeInfo.selectWidth + 'px'; + if (this.options.width === 'auto') menu.style.minWidth = 0; + menu.className = classNames.MENU + ' ' + classNames.SHOW; + menuInner.className = 'inner ' + classNames.SHOW; + menuInnerInner.className = classNames.MENU + ' inner ' + (version.major === '4' ? classNames.SHOW : ''); + divider.className = classNames.DIVIDER; + dropdownHeader.className = 'dropdown-header'; + + text.appendChild(document.createTextNode('\u200b')); + a.appendChild(text); + li.appendChild(a); + dropdownHeader.appendChild(text.cloneNode(true)); + + if (this.selectpicker.view.widestOption) { + menuInnerInner.appendChild(this.selectpicker.view.widestOption.cloneNode(true)); + } + + menuInnerInner.appendChild(li); + menuInnerInner.appendChild(divider); + menuInnerInner.appendChild(dropdownHeader); + if (header) menu.appendChild(header); + if (search) { + var input = document.createElement('input'); + search.className = 'bs-searchbox'; + input.className = 'form-control'; + search.appendChild(input); + menu.appendChild(search); + } + if (actions) menu.appendChild(actions); + menuInner.appendChild(menuInnerInner); + menu.appendChild(menuInner); + if (doneButton) menu.appendChild(doneButton); + newElement.appendChild(menu); + + document.body.appendChild(newElement); + + var liHeight = li.offsetHeight, + dropdownHeaderHeight = dropdownHeader ? dropdownHeader.offsetHeight : 0, + headerHeight = header ? header.offsetHeight : 0, + searchHeight = search ? search.offsetHeight : 0, + actionsHeight = actions ? actions.offsetHeight : 0, + doneButtonHeight = doneButton ? doneButton.offsetHeight : 0, + dividerHeight = $(divider).outerHeight(true), + // fall back to jQuery if getComputedStyle is not supported + menuStyle = window.getComputedStyle ? window.getComputedStyle(menu) : false, + menuWidth = menu.offsetWidth, + $menu = menuStyle ? null : $(menu), + menuPadding = { + vert: toInteger(menuStyle ? menuStyle.paddingTop : $menu.css('paddingTop')) + + toInteger(menuStyle ? menuStyle.paddingBottom : $menu.css('paddingBottom')) + + toInteger(menuStyle ? menuStyle.borderTopWidth : $menu.css('borderTopWidth')) + + toInteger(menuStyle ? menuStyle.borderBottomWidth : $menu.css('borderBottomWidth')), + horiz: toInteger(menuStyle ? menuStyle.paddingLeft : $menu.css('paddingLeft')) + + toInteger(menuStyle ? menuStyle.paddingRight : $menu.css('paddingRight')) + + toInteger(menuStyle ? menuStyle.borderLeftWidth : $menu.css('borderLeftWidth')) + + toInteger(menuStyle ? menuStyle.borderRightWidth : $menu.css('borderRightWidth')) + }, + menuExtras = { + vert: menuPadding.vert + + toInteger(menuStyle ? menuStyle.marginTop : $menu.css('marginTop')) + + toInteger(menuStyle ? menuStyle.marginBottom : $menu.css('marginBottom')) + 2, + horiz: menuPadding.horiz + + toInteger(menuStyle ? menuStyle.marginLeft : $menu.css('marginLeft')) + + toInteger(menuStyle ? menuStyle.marginRight : $menu.css('marginRight')) + 2 + }, + scrollBarWidth; + + menuInner.style.overflowY = 'scroll'; + + scrollBarWidth = menu.offsetWidth - menuWidth; + + document.body.removeChild(newElement); + + this.sizeInfo.liHeight = liHeight; + this.sizeInfo.dropdownHeaderHeight = dropdownHeaderHeight; + this.sizeInfo.headerHeight = headerHeight; + this.sizeInfo.searchHeight = searchHeight; + this.sizeInfo.actionsHeight = actionsHeight; + this.sizeInfo.doneButtonHeight = doneButtonHeight; + this.sizeInfo.dividerHeight = dividerHeight; + this.sizeInfo.menuPadding = menuPadding; + this.sizeInfo.menuExtras = menuExtras; + this.sizeInfo.menuWidth = menuWidth; + this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth; + this.sizeInfo.scrollBarWidth = scrollBarWidth; + this.sizeInfo.selectHeight = this.$newElement[0].offsetHeight; + + this.setPositionData(); + }, + + getSelectPosition: function () { + var that = this, + $window = $(window), + pos = that.$newElement.offset(), + $container = $(that.options.container), + containerPos; + + if (that.options.container && $container.length && !$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')); + containerPos.left += parseInt($container.css('borderLeftWidth')); + } else { + containerPos = { top: 0, left: 0 }; + } + + var winPad = that.options.windowPadding; + + this.sizeInfo.selectOffsetTop = pos.top - containerPos.top - $window.scrollTop(); + this.sizeInfo.selectOffsetBot = $window.height() - this.sizeInfo.selectOffsetTop - this.sizeInfo.selectHeight - containerPos.top - winPad[2]; + this.sizeInfo.selectOffsetLeft = pos.left - containerPos.left - $window.scrollLeft(); + this.sizeInfo.selectOffsetRight = $window.width() - this.sizeInfo.selectOffsetLeft - this.sizeInfo.selectWidth - containerPos.left - winPad[1]; + this.sizeInfo.selectOffsetTop -= winPad[0]; + this.sizeInfo.selectOffsetLeft -= winPad[3]; + }, + + setMenuSize: function (isAuto) { + this.getSelectPosition(); + + var selectWidth = this.sizeInfo.selectWidth, + liHeight = this.sizeInfo.liHeight, + headerHeight = this.sizeInfo.headerHeight, + searchHeight = this.sizeInfo.searchHeight, + actionsHeight = this.sizeInfo.actionsHeight, + doneButtonHeight = this.sizeInfo.doneButtonHeight, + divHeight = this.sizeInfo.dividerHeight, + menuPadding = this.sizeInfo.menuPadding, + menuInnerHeight, + menuHeight, + divLength = 0, + minHeight, + _minHeight, + maxHeight, + menuInnerMinHeight, + estimate; + + if (this.options.dropupAuto) { + // Get the estimated height of the menu without scrollbars. + // This is useful for smaller menus, where there might be plenty of room + // below the button without setting dropup, but we can't know + // the exact height of the menu until createView is called later + estimate = liHeight * this.selectpicker.current.elements.length + menuPadding.vert; + this.$newElement.toggleClass(classNames.DROPUP, this.sizeInfo.selectOffsetTop - this.sizeInfo.selectOffsetBot > this.sizeInfo.menuExtras.vert && estimate + this.sizeInfo.menuExtras.vert + 50 > this.sizeInfo.selectOffsetBot); + } + + if (this.options.size === 'auto') { + _minHeight = this.selectpicker.current.elements.length > 3 ? this.sizeInfo.liHeight * 3 + this.sizeInfo.menuExtras.vert - 2 : 0; + menuHeight = this.sizeInfo.selectOffsetBot - this.sizeInfo.menuExtras.vert; + minHeight = _minHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; + menuInnerMinHeight = Math.max(_minHeight - menuPadding.vert, 0); + + if (this.$newElement.hasClass(classNames.DROPUP)) { + menuHeight = this.sizeInfo.selectOffsetTop - this.sizeInfo.menuExtras.vert; + } + + maxHeight = menuHeight; + menuInnerHeight = menuHeight - headerHeight - searchHeight - actionsHeight - doneButtonHeight - menuPadding.vert; + } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { + for (var i = 0; i < this.options.size; i++) { + if (this.selectpicker.current.data[i].type === 'divider') divLength++; + } + + menuHeight = liHeight * this.options.size + divLength * divHeight + menuPadding.vert; + menuInnerHeight = menuHeight - menuPadding.vert; + maxHeight = menuHeight + headerHeight + searchHeight + actionsHeight + doneButtonHeight; + minHeight = menuInnerMinHeight = ''; + } + + if (this.options.dropdownAlignRight === 'auto') { + this.$menu.toggleClass(classNames.MENURIGHT, this.sizeInfo.selectOffsetLeft > this.sizeInfo.selectOffsetRight && this.sizeInfo.selectOffsetRight < (this.sizeInfo.totalMenuWidth - selectWidth)); + } + + this.$menu.css({ + 'max-height': maxHeight + 'px', + 'overflow': 'hidden', + 'min-height': minHeight + 'px' + }); + + this.$menuInner.css({ + 'max-height': menuInnerHeight + 'px', + 'overflow-y': 'auto', + 'min-height': menuInnerMinHeight + 'px' + }); + + // ensure menuInnerHeight is always a positive number to prevent issues calculating chunkSize in createView + this.sizeInfo.menuInnerHeight = Math.max(menuInnerHeight, 1); + + if (this.selectpicker.current.data.length && this.selectpicker.current.data[this.selectpicker.current.data.length - 1].position > this.sizeInfo.menuInnerHeight) { + this.sizeInfo.hasScrollBar = true; + this.sizeInfo.totalMenuWidth = this.sizeInfo.menuWidth + this.sizeInfo.scrollBarWidth; + + this.$menu.css('min-width', this.sizeInfo.totalMenuWidth); + } + + if (this.dropdown && this.dropdown._popper) this.dropdown._popper.update(); + }, + + setSize: function (refresh) { + this.liHeight(refresh); + + if (this.options.header) this.$menu.css('padding-top', 0); + if (this.options.size === false) return; + + var that = this, + $window = $(window); + + this.setMenuSize(); + + if (this.options.liveSearch) { + this.$searchbox + .off('input.setMenuSize propertychange.setMenuSize') + .on('input.setMenuSize propertychange.setMenuSize', function () { + return that.setMenuSize(); + }); + } + + if (this.options.size === 'auto') { + $window + .off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize') + .on('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize', function () { + return that.setMenuSize(); + }); + } else if (this.options.size && this.options.size != 'auto' && this.selectpicker.current.elements.length > this.options.size) { + $window.off('resize' + EVENT_KEY + '.' + this.selectId + '.setMenuSize' + ' scroll' + EVENT_KEY + '.' + this.selectId + '.setMenuSize'); + } + + that.createView(false, true, refresh); + }, + + setWidth: function () { + var that = this; + + if (this.options.width === 'auto') { + requestAnimationFrame(function () { + that.$menu.css('min-width', '0'); + + that.$element.on('loaded' + EVENT_KEY, function () { + that.liHeight(); + that.setMenuSize(); + + // Get correct width if element is hidden + var $selectClone = that.$newElement.clone().appendTo('body'), + btnWidth = $selectClone.css('width', 'auto').children('button').outerWidth(); + + $selectClone.remove(); + + // Set width to whatever's larger, button title or longest option + that.sizeInfo.selectWidth = Math.max(that.sizeInfo.totalMenuWidth, btnWidth); + that.$newElement.css('width', that.sizeInfo.selectWidth + 'px'); + }); + }); + } else if (this.options.width === 'fit') { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', '').addClass('fit-width'); + } else if (this.options.width) { + // Remove inline min-width so width can be changed from 'auto' + this.$menu.css('min-width', ''); + this.$newElement.css('width', this.options.width); + } else { + // Remove inline min-width/width so width can be changed + this.$menu.css('min-width', ''); + this.$newElement.css('width', ''); + } + // Remove fit-width class if width is changed programmatically + if (this.$newElement.hasClass('fit-width') && this.options.width !== 'fit') { + this.$newElement[0].classList.remove('fit-width'); + } + }, + + selectPosition: function () { + this.$bsContainer = $('
    '); + + var that = this, + $container = $(this.options.container), + pos, + containerPos, + actualHeight, + getPlacement = function ($element) { + var containerPosition = {}, + // fall back to dropdown's default display setting if display is not manually set + display = that.options.display || ( + // Bootstrap 3 doesn't have $.fn.dropdown.Constructor.Default + $.fn.dropdown.Constructor.Default ? $.fn.dropdown.Constructor.Default.display + : false + ); + + that.$bsContainer.addClass($element.attr('class').replace(/form-control|fit-width/gi, '')).toggleClass(classNames.DROPUP, $element.hasClass(classNames.DROPUP)); + pos = $element.offset(); + + if (!$container.is('body')) { + containerPos = $container.offset(); + containerPos.top += parseInt($container.css('borderTopWidth')) - $container.scrollTop(); + containerPos.left += parseInt($container.css('borderLeftWidth')) - $container.scrollLeft(); + } else { + containerPos = { top: 0, left: 0 }; + } + + actualHeight = $element.hasClass(classNames.DROPUP) ? 0 : $element[0].offsetHeight; + + // Bootstrap 4+ uses Popper for menu positioning + if (version.major < 4 || display === 'static') { + containerPosition.top = pos.top - containerPos.top + actualHeight; + containerPosition.left = pos.left - containerPos.left; + } + + containerPosition.width = $element[0].offsetWidth; + + that.$bsContainer.css(containerPosition); + }; + + this.$button.on('click.bs.dropdown.data-api', function () { + if (that.isDisabled()) { + return; + } + + getPlacement(that.$newElement); + + that.$bsContainer + .appendTo(that.options.container) + .toggleClass(classNames.SHOW, !that.$button.hasClass(classNames.SHOW)) + .append(that.$menu); + }); + + $(window) + .off('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId) + .on('resize' + EVENT_KEY + '.' + this.selectId + ' scroll' + EVENT_KEY + '.' + this.selectId, function () { + var isActive = that.$newElement.hasClass(classNames.SHOW); + + if (isActive) getPlacement(that.$newElement); + }); + + this.$element.on('hide' + EVENT_KEY, function () { + that.$menu.data('height', that.$menu.height()); + that.$bsContainer.detach(); + }); + }, + + setOptionStatus: function (selectedOnly) { + var that = this; + + that.noScroll = false; + + if (that.selectpicker.view.visibleElements && that.selectpicker.view.visibleElements.length) { + for (var i = 0; i < that.selectpicker.view.visibleElements.length; i++) { + var liData = that.selectpicker.current.data[i + that.selectpicker.view.position0], + option = liData.option; + + if (option) { + if (selectedOnly !== true) { + that.setDisabled( + liData.index, + liData.disabled + ); + } + + that.setSelected( + liData.index, + option.selected + ); + } + } + } + }, + + /** + * @param {number} index - the index of the option that is being changed + * @param {boolean} selected - true if the option is being selected, false if being deselected + */ + setSelected: function (index, selected) { + var li = this.selectpicker.main.elements[index], + liData = this.selectpicker.main.data[index], + activeIndexIsSet = this.activeIndex !== undefined, + thisIsActive = this.activeIndex === index, + prevActive, + a, + // if current option is already active + // OR + // if the current option is being selected, it's NOT multiple, and + // activeIndex is undefined: + // - when the menu is first being opened, OR + // - after a search has been performed, OR + // - when retainActive is false when selecting a new option (i.e. index of the newly selected option is not the same as the current activeIndex) + keepActive = thisIsActive || (selected && !this.multiple && !activeIndexIsSet); + + liData.selected = selected; + + a = li.firstChild; + + if (selected) { + this.selectedIndex = index; + } + + li.classList.toggle('selected', selected); + + if (keepActive) { + this.focusItem(li, liData); + this.selectpicker.view.currentActive = li; + this.activeIndex = index; + } else { + this.defocusItem(li); + } + + if (a) { + a.classList.toggle('selected', selected); + + if (selected) { + a.setAttribute('aria-selected', true); + } else { + if (this.multiple) { + a.setAttribute('aria-selected', false); + } else { + a.removeAttribute('aria-selected'); + } + } + } + + if (!keepActive && !activeIndexIsSet && selected && this.prevActiveIndex !== undefined) { + prevActive = this.selectpicker.main.elements[this.prevActiveIndex]; + + this.defocusItem(prevActive); + } + }, + + /** + * @param {number} index - the index of the option that is being disabled + * @param {boolean} disabled - true if the option is being disabled, false if being enabled + */ + setDisabled: function (index, disabled) { + var li = this.selectpicker.main.elements[index], + a; + + this.selectpicker.main.data[index].disabled = disabled; + + a = li.firstChild; + + li.classList.toggle(classNames.DISABLED, disabled); + + if (a) { + if (version.major === '4') a.classList.toggle(classNames.DISABLED, disabled); + + if (disabled) { + a.setAttribute('aria-disabled', disabled); + a.setAttribute('tabindex', -1); + } else { + a.removeAttribute('aria-disabled'); + a.setAttribute('tabindex', 0); + } + } + }, + + isDisabled: function () { + return this.$element[0].disabled; + }, + + checkDisabled: function () { + var that = this; + + if (this.isDisabled()) { + this.$newElement[0].classList.add(classNames.DISABLED); + this.$button.addClass(classNames.DISABLED).attr('tabindex', -1).attr('aria-disabled', true); + } else { + if (this.$button[0].classList.contains(classNames.DISABLED)) { + this.$newElement[0].classList.remove(classNames.DISABLED); + this.$button.removeClass(classNames.DISABLED).attr('aria-disabled', false); + } + + if (this.$button.attr('tabindex') == -1 && !this.$element.data('tabindex')) { + this.$button.removeAttr('tabindex'); + } + } + + this.$button.on('click', function () { + return !that.isDisabled(); + }); + }, + + tabIndex: function () { + if (this.$element.data('tabindex') !== this.$element.attr('tabindex') && + (this.$element.attr('tabindex') !== -98 && this.$element.attr('tabindex') !== '-98')) { + this.$element.data('tabindex', this.$element.attr('tabindex')); + this.$button.attr('tabindex', this.$element.data('tabindex')); + } + + this.$element.attr('tabindex', -98); + }, + + clickListener: function () { + var that = this, + $document = $(document); + + $document.data('spaceSelect', false); + + this.$button.on('keyup', function (e) { + if (/(32)/.test(e.keyCode.toString(10)) && $document.data('spaceSelect')) { + e.preventDefault(); + $document.data('spaceSelect', false); + } + }); + + this.$newElement.on('show.bs.dropdown', function () { + if (version.major > 3 && !that.dropdown) { + that.dropdown = that.$button.data('bs.dropdown'); + that.dropdown._menu = that.$menu[0]; + } + }); + + this.$button.on('click.bs.dropdown.data-api', function () { + if (!that.$newElement.hasClass(classNames.SHOW)) { + that.setSize(); + } + }); + + function setFocus () { + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$menuInner.trigger('focus'); + } + } + + function checkPopperExists () { + if (that.dropdown && that.dropdown._popper && that.dropdown._popper.state.isCreated) { + setFocus(); + } else { + requestAnimationFrame(checkPopperExists); + } + } + + this.$element.on('shown' + EVENT_KEY, function () { + if (that.$menuInner[0].scrollTop !== that.selectpicker.view.scrollTop) { + that.$menuInner[0].scrollTop = that.selectpicker.view.scrollTop; + } + + if (version.major > 3) { + requestAnimationFrame(checkPopperExists); + } else { + setFocus(); + } + }); + + // ensure posinset and setsize are correct before selecting an option via a click + this.$menuInner.on('mouseenter', 'li a', function (e) { + var hoverLi = this.parentElement, + position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0, + index = Array.prototype.indexOf.call(hoverLi.parentElement.children, hoverLi), + hoverData = that.selectpicker.current.data[index + position0]; + + that.focusItem(hoverLi, hoverData, true); + }); + + this.$menuInner.on('click', 'li a', function (e, retainActive) { + var $this = $(this), + element = that.$element[0], + position0 = that.isVirtual() ? that.selectpicker.view.position0 : 0, + clickedData = that.selectpicker.current.data[$this.parent().index() + position0], + clickedIndex = clickedData.index, + prevValue = getSelectValues(element), + prevIndex = element.selectedIndex, + prevOption = element.options[prevIndex], + triggerChange = true; + + // Don't close on multi choice menu + if (that.multiple && that.options.maxOptions !== 1) { + e.stopPropagation(); + } + + e.preventDefault(); + + // Don't run if the select is disabled + if (!that.isDisabled() && !$this.parent().hasClass(classNames.DISABLED)) { + var $options = that.$element.find('option'), + option = clickedData.option, + $option = $(option), + state = option.selected, + $optgroup = $option.parent('optgroup'), + $optgroupOptions = $optgroup.find('option'), + maxOptions = that.options.maxOptions, + maxOptionsGrp = $optgroup.data('maxOptions') || false; + + if (clickedIndex === that.activeIndex) retainActive = true; + + if (!retainActive) { + that.prevActiveIndex = that.activeIndex; + that.activeIndex = undefined; + } + + if (!that.multiple) { // Deselect all others if not multi select box + prevOption.selected = false; + option.selected = true; + that.setSelected(clickedIndex, true); + } else { // Toggle the one we have chosen if we are multi select. + option.selected = !state; + + that.setSelected(clickedIndex, !state); + $this.trigger('blur'); + + if (maxOptions !== false || maxOptionsGrp !== false) { + var maxReached = maxOptions < $options.filter(':selected').length, + maxReachedGrp = maxOptionsGrp < $optgroup.find('option:selected').length; + + if ((maxOptions && maxReached) || (maxOptionsGrp && maxReachedGrp)) { + if (maxOptions && maxOptions == 1) { + $options.prop('selected', false); + $option.prop('selected', true); + + for (var i = 0; i < $options.length; i++) { + that.setSelected(i, false); + } + + that.setSelected(clickedIndex, true); + } else if (maxOptionsGrp && maxOptionsGrp == 1) { + $optgroup.find('option:selected').prop('selected', false); + $option.prop('selected', true); + + for (var i = 0; i < $optgroupOptions.length; i++) { + var option = $optgroupOptions[i]; + that.setSelected($options.index(option), false); + } + + that.setSelected(clickedIndex, true); + } else { + var maxOptionsText = typeof that.options.maxOptionsText === 'string' ? [that.options.maxOptionsText, that.options.maxOptionsText] : that.options.maxOptionsText, + maxOptionsArr = typeof maxOptionsText === 'function' ? maxOptionsText(maxOptions, maxOptionsGrp) : maxOptionsText, + maxTxt = maxOptionsArr[0].replace('{n}', maxOptions), + maxTxtGrp = maxOptionsArr[1].replace('{n}', maxOptionsGrp), + $notify = $('
    '); + // If {var} is set in array, replace it + /** @deprecated */ + if (maxOptionsArr[2]) { + maxTxt = maxTxt.replace('{var}', maxOptionsArr[2][maxOptions > 1 ? 0 : 1]); + maxTxtGrp = maxTxtGrp.replace('{var}', maxOptionsArr[2][maxOptionsGrp > 1 ? 0 : 1]); + } + + $option.prop('selected', false); + + that.$menu.append($notify); + + if (maxOptions && maxReached) { + $notify.append($('
    ' + maxTxt + '
    ')); + triggerChange = false; + that.$element.trigger('maxReached' + EVENT_KEY); + } + + if (maxOptionsGrp && maxReachedGrp) { + $notify.append($('
    ' + maxTxtGrp + '
    ')); + triggerChange = false; + that.$element.trigger('maxReachedGrp' + EVENT_KEY); + } + + setTimeout(function () { + that.setSelected(clickedIndex, false); + }, 10); + + $notify.delay(750).fadeOut(300, function () { + $(this).remove(); + }); + } + } + } + } + + if (!that.multiple || (that.multiple && that.options.maxOptions === 1)) { + that.$button.trigger('focus'); + } else if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } + + // Trigger select 'change' + if (triggerChange) { + if (that.multiple || prevIndex !== element.selectedIndex) { + // $option.prop('selected') is current option state (selected/unselected). prevValue is the value of the select prior to being changed. + changedArguments = [option.index, $option.prop('selected'), prevValue]; + that.$element + .triggerNative('change'); + } + } + } + }); + + this.$menu.on('click', 'li.' + classNames.DISABLED + ' a, .' + classNames.POPOVERHEADER + ', .' + classNames.POPOVERHEADER + ' :not(.close)', function (e) { + if (e.currentTarget == this) { + e.preventDefault(); + e.stopPropagation(); + if (that.options.liveSearch && !$(e.target).hasClass('close')) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + } + }); + + this.$menuInner.on('click', '.divider, .dropdown-header', function (e) { + e.preventDefault(); + e.stopPropagation(); + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + }); + + this.$menu.on('click', '.' + classNames.POPOVERHEADER + ' .close', function () { + that.$button.trigger('click'); + }); + + this.$searchbox.on('click', function (e) { + e.stopPropagation(); + }); + + this.$menu.on('click', '.actions-btn', function (e) { + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + that.$button.trigger('focus'); + } + + e.preventDefault(); + e.stopPropagation(); + + if ($(this).hasClass('bs-select-all')) { + that.selectAll(); + } else { + that.deselectAll(); + } + }); + + this.$element + .on('change' + EVENT_KEY, function () { + that.render(); + that.$element.trigger('changed' + EVENT_KEY, changedArguments); + changedArguments = null; + }) + .on('focus' + EVENT_KEY, function () { + if (!that.options.mobile) that.$button.trigger('focus'); + }); + }, + + liveSearchListener: function () { + var that = this, + noResults = document.createElement('li'); + + this.$button.on('click.bs.dropdown.data-api', function () { + if (!!that.$searchbox.val()) { + that.$searchbox.val(''); + } + }); + + this.$searchbox.on('click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api', function (e) { + e.stopPropagation(); + }); + + this.$searchbox.on('input propertychange', function () { + var searchValue = that.$searchbox.val(); + + that.selectpicker.search.elements = []; + that.selectpicker.search.data = []; + + if (searchValue) { + var i, + searchMatch = [], + q = searchValue.toUpperCase(), + cache = {}, + cacheArr = [], + searchStyle = that._searchStyle(), + normalizeSearch = that.options.liveSearchNormalize; + + if (normalizeSearch) q = normalizeToBase(q); + + that._$lisSelected = that.$menuInner.find('.selected'); + + for (var i = 0; i < that.selectpicker.main.data.length; i++) { + var li = that.selectpicker.main.data[i]; + + if (!cache[i]) { + cache[i] = stringSearch(li, q, searchStyle, normalizeSearch); + } + + if (cache[i] && li.headerIndex !== undefined && cacheArr.indexOf(li.headerIndex) === -1) { + if (li.headerIndex > 0) { + cache[li.headerIndex - 1] = true; + cacheArr.push(li.headerIndex - 1); + } + + cache[li.headerIndex] = true; + cacheArr.push(li.headerIndex); + + cache[li.lastIndex + 1] = true; + } + + if (cache[i] && li.type !== 'optgroup-label') cacheArr.push(i); + } + + for (var i = 0, cacheLen = cacheArr.length; i < cacheLen; i++) { + var index = cacheArr[i], + prevIndex = cacheArr[i - 1], + li = that.selectpicker.main.data[index], + liPrev = that.selectpicker.main.data[prevIndex]; + + if (li.type !== 'divider' || (li.type === 'divider' && liPrev && liPrev.type !== 'divider' && cacheLen - 1 !== i)) { + that.selectpicker.search.data.push(li); + searchMatch.push(that.selectpicker.main.elements[index]); + } + } + + that.activeIndex = undefined; + that.noScroll = true; + that.$menuInner.scrollTop(0); + that.selectpicker.search.elements = searchMatch; + that.createView(true); + + if (!searchMatch.length) { + noResults.className = 'no-results'; + noResults.innerHTML = that.options.noneResultsText.replace('{0}', '"' + htmlEscape(searchValue) + '"'); + that.$menuInner[0].firstChild.appendChild(noResults); + } + } else { + that.$menuInner.scrollTop(0); + that.createView(false); + } + }); + }, + + _searchStyle: function () { + return this.options.liveSearchStyle || 'contains'; + }, + + val: function (value) { + var element = this.$element[0]; + + if (typeof value !== 'undefined') { + var prevValue = getSelectValues(element); + + changedArguments = [null, null, prevValue]; + + this.$element + .val(value) + .trigger('changed' + EVENT_KEY, changedArguments); + + if (this.$newElement.hasClass(classNames.SHOW)) { + if (this.multiple) { + this.setOptionStatus(true); + } else { + var liSelectedIndex = (element.options[element.selectedIndex] || {}).liIndex; + + if (typeof liSelectedIndex === 'number') { + this.setSelected(this.selectedIndex, false); + this.setSelected(liSelectedIndex, true); + } + } + } + + this.render(); + + changedArguments = null; + + return this.$element; + } else { + return this.$element.val(); + } + }, + + changeAll: function (status) { + if (!this.multiple) return; + if (typeof status === 'undefined') status = true; + + var element = this.$element[0], + previousSelected = 0, + currentSelected = 0, + prevValue = getSelectValues(element); + + element.classList.add('bs-select-hidden'); + + for (var i = 0, len = this.selectpicker.current.elements.length; i < len; i++) { + var liData = this.selectpicker.current.data[i], + option = liData.option; + + if (option && !liData.disabled && liData.type !== 'divider') { + if (liData.selected) previousSelected++; + option.selected = status; + if (status) currentSelected++; + } + } + + element.classList.remove('bs-select-hidden'); + + if (previousSelected === currentSelected) return; + + this.setOptionStatus(); + + changedArguments = [null, null, prevValue]; + + this.$element + .triggerNative('change'); + }, + + selectAll: function () { + return this.changeAll(true); + }, + + deselectAll: function () { + return this.changeAll(false); + }, + + toggle: function (e) { + e = e || window.event; + + if (e) e.stopPropagation(); + + this.$button.trigger('click.bs.dropdown.data-api'); + }, + + keydown: function (e) { + var $this = $(this), + isToggle = $this.hasClass('dropdown-toggle'), + $parent = isToggle ? $this.closest('.dropdown') : $this.closest(Selector.MENU), + that = $parent.data('this'), + $items = that.findLis(), + index, + isActive, + liActive, + activeLi, + offset, + updateScroll = false, + downOnTab = e.which === keyCodes.TAB && !isToggle && !that.options.selectOnTab, + isArrowKey = REGEXP_ARROW.test(e.which) || downOnTab, + scrollTop = that.$menuInner[0].scrollTop, + isVirtual = that.isVirtual(), + position0 = isVirtual === true ? that.selectpicker.view.position0 : 0; + + isActive = that.$newElement.hasClass(classNames.SHOW); + + if ( + !isActive && + ( + isArrowKey || + (e.which >= 48 && e.which <= 57) || + (e.which >= 96 && e.which <= 105) || + (e.which >= 65 && e.which <= 90) + ) + ) { + that.$button.trigger('click.bs.dropdown.data-api'); + + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + return; + } + } + + if (e.which === keyCodes.ESCAPE && isActive) { + e.preventDefault(); + that.$button.trigger('click.bs.dropdown.data-api').trigger('focus'); + } + + if (isArrowKey) { // if up or down + if (!$items.length) return; + + liActive = that.selectpicker.main.elements[that.activeIndex]; + index = liActive ? Array.prototype.indexOf.call(liActive.parentElement.children, liActive) : -1; + + if (index !== -1) { + that.defocusItem(liActive); + } + + if (e.which === keyCodes.ARROW_UP) { // up + if (index !== -1) index--; + if (index + position0 < 0) index += $items.length; + + if (!that.selectpicker.view.canHighlight[index + position0]) { + index = that.selectpicker.view.canHighlight.slice(0, index + position0).lastIndexOf(true) - position0; + if (index === -1) index = $items.length - 1; + } + } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down + index++; + if (index + position0 >= that.selectpicker.view.canHighlight.length) index = 0; + + if (!that.selectpicker.view.canHighlight[index + position0]) { + index = index + 1 + that.selectpicker.view.canHighlight.slice(index + position0 + 1).indexOf(true); + } + } + + e.preventDefault(); + + var liActiveIndex = position0 + index; + + if (e.which === keyCodes.ARROW_UP) { // up + // scroll to bottom and highlight last option + if (position0 === 0 && index === $items.length - 1) { + that.$menuInner[0].scrollTop = that.$menuInner[0].scrollHeight; + + liActiveIndex = that.selectpicker.current.elements.length - 1; + } else { + activeLi = that.selectpicker.current.data[liActiveIndex]; + offset = activeLi.position - activeLi.height; + + updateScroll = offset < scrollTop; + } + } else if (e.which === keyCodes.ARROW_DOWN || downOnTab) { // down + // scroll to top and highlight first option + if (index === 0) { + that.$menuInner[0].scrollTop = 0; + + liActiveIndex = 0; + } else { + activeLi = that.selectpicker.current.data[liActiveIndex]; + offset = activeLi.position - that.sizeInfo.menuInnerHeight; + + updateScroll = offset > scrollTop; + } + } + + liActive = that.selectpicker.current.elements[liActiveIndex]; + + that.activeIndex = that.selectpicker.current.data[liActiveIndex].index; + + that.focusItem(liActive); + + that.selectpicker.view.currentActive = liActive; + + if (updateScroll) that.$menuInner[0].scrollTop = offset; + + if (that.options.liveSearch) { + that.$searchbox.trigger('focus'); + } else { + $this.trigger('focus'); + } + } else if ( + (!$this.is('input') && !REGEXP_TAB_OR_ESCAPE.test(e.which)) || + (e.which === keyCodes.SPACE && that.selectpicker.keydown.keyHistory) + ) { + var searchMatch, + matches = [], + keyHistory; + + e.preventDefault(); + + that.selectpicker.keydown.keyHistory += keyCodeMap[e.which]; + + if (that.selectpicker.keydown.resetKeyHistory.cancel) clearTimeout(that.selectpicker.keydown.resetKeyHistory.cancel); + that.selectpicker.keydown.resetKeyHistory.cancel = that.selectpicker.keydown.resetKeyHistory.start(); + + keyHistory = that.selectpicker.keydown.keyHistory; + + // if all letters are the same, set keyHistory to just the first character when searching + if (/^(.)\1+$/.test(keyHistory)) { + keyHistory = keyHistory.charAt(0); + } + + // find matches + for (var i = 0; i < that.selectpicker.current.data.length; i++) { + var li = that.selectpicker.current.data[i], + hasMatch; + + hasMatch = stringSearch(li, keyHistory, 'startsWith', true); + + if (hasMatch && that.selectpicker.view.canHighlight[i]) { + matches.push(li.index); + } + } + + if (matches.length) { + var matchIndex = 0; + + $items.removeClass('active').find('a').removeClass('active'); + + // either only one key has been pressed or they are all the same key + if (keyHistory.length === 1) { + matchIndex = matches.indexOf(that.activeIndex); + + if (matchIndex === -1 || matchIndex === matches.length - 1) { + matchIndex = 0; + } else { + matchIndex++; + } + } + + searchMatch = matches[matchIndex]; + + activeLi = that.selectpicker.main.data[searchMatch]; + + if (scrollTop - activeLi.position > 0) { + offset = activeLi.position - activeLi.height; + updateScroll = true; + } else { + offset = activeLi.position - that.sizeInfo.menuInnerHeight; + // if the option is already visible at the current scroll position, just keep it the same + updateScroll = activeLi.position > scrollTop + that.sizeInfo.menuInnerHeight; + } + + liActive = that.selectpicker.main.elements[searchMatch]; + + that.activeIndex = matches[matchIndex]; + + that.focusItem(liActive); + + if (liActive) liActive.firstChild.focus(); + + if (updateScroll) that.$menuInner[0].scrollTop = offset; + + $this.trigger('focus'); + } + } + + // Select focused option if "Enter", "Spacebar" or "Tab" (when selectOnTab is true) are pressed inside the menu. + if ( + isActive && + ( + (e.which === keyCodes.SPACE && !that.selectpicker.keydown.keyHistory) || + e.which === keyCodes.ENTER || + (e.which === keyCodes.TAB && that.options.selectOnTab) + ) + ) { + if (e.which !== keyCodes.SPACE) e.preventDefault(); + + if (!that.options.liveSearch || e.which !== keyCodes.SPACE) { + that.$menuInner.find('.active a').trigger('click', true); // retain active class + $this.trigger('focus'); + + if (!that.options.liveSearch) { + // Prevent screen from scrolling if the user hits the spacebar + e.preventDefault(); + // Fixes spacebar selection of dropdown items in FF & IE + $(document).data('spaceSelect', true); + } + } + } + }, + + mobile: function () { + this.$element[0].classList.add('mobile-device'); + }, + + refresh: function () { + // update options if data attributes have been changed + var config = $.extend({}, this.options, this.$element.data()); + this.options = config; + + this.checkDisabled(); + this.setStyle(); + this.render(); + this.createLi(); + this.setWidth(); + + this.setSize(true); + + this.$element.trigger('refreshed' + EVENT_KEY); + }, + + hide: function () { + this.$newElement.hide(); + }, + + show: function () { + this.$newElement.show(); + }, + + remove: function () { + this.$newElement.remove(); + this.$element.remove(); + }, + + destroy: function () { + this.$newElement.before(this.$element).remove(); + + if (this.$bsContainer) { + this.$bsContainer.remove(); + } else { + this.$menu.remove(); + } + + this.$element + .off(EVENT_KEY) + .removeData('selectpicker') + .removeClass('bs-select-hidden selectpicker'); + + $(window).off(EVENT_KEY + '.' + this.selectId); + } + }; + + // SELECTPICKER PLUGIN DEFINITION + // ============================== + function Plugin (option) { + // get the args of the outer function.. + var args = arguments; + // The arguments of the function are explicitly re-defined from the argument list, because the shift causes them + // to get lost/corrupted in android 2.3 and IE9 #715 #775 + var _option = option; + + [].shift.apply(args); + + // if the version was not set successfully + if (!version.success) { + // try to retreive it again + try { + version.full = ($.fn.dropdown.Constructor.VERSION || '').split(' ')[0].split('.'); + } catch (err) { + // fall back to use BootstrapVersion if set + if (Selectpicker.BootstrapVersion) { + version.full = Selectpicker.BootstrapVersion.split(' ')[0].split('.'); + } else { + version.full = [version.major, '0', '0']; + + console.warn( + 'There was an issue retrieving Bootstrap\'s version. ' + + 'Ensure Bootstrap is being loaded before bootstrap-select and there is no namespace collision. ' + + 'If loading Bootstrap asynchronously, the version may need to be manually specified via $.fn.selectpicker.Constructor.BootstrapVersion.', + err + ); + } + } + + version.major = version.full[0]; + version.success = true; + } + + if (version.major === '4') { + // some defaults need to be changed if using Bootstrap 4 + // check to see if they have already been manually changed before forcing them to update + var toUpdate = []; + + if (Selectpicker.DEFAULTS.style === classNames.BUTTONCLASS) toUpdate.push({ name: 'style', className: 'BUTTONCLASS' }); + if (Selectpicker.DEFAULTS.iconBase === classNames.ICONBASE) toUpdate.push({ name: 'iconBase', className: 'ICONBASE' }); + if (Selectpicker.DEFAULTS.tickIcon === classNames.TICKICON) toUpdate.push({ name: 'tickIcon', className: 'TICKICON' }); + + classNames.DIVIDER = 'dropdown-divider'; + classNames.SHOW = 'show'; + classNames.BUTTONCLASS = 'btn-light'; + classNames.POPOVERHEADER = 'popover-header'; + classNames.ICONBASE = ''; + classNames.TICKICON = 'bs-ok-default'; + + for (var i = 0; i < toUpdate.length; i++) { + var option = toUpdate[i]; + Selectpicker.DEFAULTS[option.name] = classNames[option.className]; + } + } + + var value; + var chain = this.each(function () { + var $this = $(this); + if ($this.is('select')) { + var data = $this.data('selectpicker'), + options = typeof _option == 'object' && _option; + + if (!data) { + var dataAttributes = $this.data(); + + for (var dataAttr in dataAttributes) { + if (dataAttributes.hasOwnProperty(dataAttr) && $.inArray(dataAttr, DISALLOWED_ATTRIBUTES) !== -1) { + delete dataAttributes[dataAttr]; + } + } + + var config = $.extend({}, Selectpicker.DEFAULTS, $.fn.selectpicker.defaults || {}, dataAttributes, options); + config.template = $.extend({}, Selectpicker.DEFAULTS.template, ($.fn.selectpicker.defaults ? $.fn.selectpicker.defaults.template : {}), dataAttributes.template, options.template); + $this.data('selectpicker', (data = new Selectpicker(this, config))); + } else if (options) { + for (var i in options) { + if (options.hasOwnProperty(i)) { + data.options[i] = options[i]; + } + } + } + + if (typeof _option == 'string') { + if (data[_option] instanceof Function) { + value = data[_option].apply(data, args); + } else { + value = data.options[_option]; + } + } + } + }); + + if (typeof value !== 'undefined') { + // noinspection JSUnusedAssignment + return value; + } else { + return chain; + } + } + + var old = $.fn.selectpicker; + $.fn.selectpicker = Plugin; + $.fn.selectpicker.Constructor = Selectpicker; + + // SELECTPICKER NO CONFLICT + // ======================== + $.fn.selectpicker.noConflict = function () { + $.fn.selectpicker = old; + return this; + }; + + $(document) + .off('keydown.bs.dropdown.data-api') + .on('keydown' + EVENT_KEY, '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', Selectpicker.prototype.keydown) + .on('focusin.modal', '.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input', function (e) { + e.stopPropagation(); + }); + + // SELECTPICKER DATA-API + // ===================== + $(window).on('load' + EVENT_KEY + '.data-api', function () { + $('.selectpicker').each(function () { + var $selectpicker = $(this); + Plugin.call($selectpicker, $selectpicker.data()); + }) + }); +})(jQuery); + + +})); diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.css new file mode 100644 index 000000000..dafcce3a0 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap-select v1.13.10 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */.bootstrap-select>select.bs-select-hidden,select.bs-select-hidden,select.selectpicker{display:none!important}.bootstrap-select{width:220px\0;vertical-align:middle}.bootstrap-select>.dropdown-toggle{position:relative;width:100%;text-align:right;white-space:nowrap;display:-webkit-inline-box;display:-webkit-inline-flex;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:justify;-webkit-justify-content:space-between;-ms-flex-pack:justify;justify-content:space-between}.bootstrap-select>.dropdown-toggle:after{margin-top:-1px}.bootstrap-select>.dropdown-toggle.bs-placeholder,.bootstrap-select>.dropdown-toggle.bs-placeholder:active,.bootstrap-select>.dropdown-toggle.bs-placeholder:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder:hover{color:#999}.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-danger:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-dark:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-info:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-primary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-secondary:hover,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:active,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:focus,.bootstrap-select>.dropdown-toggle.bs-placeholder.btn-success:hover{color:rgba(255,255,255,.5)}.bootstrap-select>select{position:absolute!important;bottom:0;left:50%;display:block!important;width:.5px!important;height:100%!important;padding:0!important;opacity:0!important;border:none;z-index:0!important}.bootstrap-select>select.mobile-device{top:0;left:0;display:block!important;width:100%!important;z-index:2!important}.bootstrap-select.is-invalid .dropdown-toggle,.error .bootstrap-select .dropdown-toggle,.has-error .bootstrap-select .dropdown-toggle,.was-validated .bootstrap-select .selectpicker:invalid+.dropdown-toggle{border-color:#b94a48}.bootstrap-select.is-valid .dropdown-toggle,.was-validated .bootstrap-select .selectpicker:valid+.dropdown-toggle{border-color:#28a745}.bootstrap-select.fit-width{width:auto!important}.bootstrap-select:not([class*=col-]):not([class*=form-control]):not(.input-group-btn){width:220px}.bootstrap-select .dropdown-toggle:focus,.bootstrap-select>select.mobile-device:focus+.dropdown-toggle{outline:thin dotted #333!important;outline:5px auto -webkit-focus-ring-color!important;outline-offset:-2px}.bootstrap-select.form-control{margin-bottom:0;padding:0;border:none;height:auto}:not(.input-group)>.bootstrap-select.form-control:not([class*=col-]){width:100%}.bootstrap-select.form-control.input-group-btn{float:none;z-index:auto}.form-inline .bootstrap-select,.form-inline .bootstrap-select.form-control:not([class*=col-]){width:auto}.bootstrap-select:not(.input-group-btn),.bootstrap-select[class*=col-]{float:none;display:inline-block;margin-left:0}.bootstrap-select.dropdown-menu-right,.bootstrap-select[class*=col-].dropdown-menu-right,.row .bootstrap-select[class*=col-].dropdown-menu-right{float:right}.form-group .bootstrap-select,.form-horizontal .bootstrap-select,.form-inline .bootstrap-select{margin-bottom:0}.form-group-lg .bootstrap-select.form-control,.form-group-sm .bootstrap-select.form-control{padding:0}.form-group-lg .bootstrap-select.form-control .dropdown-toggle,.form-group-sm .bootstrap-select.form-control .dropdown-toggle{height:100%;font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-lg .dropdown-toggle,.bootstrap-select.form-control-sm .dropdown-toggle{font-size:inherit;line-height:inherit;border-radius:inherit}.bootstrap-select.form-control-sm .dropdown-toggle{padding:.25rem .5rem}.bootstrap-select.form-control-lg .dropdown-toggle{padding:.5rem 1rem}.form-inline .bootstrap-select .form-control{width:100%}.bootstrap-select.disabled,.bootstrap-select>.disabled{cursor:not-allowed}.bootstrap-select.disabled:focus,.bootstrap-select>.disabled:focus{outline:0!important}.bootstrap-select.bs-container{position:absolute;top:0;left:0;height:0!important;padding:0!important}.bootstrap-select.bs-container .dropdown-menu{z-index:1060}.bootstrap-select .dropdown-toggle .filter-option{position:static;top:0;left:0;float:left;height:100%;width:100%;text-align:left;overflow:hidden;-webkit-box-flex:0;-webkit-flex:0 1 auto;-ms-flex:0 1 auto;flex:0 1 auto}.bs3.bootstrap-select .dropdown-toggle .filter-option{padding-right:inherit}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option{position:absolute;padding-top:inherit;padding-bottom:inherit;padding-left:inherit;float:none}.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner{padding-right:inherit}.bootstrap-select .dropdown-toggle .filter-option-inner-inner{overflow:hidden}.bootstrap-select .dropdown-toggle .filter-expand{width:0!important;float:left;opacity:0!important;overflow:hidden}.bootstrap-select .dropdown-toggle .caret{position:absolute;top:50%;right:12px;margin-top:-2px;vertical-align:middle}.input-group .bootstrap-select.form-control .dropdown-toggle{border-radius:inherit}.bootstrap-select[class*=col-] .dropdown-toggle{width:100%}.bootstrap-select .dropdown-menu{min-width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .dropdown-menu>.inner:focus{outline:0!important}.bootstrap-select .dropdown-menu.inner{position:static;float:none;border:0;padding:0;margin:0;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.bootstrap-select .dropdown-menu li{position:relative}.bootstrap-select .dropdown-menu li.active small{color:rgba(255,255,255,.5)!important}.bootstrap-select .dropdown-menu li.disabled a{cursor:not-allowed}.bootstrap-select .dropdown-menu li a{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.bootstrap-select .dropdown-menu li a.opt{position:relative;padding-left:2.25em}.bootstrap-select .dropdown-menu li a span.check-mark{display:none}.bootstrap-select .dropdown-menu li a span.text{display:inline-block}.bootstrap-select .dropdown-menu li small{padding-left:.5em}.bootstrap-select .dropdown-menu .notify{position:absolute;bottom:5px;width:96%;margin:0 2%;min-height:26px;padding:3px 5px;background:#f5f5f5;border:1px solid #e3e3e3;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05);pointer-events:none;opacity:.9;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bootstrap-select .no-results{padding:3px;background:#f5f5f5;margin:0 5px;white-space:nowrap}.bootstrap-select.fit-width .dropdown-toggle .filter-option{position:static;display:inline;padding:0}.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner{display:inline}.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before{content:'\00a0'}.bootstrap-select.fit-width .dropdown-toggle .caret{position:static;top:auto;margin-top:-1px}.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark{position:absolute;display:inline-block;right:15px;top:5px}.bootstrap-select.show-tick .dropdown-menu li a span.text{margin-right:34px}.bootstrap-select .bs-ok-default:after{content:'';display:block;width:.5em;height:1em;border-style:solid;border-width:0 .26em .26em 0;-webkit-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle{z-index:1061}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before{content:'';border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid rgba(204,204,204,.2);position:absolute;bottom:-4px;left:9px;display:none}.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after{content:'';border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;bottom:-4px;left:10px;display:none}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before{bottom:auto;top:-4px;border-top:7px solid rgba(204,204,204,.2);border-bottom:0}.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after{bottom:auto;top:-4px;border-top:6px solid #fff;border-bottom:0}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before{right:12px;left:auto}.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after{right:13px;left:auto}.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.open>.dropdown-toggle .filter-option:before,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:after,.bootstrap-select.show-menu-arrow.show>.dropdown-toggle .filter-option:before{display:block}.bs-actionsbox,.bs-donebutton,.bs-searchbox{padding:4px 8px}.bs-actionsbox{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-actionsbox .btn-group button{width:50%}.bs-donebutton{float:left;width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.bs-donebutton .btn-group button{width:100%}.bs-searchbox+.bs-actionsbox{padding:0 8px 4px}.bs-searchbox .form-control{margin-bottom:0;width:100%;float:none} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.js new file mode 100644 index 000000000..887da61cc --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-select/bootstrap-select.min.js @@ -0,0 +1,8 @@ +/*! + * Bootstrap-select v1.13.10 (https://developer.snapappointments.com/bootstrap-select) + * + * Copyright 2012-2019 SnapAppointments, LLC + * Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE) + */ + +!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){!function(z){"use strict";var d=["sanitize","whiteList","sanitizeFn"],r=["background","cite","href","itemtype","longdesc","poster","src","xlink:href"],e={"*":["class","dir","id","lang","role","tabindex","style",/^aria-[\w-]*$/i],a:["target","href","title","rel"],area:[],b:[],br:[],col:[],code:[],div:[],em:[],hr:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],i:[],img:["src","alt","title","width","height"],li:[],ol:[],p:[],pre:[],s:[],small:[],span:[],sub:[],sup:[],strong:[],u:[],ul:[]},l=/^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|$))/gi,a=/^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i;function v(e,t){var i=e.nodeName.toLowerCase();if(-1!==z.inArray(i,t))return-1===z.inArray(i,r)||Boolean(e.nodeValue.match(l)||e.nodeValue.match(a));for(var s=z(t).filter(function(e,t){return t instanceof RegExp}),n=0,o=s.length;n]+>/g,"")),s&&(a=w(a)),a=a.toUpperCase(),o="contains"===i?0<=a.indexOf(t):a.startsWith(t)))break}return o}function A(e){return parseInt(e,10)||0}z.fn.triggerNative=function(e){var t,i=this[0];i.dispatchEvent?(u?t=new Event(e,{bubbles:!0}):(t=document.createEvent("Event")).initEvent(e,!0,!1),i.dispatchEvent(t)):i.fireEvent?((t=document.createEventObject()).eventType=e,i.fireEvent("on"+e,t)):this.trigger(e)};var f={"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"},m=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,g=RegExp("[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff\\u1ab0-\\u1aff\\u1dc0-\\u1dff]","g");function b(e){return f[e]}function w(e){return(e=e.toString())&&e.replace(m,b).replace(g,"")}var I,x,$,y,S,E=(I={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},x=function(e){return I[e]},$="(?:"+Object.keys(I).join("|")+")",y=RegExp($),S=RegExp($,"g"),function(e){return e=null==e?"":""+e,y.test(e)?e.replace(S,x):e}),C={32:" ",48:"0",49:"1",50:"2",51:"3",52:"4",53:"5",54:"6",55:"7",56:"8",57:"9",59:";",65:"A",66:"B",67:"C",68:"D",69:"E",70:"F",71:"G",72:"H",73:"I",74:"J",75:"K",76:"L",77:"M",78:"N",79:"O",80:"P",81:"Q",82:"R",83:"S",84:"T",85:"U",86:"V",87:"W",88:"X",89:"Y",90:"Z",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9"},L=27,N=13,D=32,H=9,B=38,W=40,M={success:!1,major:"3"};try{M.full=(z.fn.dropdown.Constructor.VERSION||"").split(" ")[0].split("."),M.major=M.full[0],M.success=!0}catch(e){}var R=0,U=".bs.select",j={DISABLED:"disabled",DIVIDER:"divider",SHOW:"open",DROPUP:"dropup",MENU:"dropdown-menu",MENURIGHT:"dropdown-menu-right",MENULEFT:"dropdown-menu-left",BUTTONCLASS:"btn-default",POPOVERHEADER:"popover-title",ICONBASE:"glyphicon",TICKICON:"glyphicon-ok"},V={MENU:"."+j.MENU},F={span:document.createElement("span"),i:document.createElement("i"),subtext:document.createElement("small"),a:document.createElement("a"),li:document.createElement("li"),whitespace:document.createTextNode("\xa0"),fragment:document.createDocumentFragment()};F.a.setAttribute("role","option"),F.subtext.className="text-muted",F.text=F.span.cloneNode(!1),F.text.className="text",F.checkMark=F.span.cloneNode(!1);var _=new RegExp(B+"|"+W),G=new RegExp("^"+H+"$|"+L),q=function(e,t,i){var s=F.li.cloneNode(!1);return e&&(1===e.nodeType||11===e.nodeType?s.appendChild(e):s.innerHTML=e),void 0!==t&&""!==t&&(s.className=t),null!=i&&s.classList.add("optgroup-"+i),s},K=function(e,t,i){var s=F.a.cloneNode(!0);return e&&(11===e.nodeType?s.appendChild(e):s.insertAdjacentHTML("beforeend",e)),void 0!==t&&""!==t&&(s.className=t),"4"===M.major&&s.classList.add("dropdown-item"),i&&s.setAttribute("style",i),s},Y=function(e,t){var i,s,n=F.text.cloneNode(!1);if(e.content)n.innerHTML=e.content;else{if(n.textContent=e.text,e.icon){var o=F.whitespace.cloneNode(!1);(s=(!0===t?F.i:F.span).cloneNode(!1)).className=e.iconBase+" "+e.icon,F.fragment.appendChild(s),F.fragment.appendChild(o)}e.subtext&&((i=F.subtext.cloneNode(!1)).textContent=e.subtext,n.appendChild(i))}if(!0===t)for(;0'},maxOptions:!1,mobile:!1,selectOnTab:!1,dropdownAlignRight:!1,windowPadding:0,virtualScroll:600,display:!1,sanitize:!0,sanitizeFn:null,whiteList:e},J.prototype={constructor:J,init:function(){var i=this,e=this.$element.attr("id");R++,this.selectId="bs-select-"+R,this.$element[0].classList.add("bs-select-hidden"),this.multiple=this.$element.prop("multiple"),this.autofocus=this.$element.prop("autofocus"),this.$element[0].classList.contains("show-tick")&&(this.options.showTick=!0),this.$newElement=this.createDropdown(),this.$element.after(this.$newElement).prependTo(this.$newElement),this.$button=this.$newElement.children("button"),this.$menu=this.$newElement.children(V.MENU),this.$menuInner=this.$menu.children(".inner"),this.$searchbox=this.$menu.find("input"),this.$element[0].classList.remove("bs-select-hidden"),!0===this.options.dropdownAlignRight&&this.$menu[0].classList.add(j.MENURIGHT),void 0!==e&&this.$button.attr("data-id",e),this.checkDisabled(),this.clickListener(),this.options.liveSearch?(this.liveSearchListener(),this.focusedParent=this.$searchbox[0]):this.focusedParent=this.$menuInner[0],this.setStyle(),this.render(),this.setWidth(),this.options.container?this.selectPosition():this.$element.on("hide"+U,function(){if(i.isVirtual()){var e=i.$menuInner[0],t=e.firstChild.cloneNode(!1);e.replaceChild(t,e.firstChild),e.scrollTop=0}}),this.$menu.data("this",this),this.$newElement.data("this",this),this.options.mobile&&this.mobile(),this.$newElement.on({"hide.bs.dropdown":function(e){i.$element.trigger("hide"+U,e)},"hidden.bs.dropdown":function(e){i.$element.trigger("hidden"+U,e)},"show.bs.dropdown":function(e){i.$element.trigger("show"+U,e)},"shown.bs.dropdown":function(e){i.$element.trigger("shown"+U,e)}}),i.$element[0].hasAttribute("required")&&this.$element.on("invalid"+U,function(){i.$button[0].classList.add("bs-invalid"),i.$element.on("shown"+U+".invalid",function(){i.$element.val(i.$element.val()).off("shown"+U+".invalid")}).on("rendered"+U,function(){this.validity.valid&&i.$button[0].classList.remove("bs-invalid"),i.$element.off("rendered"+U)}),i.$button.on("blur"+U,function(){i.$element.trigger("focus").trigger("blur"),i.$button.off("blur"+U)})}),setTimeout(function(){i.createLi(),i.$element.trigger("loaded"+U)})},createDropdown:function(){var e=this.multiple||this.options.showTick?" show-tick":"",t=this.multiple?' aria-multiselectable="true"':"",i="",s=this.autofocus?" autofocus":"";M.major<4&&this.$element.parent().hasClass("input-group")&&(i=" input-group-btn");var n,o="",r="",l="",a="";return this.options.header&&(o='
    '+this.options.header+"
    "),this.options.liveSearch&&(r=''),this.multiple&&this.options.actionsBox&&(l='
    "),this.multiple&&this.options.doneButton&&(a='
    "),n='",z(n)},setPositionData:function(){this.selectpicker.view.canHighlight=[];for(var e=this.selectpicker.view.size=0;e=this.options.virtualScroll||!0===this.options.virtualScroll},createView:function(A,e,t){var L,N,D=this,i=0,H=[];if(this.selectpicker.current=A?this.selectpicker.search:this.selectpicker.main,this.setPositionData(),e)if(t)i=this.$menuInner[0].scrollTop;else if(!D.multiple){var s=D.$element[0],n=(s.options[s.selectedIndex]||{}).liIndex;if("number"==typeof n&&!1!==D.options.size){var o=D.selectpicker.main.data[n],r=o&&o.position;r&&(i=r-(D.sizeInfo.menuInnerHeight+D.sizeInfo.liHeight)/2)}}function l(e,t){var i,s,n,o,r,l,a,c,d,h,p=D.selectpicker.current.elements.length,u=[],f=!0,m=D.isVirtual();D.selectpicker.view.scrollTop=e,!0===m&&D.sizeInfo.hasScrollBar&&D.$menu[0].offsetWidth>D.sizeInfo.totalMenuWidth&&(D.sizeInfo.menuWidth=D.$menu[0].offsetWidth,D.sizeInfo.totalMenuWidth=D.sizeInfo.menuWidth+D.sizeInfo.scrollBarWidth,D.$menu.css("min-width",D.sizeInfo.menuWidth)),i=Math.ceil(D.sizeInfo.menuInnerHeight/D.sizeInfo.liHeight*1.5),s=Math.round(p/i)||1;for(var v=0;vp-1?0:D.selectpicker.current.data[p-1].position-D.selectpicker.current.data[D.selectpicker.view.position1-1].position,I.firstChild.style.marginTop=b+"px",w+"px"):I.firstChild.style.marginTop=0,I.firstChild.appendChild(x)}if(D.prevActiveIndex=D.activeIndex,D.options.liveSearch){if(A&&t){var z,T=0;D.selectpicker.view.canHighlight[T]||(T=1+D.selectpicker.view.canHighlight.slice(1).indexOf(!0)),z=D.selectpicker.view.visibleElements[T],D.defocusItem(D.selectpicker.view.currentActive),D.activeIndex=(D.selectpicker.current.data[T]||{}).index,D.focusItem(z)}}else D.$menuInner.trigger("focus")}l(i,!0),this.$menuInner.off("scroll.createView").on("scroll.createView",function(e,t){D.noScroll||l(this.scrollTop,t),D.noScroll=!1}),z(window).off("resize"+U+"."+this.selectId+".createView").on("resize"+U+"."+this.selectId+".createView",function(){D.$newElement.hasClass(j.SHOW)&&l(D.$menuInner[0].scrollTop)})},focusItem:function(e,t,i){if(e){t=t||this.selectpicker.main.data[this.activeIndex];var s=e.firstChild;s&&(s.setAttribute("aria-setsize",this.selectpicker.view.size),s.setAttribute("aria-posinset",t.posinset),!0!==i&&(this.focusedParent.setAttribute("aria-activedescendant",s.id),e.classList.add("active"),s.classList.add("active")))}},defocusItem:function(e){e&&(e.classList.remove("active"),e.firstChild&&e.firstChild.classList.remove("active"))},setPlaceholder:function(){var e=!1;if(this.options.title&&!this.multiple){this.selectpicker.view.titleOption||(this.selectpicker.view.titleOption=document.createElement("option")),e=!0;var t=this.$element[0],i=!1,s=!this.selectpicker.view.titleOption.parentNode;if(s)this.selectpicker.view.titleOption.className="bs-title-option",this.selectpicker.view.titleOption.value="",i=void 0===z(t.options[t.selectedIndex]).attr("selected")&&void 0===this.$element.data("selected");(s||0!==this.selectpicker.view.titleOption.index)&&t.insertBefore(this.selectpicker.view.titleOption,t.firstChild),i&&(t.selectedIndex=0)}return e},createLi:function(){var c=this,f=this.options.iconBase,m=':not([hidden]):not([data-hidden="true"])',v=[],g=[],d=0,b=0,e=this.setPlaceholder()?1:0;this.options.hideDisabled&&(m+=":not(:disabled)"),!c.options.showTick&&!c.multiple||F.checkMark.parentNode||(F.checkMark.className=f+" "+c.options.tickIcon+" check-mark",F.a.appendChild(F.checkMark));var t=this.$element[0].querySelectorAll("select > *"+m);function w(e){var t=g[g.length-1];t&&"divider"===t.type&&(t.optID||e.optID)||((e=e||{}).type="divider",v.push(q(!1,j.DIVIDER,e.optID?e.optID+"div":void 0)),g.push(e))}function I(e,t){if((t=t||{}).divider="true"===e.getAttribute("data-divider"),t.divider)w({optID:t.optID});else{var i=g.length,s=e.style.cssText,n=s?E(s):"",o=(e.className||"")+(t.optgroupClass||"");t.optID&&(o="opt "+o),t.text=e.textContent,t.content=e.getAttribute("data-content"),t.tokens=e.getAttribute("data-tokens"),t.subtext=e.getAttribute("data-subtext"),t.icon=e.getAttribute("data-icon"),t.iconBase=f;var r=Y(t),l=q(K(r,o,n),"",t.optID);l.firstChild&&(l.firstChild.id=c.selectId+"-"+i),v.push(l),e.liIndex=i,t.display=t.content||t.text,t.type="option",t.index=i,t.option=e,t.disabled=t.disabled||e.disabled,g.push(t);var a=0;t.display&&(a+=t.display.length),t.subtext&&(a+=t.subtext.length),t.icon&&(a+=1),d li")},render:function(){this.setPlaceholder();var e,t,i=this,s=this.$element[0],n=function(e,t){var i,s=e.selectedOptions,n=[];if(t){for(var o=0,r=s.length;o")).length&&o>t[1]||1===t.length&&2<=o),!1===e){for(var h=0;h option"+m+", optgroup"+m+" option"+m).length,g="function"==typeof this.options.countSelectedText?this.options.countSelectedText(o,v):this.options.countSelectedText;c=Y({text:g.replace("{0}",o.toString()).replace("{1}",v.toString())},!0)}if(null==this.options.title&&(this.options.title=this.$element.attr("title")),c.childNodes.length||(c=Y({text:void 0!==this.options.title?this.options.title:this.options.noneSelectedText},!0)),r.title=c.textContent.replace(/<[^>]*>?/g,"").trim(),this.options.sanitize&&d&&P([c],i.options.whiteList,i.options.sanitizeFn),l.innerHTML="",l.appendChild(c),M.major<4&&this.$newElement[0].classList.contains("bs3-has-addon")){var b=r.querySelector(".filter-expand"),w=l.cloneNode(!0);w.className="filter-expand",b?r.replaceChild(w,b):r.appendChild(w)}this.$element.trigger("rendered"+U)},setStyle:function(e,t){var i,s=this.$button[0],n=this.$newElement[0],o=this.options.style.trim();this.$element.attr("class")&&this.$newElement.addClass(this.$element.attr("class").replace(/selectpicker|mobile-device|bs-select-hidden|validate\[.*\]/gi,"")),M.major<4&&(n.classList.add("bs3"),n.parentNode.classList.contains("input-group")&&(n.previousElementSibling||n.nextElementSibling)&&(n.previousElementSibling||n.nextElementSibling).classList.contains("input-group-addon")&&n.classList.add("bs3-has-addon")),i=e?e.trim():o,"add"==t?i&&s.classList.add.apply(s.classList,i.split(" ")):"remove"==t?i&&s.classList.remove.apply(s.classList,i.split(" ")):(o&&s.classList.remove.apply(s.classList,o.split(" ")),i&&s.classList.add.apply(s.classList,i.split(" ")))},liHeight:function(e){if(e||!1!==this.options.size&&!this.sizeInfo){this.sizeInfo||(this.sizeInfo={});var t=document.createElement("div"),i=document.createElement("div"),s=document.createElement("div"),n=document.createElement("ul"),o=document.createElement("li"),r=document.createElement("li"),l=document.createElement("li"),a=document.createElement("a"),c=document.createElement("span"),d=this.options.header&&0this.sizeInfo.menuExtras.vert&&l+this.sizeInfo.menuExtras.vert+50>this.sizeInfo.selectOffsetBot)),"auto"===this.options.size)n=3this.options.size){for(var g=0;gthis.sizeInfo.selectOffsetRight&&this.sizeInfo.selectOffsetRightthis.sizeInfo.menuInnerHeight&&(this.sizeInfo.hasScrollBar=!0,this.sizeInfo.totalMenuWidth=this.sizeInfo.menuWidth+this.sizeInfo.scrollBarWidth,this.$menu.css("min-width",this.sizeInfo.totalMenuWidth)),this.dropdown&&this.dropdown._popper&&this.dropdown._popper.update()},setSize:function(e){if(this.liHeight(e),this.options.header&&this.$menu.css("padding-top",0),!1!==this.options.size){var t=this,i=z(window);this.setMenuSize(),this.options.liveSearch&&this.$searchbox.off("input.setMenuSize propertychange.setMenuSize").on("input.setMenuSize propertychange.setMenuSize",function(){return t.setMenuSize()}),"auto"===this.options.size?i.off("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize").on("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize",function(){return t.setMenuSize()}):this.options.size&&"auto"!=this.options.size&&this.selectpicker.current.elements.length>this.options.size&&i.off("resize"+U+"."+this.selectId+".setMenuSize scroll"+U+"."+this.selectId+".setMenuSize"),t.createView(!1,!0,e)}},setWidth:function(){var i=this;"auto"===this.options.width?requestAnimationFrame(function(){i.$menu.css("min-width","0"),i.$element.on("loaded"+U,function(){i.liHeight(),i.setMenuSize();var e=i.$newElement.clone().appendTo("body"),t=e.css("width","auto").children("button").outerWidth();e.remove(),i.sizeInfo.selectWidth=Math.max(i.sizeInfo.totalMenuWidth,t),i.$newElement.css("width",i.sizeInfo.selectWidth+"px")})}):"fit"===this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width","").addClass("fit-width")):this.options.width?(this.$menu.css("min-width",""),this.$newElement.css("width",this.options.width)):(this.$menu.css("min-width",""),this.$newElement.css("width","")),this.$newElement.hasClass("fit-width")&&"fit"!==this.options.width&&this.$newElement[0].classList.remove("fit-width")},selectPosition:function(){this.$bsContainer=z('
    ');var s,n,o,r=this,l=z(this.options.container),e=function(e){var t={},i=r.options.display||!!z.fn.dropdown.Constructor.Default&&z.fn.dropdown.Constructor.Default.display;r.$bsContainer.addClass(e.attr("class").replace(/form-control|fit-width/gi,"")).toggleClass(j.DROPUP,e.hasClass(j.DROPUP)),s=e.offset(),l.is("body")?n={top:0,left:0}:((n=l.offset()).top+=parseInt(l.css("borderTopWidth"))-l.scrollTop(),n.left+=parseInt(l.css("borderLeftWidth"))-l.scrollLeft()),o=e.hasClass(j.DROPUP)?0:e[0].offsetHeight,(M.major<4||"static"===i)&&(t.top=s.top-n.top+o,t.left=s.left-n.left),t.width=e[0].offsetWidth,r.$bsContainer.css(t)};this.$button.on("click.bs.dropdown.data-api",function(){r.isDisabled()||(e(r.$newElement),r.$bsContainer.appendTo(r.options.container).toggleClass(j.SHOW,!r.$button.hasClass(j.SHOW)).append(r.$menu))}),z(window).off("resize"+U+"."+this.selectId+" scroll"+U+"."+this.selectId).on("resize"+U+"."+this.selectId+" scroll"+U+"."+this.selectId,function(){r.$newElement.hasClass(j.SHOW)&&e(r.$newElement)}),this.$element.on("hide"+U,function(){r.$menu.data("height",r.$menu.height()),r.$bsContainer.detach()})},setOptionStatus:function(e){var t=this;if(t.noScroll=!1,t.selectpicker.view.visibleElements&&t.selectpicker.view.visibleElements.length)for(var i=0;i
    ');$[2]&&(y=y.replace("{var}",$[2][1"+y+"
    ")),d=!1,C.$element.trigger("maxReached"+U)),b&&I&&(E.append(z("
    "+S+"
    ")),d=!1,C.$element.trigger("maxReachedGrp"+U)),setTimeout(function(){C.setSelected(r,!1)},10),E.delay(750).fadeOut(300,function(){z(this).remove()})}}}else c.selected=!1,p.selected=!0,C.setSelected(r,!0);!C.multiple||C.multiple&&1===C.options.maxOptions?C.$button.trigger("focus"):C.options.liveSearch&&C.$searchbox.trigger("focus"),d&&(C.multiple||a!==s.selectedIndex)&&(T=[p.index,u.prop("selected"),l],C.$element.triggerNative("change"))}}),this.$menu.on("click","li."+j.DISABLED+" a, ."+j.POPOVERHEADER+", ."+j.POPOVERHEADER+" :not(.close)",function(e){e.currentTarget==this&&(e.preventDefault(),e.stopPropagation(),C.options.liveSearch&&!z(e.target).hasClass("close")?C.$searchbox.trigger("focus"):C.$button.trigger("focus"))}),this.$menuInner.on("click",".divider, .dropdown-header",function(e){e.preventDefault(),e.stopPropagation(),C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus")}),this.$menu.on("click","."+j.POPOVERHEADER+" .close",function(){C.$button.trigger("click")}),this.$searchbox.on("click",function(e){e.stopPropagation()}),this.$menu.on("click",".actions-btn",function(e){C.options.liveSearch?C.$searchbox.trigger("focus"):C.$button.trigger("focus"),e.preventDefault(),e.stopPropagation(),z(this).hasClass("bs-select-all")?C.selectAll():C.deselectAll()}),this.$element.on("change"+U,function(){C.render(),C.$element.trigger("changed"+U,T),T=null}).on("focus"+U,function(){C.options.mobile||C.$button.trigger("focus")})},liveSearchListener:function(){var u=this,f=document.createElement("li");this.$button.on("click.bs.dropdown.data-api",function(){u.$searchbox.val()&&u.$searchbox.val("")}),this.$searchbox.on("click.bs.dropdown.data-api focus.bs.dropdown.data-api touchend.bs.dropdown.data-api",function(e){e.stopPropagation()}),this.$searchbox.on("input propertychange",function(){var e=u.$searchbox.val();if(u.selectpicker.search.elements=[],u.selectpicker.search.data=[],e){var t=[],i=e.toUpperCase(),s={},n=[],o=u._searchStyle(),r=u.options.liveSearchNormalize;r&&(i=w(i)),u._$lisSelected=u.$menuInner.find(".selected");for(var l=0;l=a.selectpicker.view.canHighlight.length&&(t=0),a.selectpicker.view.canHighlight[t+f]||(t=t+1+a.selectpicker.view.canHighlight.slice(t+f+1).indexOf(!0))),e.preventDefault();var m=f+t;e.which===B?0===f&&t===c.length-1?(a.$menuInner[0].scrollTop=a.$menuInner[0].scrollHeight,m=a.selectpicker.current.elements.length-1):d=(o=(n=a.selectpicker.current.data[m]).position-n.height)u+a.sizeInfo.menuInnerHeight),s=a.selectpicker.main.elements[v],a.activeIndex=b[x],a.focusItem(s),s&&s.firstChild.focus(),d&&(a.$menuInner[0].scrollTop=o),r.trigger("focus")}}i&&(e.which===D&&!a.selectpicker.keydown.keyHistory||e.which===N||e.which===H&&a.options.selectOnTab)&&(e.which!==D&&e.preventDefault(),a.options.liveSearch&&e.which===D||(a.$menuInner.find(".active a").trigger("click",!0),r.trigger("focus"),a.options.liveSearch||(e.preventDefault(),z(document).data("spaceSelect",!0))))}},mobile:function(){this.$element[0].classList.add("mobile-device")},refresh:function(){var e=z.extend({},this.options,this.$element.data());this.options=e,this.checkDisabled(),this.setStyle(),this.render(),this.createLi(),this.setWidth(),this.setSize(!0),this.$element.trigger("refreshed"+U)},hide:function(){this.$newElement.hide()},show:function(){this.$newElement.show()},remove:function(){this.$newElement.remove(),this.$element.remove()},destroy:function(){this.$newElement.before(this.$element).remove(),this.$bsContainer?this.$bsContainer.remove():this.$menu.remove(),this.$element.off(U).removeData("selectpicker").removeClass("bs-select-hidden selectpicker"),z(window).off(U+"."+this.selectId)}};var X=z.fn.selectpicker;z.fn.selectpicker=Q,z.fn.selectpicker.Constructor=J,z.fn.selectpicker.noConflict=function(){return z.fn.selectpicker=X,this},z(document).off("keydown.bs.dropdown.data-api").on("keydown"+U,'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',J.prototype.keydown).on("focusin.modal",'.bootstrap-select [data-toggle="dropdown"], .bootstrap-select [role="listbox"], .bootstrap-select .bs-searchbox input',function(e){e.stopPropagation()}),z(window).on("load"+U+".data-api",function(){z(".selectpicker").each(function(){var e=z(this);Q.call(e,e.data())})})}(e)}); diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.css new file mode 100644 index 000000000..e6ff6bf6e --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.css @@ -0,0 +1,6 @@ +/** + * @author zhixin wen + * version: 1.18.3 + * https://github.com/wenzhixin/bootstrap-table/ + */ +.bootstrap-table .fixed-table-toolbar::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-toolbar .bs-bars,.bootstrap-table .fixed-table-toolbar .columns,.bootstrap-table .fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group>.btn{border-radius:0}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu{text-align:left;max-height:300px;overflow:auto;-ms-overflow-style:scrollbar;z-index:1001}.bootstrap-table .fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.bootstrap-table .fixed-table-toolbar .columns-left{margin-right:5px}.bootstrap-table .fixed-table-toolbar .columns-right{margin-left:5px}.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu{right:0;left:auto}.bootstrap-table .fixed-table-container{position:relative;clear:both}.bootstrap-table .fixed-table-container .table{width:100%;margin-bottom:0!important}.bootstrap-table .fixed-table-container .table td,.bootstrap-table .fixed-table-container .table th{vertical-align:middle;box-sizing:border-box}.bootstrap-table .fixed-table-container .table thead th{vertical-align:bottom;padding:0;margin:0}.bootstrap-table .fixed-table-container .table thead th:focus{outline:0 solid transparent}.bootstrap-table .fixed-table-container .table thead th.detail{width:30px}.bootstrap-table .fixed-table-container .table thead th .th-inner{padding:.75rem;vertical-align:bottom;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.bootstrap-table .fixed-table-container .table thead th .sortable{cursor:pointer;background-position:right;background-repeat:no-repeat;padding-right:30px!important}.bootstrap-table .fixed-table-container .table thead th .both{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC")}.bootstrap-table .fixed-table-container .table thead th .asc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==")}.bootstrap-table .fixed-table-container .table thead th .desc{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ")}.bootstrap-table .fixed-table-container .table tbody tr.selected td{background-color:rgba(0,0,0,.075)}.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td{text-align:center}.bootstrap-table .fixed-table-container .table tbody tr .card-view{display:flex}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title{font-weight:700;display:inline-block;min-width:30%;width:auto!important;text-align:left!important}.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value{width:100%!important}.bootstrap-table .fixed-table-container .table .bs-checkbox{text-align:center}.bootstrap-table .fixed-table-container .table .bs-checkbox label{margin-bottom:0}.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=checkbox],.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type=radio]{margin:0 auto!important}.bootstrap-table .fixed-table-container .table.table-sm .th-inner{padding:.3rem}.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer){border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height.has-card-view{border-top:1px solid #dee2e6;border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border{border-left:1px solid #dee2e6;border-right:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table thead th{border-bottom:1px solid #dee2e6}.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th{border-bottom:1px solid #32383e}.bootstrap-table .fixed-table-container .fixed-table-header{overflow:hidden}.bootstrap-table .fixed-table-container .fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading{align-items:center;background:#fff;display:flex;justify-content:center;position:absolute;bottom:0;width:100%;z-index:1000;transition:visibility 0s,opacity .15s ease-in-out;opacity:0;visibility:hidden}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open{visibility:visible;opacity:1}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap{align-items:baseline;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text{margin-right:6px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap{align-items:center;display:flex;justify-content:center}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before{content:"";animation-duration:1.5s;animation-iteration-count:infinite;animation-name:LOADING;background:#212529;border-radius:50%;display:block;height:5px;margin:0 4px;opacity:0;width:5px}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot{animation-delay:.3s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after{animation-delay:.6s}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark{background:#212529}.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before{background:#fff}.bootstrap-table .fixed-table-container .fixed-table-footer{overflow:hidden}.bootstrap-table .fixed-table-pagination::after{content:"";display:block;clear:both}.bootstrap-table .fixed-table-pagination>.pagination,.bootstrap-table .fixed-table-pagination>.pagination-detail{margin-top:10px;margin-bottom:10px}.bootstrap-table .fixed-table-pagination>.pagination-detail .pagination-info{line-height:34px;margin-right:5px}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list{display:inline-block}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group{position:relative;display:inline-block;vertical-align:middle}.bootstrap-table .fixed-table-pagination>.pagination-detail .page-list .btn-group .dropdown-menu{margin-bottom:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination{margin:0}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a{color:#c8c8c8}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::before{content:'\2B05'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.page-intermediate a::after{content:'\27A1'}.bootstrap-table .fixed-table-pagination>.pagination ul.pagination li.disabled a{pointer-events:none;cursor:default}.bootstrap-table.fullscreen{position:fixed;top:0;left:0;z-index:1050;width:100%!important;background:#fff;height:calc(100vh);overflow-y:scroll}.bootstrap-table.bootstrap4 .pagination-lg .page-link,.bootstrap-table.bootstrap5 .pagination-lg .page-link{padding:.5rem 1rem}.bootstrap-table.bootstrap5 .float-left{float:left}.bootstrap-table.bootstrap5 .float-right{float:right}div.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden}@keyframes LOADING{0%{opacity:0}50%{opacity:1}to{opacity:0}} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.js new file mode 100644 index 000000000..89c3b450d --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/bootstrap-table.min.js @@ -0,0 +1,6 @@ +/** + * @author zhixin wen + * version: 1.18.3 + * https://github.com/wenzhixin/bootstrap-table/ + */ +function getRememberRowIds(a,b){return $.isArray(a)?props=$.map(a,function(c){return c[b]}):props=[a[b]],props}function addRememberRow(b,c){var a=null==table.options.uniqueId?table.options.columns[1].field:table.options.uniqueId,d=getRememberRowIds(b,a);-1==$.inArray(c[a],d)&&(b[b.length]=c)}function removeRememberRow(b,c){var a=null==table.options.uniqueId?table.options.columns[1].field:table.options.uniqueId,f=getRememberRowIds(b,a),d=$.inArray(c[a],f);-1!=d&&b.splice(d,1)}!function(a,b){"object"==typeof exports&&"undefined"!=typeof module?module.exports=b(require("jquery")):"function"==typeof define&&define.amd?define(["jquery"],b):(a="undefined"!=typeof globalThis?globalThis:a||self,a.BootstrapTable=b(a.jQuery))}(this,function(fl){function fK(a){return a&&"object"==typeof a&&"default" in a?a:{"default":a}}function fD(a){return(fD="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(b){return typeof b}:function(b){return b&&"function"==typeof Symbol&&b.constructor===Symbol&&b!==Symbol.prototype?"symbol":typeof b})(a)}function fw(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function fu(b,c){for(var a=0;ab.length)&&(c=b.length);for(var a=0,d=Array(c);c>a;a++){d[a]=b[a]}return d}function fr(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function fG(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function fg(d,h){var c;if("undefined"==typeof Symbol||null==d[Symbol.iterator]){if(Array.isArray(d)||(c=fL(d))||h&&d&&"number"==typeof d.length){c&&(d=c);var k=0,j=function(){};return{s:j,n:function(){return k>=d.length?{done:!0}:{done:!1,value:d[k++]}},e:function(a){throw a},f:j}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var b,f=!0,g=!1;return{s:function(){c=d[Symbol.iterator]()},n:function(){var a=c.next();return f=a.done,a},e:function(a){g=!0,b=a},f:function(){try{f||null==c["return"]||c["return"]()}finally{if(g){throw b}}}}}function fO(a,b){return b={exports:{}},a(b,b.exports),b.exports}function fx(a,b){return RegExp(a,b)}var fc=fK(fl),ff="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},f2=function(a){return a&&a.Math==Math&&a},fd=f2("object"==typeof globalThis&&globalThis)||f2("object"==typeof window&&window)||f2("object"==typeof self&&self)||f2("object"==typeof ff&&ff)||function(){return this}()||Function("return this")(),fA=function(a){try{return !!a()}catch(b){return !0}},f8=!fA(function(){return 7!=Object.defineProperty({},1,{get:function(){return 7}})[1]}),f1={}.propertyIsEnumerable,gw=Object.getOwnPropertyDescriptor,f6=gw&&!f1.call({1:2},1),gk=f6?function(a){var b=gw(this,a);return !!b&&b.enumerable}:f1,gz={f:gk},gS=function(a,b){return{enumerable:!(1&a),configurable:!(2&a),writable:!(4&a),value:b}},f3={}.toString,gs=function(a){return f3.call(a).slice(8,-1)},fB="".split,fR=fA(function(){return !Object("z").propertyIsEnumerable(0)})?function(a){return"String"==gs(a)?fB.call(a,""):Object(a)}:Object,f9=function(a){if(void 0==a){throw TypeError("Can't call method on "+a)}return a},gr=function(a){return fR(f9(a))},gu=function(a){return"object"==typeof a?null!==a:"function"==typeof a},fY=function(b,c){if(!gu(b)){return b}var a,d;if(c&&"function"==typeof(a=b.toString)&&!gu(d=a.call(b))){return d}if("function"==typeof(a=b.valueOf)&&!gu(d=a.call(b))){return d}if(!c&&"function"==typeof(a=b.toString)&&!gu(d=a.call(b))){return d}throw TypeError("Can't convert object to primitive value")},gy={}.hasOwnProperty,gd=function(a,b){return gy.call(a,b)},gl=fd.document,gc=gu(gl)&&gu(gl.createElement),f0=function(a){return gc?gl.createElement(a):{}},e9=!f8&&!fA(function(){return 7!=Object.defineProperty(f0("div"),"a",{get:function(){return 7}}).a}),fq=Object.getOwnPropertyDescriptor,fX=f8?fq:function(b,c){if(b=gr(b),c=fY(c,!0),e9){try{return fq(b,c)}catch(a){}}return gd(b,c)?gS(!gz.f.call(b,c),b[c]):void 0},gp={f:fX},gf=function(a){if(!gu(a)){throw TypeError(a+" is not an object")}return a},fV=Object.defineProperty,fW=f8?fV:function(b,c,a){if(gf(b),c=fY(c,!0),gf(a),e9){try{return fV(b,c,a)}catch(d){}}if("get" in a||"set" in a){throw TypeError("Accessors not supported")}return"value" in a&&(b[c]=a.value),b},gh={f:fW},f4=f8?function(b,c,a){return gh.f(b,c,gS(1,a))}:function(b,c,a){return b[c]=a,b},fU=function(b,c){try{f4(fd,b,c)}catch(a){fd[b]=c}return c},a8="__core-js_shared__",eM=fd[a8]||fU(a8,{}),dS=eM,cJ=Function.toString;"function"!=typeof dS.inspectSource&&(dS.inspectSource=function(a){return cJ.call(a)});var cr,gF,bq,bK=dS.inspectSource,dc=fd.WeakMap,fj="function"==typeof dc&&/native code/.test(bK(dc)),d4=fO(function(a){(a.exports=function(b,c){return dS[b]||(dS[b]=void 0!==c?c:{})})("versions",[]).push({version:"3.9.1",mode:"global",copyright:"© 2021 Denis Pushkarev (zloirock.ru)"})}),aW=0,eZ=Math.random(),eA=function(a){return"Symbol("+((void 0===a?"":a)+"")+")_"+(++aW+eZ).toString(36)},cb=d4("keys"),ek=function(a){return cb[a]||(cb[a]=eA(a))},aK={},fZ=fd.WeakMap,cZ=function(a){return bq(a)?gF(a):cr(a,{})},gV=function(a){return function(c){var b;if(!gu(c)||(b=gF(c)).type!==a){throw TypeError("Incompatible receiver, "+a+" required")}return b}};if(fj){var ay=dS.state||(dS.state=new fZ),dh=ay.get,g7=ay.has,du=ay.set;cr=function(a,b){return b.facade=a,du.call(ay,a,b),b},gF=function(a){return dh.call(ay,a)||{}},bq=function(a){return g7.call(ay,a)}}else{var d8=ek("state");aK[d8]=!0,cr=function(a,b){return b.facade=a,f4(a,d8,b),b},gF=function(a){return gd(a,d8)?a[d8]:{}},bq=function(a){return gd(a,d8)}}var c2={set:cr,get:gF,has:bq,enforce:cZ,getterFor:gV},a2=fO(function(b){var c=c2.get,a=c2.enforce,d=(String+"").split("String");(b.exports=function(h,k,m,g){var i,j=g?!!g.unsafe:!1,f=g?!!g.enumerable:!1,n=g?!!g.noTargetGet:!1;return"function"==typeof m&&("string"!=typeof k||gd(m,"name")||f4(m,"name",k),i=a(m),i.source||(i.source=d.join("string"==typeof k?k:""))),h===fd?void (f?h[k]=m:fU(k,m)):(j?!n&&h[k]&&(f=!0):delete h[k],void (f?h[k]=m:f4(h,k,m)))})(Function.prototype,"toString",function(){return"function"==typeof this&&c(this).source||bK(this)})}),dV=fd,gb=function(a){return"function"==typeof a?a:void 0},bB=function(a,b){return arguments.length<2?gb(dV[a])||gb(fd[a]):dV[a]&&dV[a][b]||fd[a]&&fd[a][b]},cF=Math.ceil,dx=Math.floor,aE=function(a){return isNaN(a=+a)?0:(a>0?dx:cF)(a)},dG=Math.min,af=function(a){return a>0?dG(aE(a),9007199254740991):0},ep=Math.max,al=Math.min,aQ=function(b,c){var a=aE(b);return 0>a?ep(a+c,0):al(a,c)},cx=function(a){return function(g,c,j){var h,b=gr(g),d=af(b.length),f=aQ(j,d);if(a&&c!=c){for(;d>f;){if(h=b[f++],h!=h){return !0}}}else{for(;d>f;f++){if((a||f in b)&&b[f]===c){return a||f||0}}}return !a&&-1}},bh={includes:cx(!0),indexOf:cx(!1)},eQ=bh.indexOf,gL=function(d,f){var c,h=gr(d),g=0,b=[];for(c in h){!gd(aK,c)&&gd(h,c)&&b.push(c)}for(;f.length>g;){gd(h,c=f[g++])&&(~eQ(b,c)||b.push(c))}return b},eD=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],cP=eD.concat("length","prototype"),gB=Object.getOwnPropertyNames||function(a){return gL(a,cP)},bY={f:gB},cf=Object.getOwnPropertySymbols,g1={f:cf},e1=bB("Reflect","ownKeys")||function(b){var c=bY.f(gf(b)),a=g1.f;return a?c.concat(a(b)):c},bP=function(d,g){for(var c=e1(g),j=gh.f,h=gp.f,b=0;b0&&(!q.multiline||q.multiline&&"\n"!==u[q.lastIndex-1])&&(g="(?: "+g+")",k=" "+k,p++),j=RegExp("^(?:"+g+")",b)),d2&&(j=RegExp("^"+g+"$(?!\\s)",b)),c9&&(m=q.lastIndex),f=ak.call(v?j:q,k),v?f?(f.input=f.input.slice(p),f[0]=f[0].slice(p),f.index=q.lastIndex,q.lastIndex+=f[0].length):q.lastIndex=0:c9&&f&&(q.lastIndex=q.global?f.index+f[0].length:m),d2&&f&&f.length>1&&dB.call(f[0],j,function(){for(d=1;d=74)&&(cO=dN.match(/Chrome\/(\d+)/),cO&&(dE=cO[1])));var aV=dE&&+dE,cE=!!Object.getOwnPropertySymbols&&!fA(function(){return !Symbol.sham&&(aJ?38===aV:aV>37&&41>aV)}),bp=cE&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,eX=d4("wks"),gU=fd.Symbol,eK=bp?gU:gU&&gU.withoutSetter||eA,cX=function(a){return(!gd(eX,a)||!cE&&"string"!=typeof eX[a])&&(cE&&gd(gU,a)?eX[a]=gU[a]:eX[a]=eK("Symbol."+a)),eX[a]},gK=cX("species"),b5=!fA(function(){var a=/./;return a.exec=function(){var b=[];return b.groups={a:"7"},b},"7"!=="".replace(a,"$")}),cp=function(){return"$0"==="a".replace(/./,"$0")}(),g6=cX("replace"),e8=function(){return/./[g6]?""===/./[g6]("a","$0"):!1}(),bW=!fA(function(){var b=/(?:)/,c=b.exec;b.exec=function(){return c.apply(this,arguments)};var a="ab".split(b);return 2!==a.length||"a"!==a[0]||"b"!==a[1]}),b8=function(u,m,j,f){var d=cX(u),q=!fA(function(){var a={};return a[d]=function(){return 7},7!=""[u](a)}),v=q&&!fA(function(){var c=!1,a=/a/;return"split"===u&&(a={},a.constructor={},a.constructor[gK]=function(){return a},a.flags="",a[d]=/./[d]),a.exec=function(){return c=!0,null},a[d](""),!c});if(!q||!v||"replace"===u&&(!b5||!cp||e8)||"split"===u&&!bW){var b=/./[d],g=j(d,""[u],function(c,h,a,r,l){return h.exec===bJ?q&&!l?{done:!0,value:b.call(h,a,r)}:{done:!0,value:c.call(a,h,r)}:{done:!1}},{REPLACE_KEEPS_$0:cp,REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE:e8}),p=g[0],k=g[1];a2(String.prototype,u,p),a2(RegExp.prototype,d,2==m?function(a,c){return k.call(a,this,c)}:function(a){return k.call(a,this)})}f&&f4(RegExp.prototype[d],"sham",!0)},fS=cX("match"),dQ=function(a){var b;return gu(a)&&(void 0!==(b=a[fS])?!!b:"RegExp"==gs(a))},bG=function(a){if("function"!=typeof a){throw TypeError(a+" is not a function")}return a},bf=cX("species"),eR=function(b,c){var a,d=gf(b).constructor;return void 0===d||void 0==(a=gf(d)[bf])?c:bG(a)},dW=function(a){return function(g,c){var j,h,b=f9(g)+"",d=aE(c),f=b.length;return 0>d||d>=f?a?"":void 0:(j=b.charCodeAt(d),55296>j||j>56319||d+1===f||(h=b.charCodeAt(d+1))<56320||h>57343?a?b.charAt(d):j:a?b.slice(d,d+2):(j-55296<<10)+(h-56320)+65536)}},cQ={codeAt:dW(!1),charAt:dW(!0)},cy=cQ.charAt,gN=function(b,c,a){return c+(a?cy(b,c).length:1)},bx=function(b,c){var a=b.exec;if("function"==typeof a){var d=a.call(b,c);if("object"!=typeof d){throw TypeError("RegExp exec method returned something other than an Object or null")}return d}if("RegExp"!==gs(b)){throw TypeError("RegExp#exec called on incompatible receiver")}return bJ.call(b,c)},bQ=[].push,dj=Math.min,fC=4294967295,d9=!fA(function(){return !RegExp(fC,"y")});b8("split",2,function(b,c,a){var d;return d="c"=="abbc".split(/(b)*/)[1]||4!="test".split(/(?:)/,-1).length||2!="ab".split(/(?:ab)*/).length||4!=".".split(/(.?)(.?)/).length||".".split(/()()/).length>1||"".split(/.?/).length?function(w,k){var g=f9(this)+"",f=void 0===k?fC:k>>>0;if(0===f){return[]}if(void 0===w){return[g]}if(!dQ(w)){return c.call(g,w,f)}for(var q,x,e,j=[],p=(w.ignoreCase?"i":"")+(w.multiline?"m":"")+(w.unicode?"u":"")+(w.sticky?"y":""),m=0,v=RegExp(w.source,p+"g");(q=bJ.call(v,g))&&(x=v.lastIndex,!(x>m&&(j.push(g.slice(m,q.index)),q.length>1&&q.index=f)));){v.lastIndex===q.index&&v.lastIndex++}return m===g.length?(e||!v.test(""))&&j.push(""):j.push(g.slice(m)),j.length>f?j.slice(0,f):j}:"0".split(void 0,0).length?function(f,e){return void 0===f&&0===e?[]:c.call(this,f,e)}:c,[function(h,g){var j=f9(this),f=void 0==h?void 0:h[b];return void 0!==f?f.call(h,j,g):d.call(j+"",h,g)},function(E,j){var B=a(d,E,this,j,d!==c);if(B.done){return B.value}var F=gf(E),e=this+"",n=eR(F,RegExp),z=F.unicode,q=(F.ignoreCase?"i":"")+(F.multiline?"m":"")+(F.unicode?"u":"")+(d9?"y":"g"),D=new n(d9?F:"^(?:"+F.source+")",q),y=void 0===j?fC:j>>>0;if(0===y){return[]}if(0===e.length){return null===bx(D,e)?[e]:[]}for(var x=0,i=0,w=[];ib;){gh.f(d,c=h[b++],f[c])}return d},cg=bB("document","documentElement"),eq=">",aO="<",gg="prototype",c3="script",gZ=ek("IE_PROTO"),aC=function(){},dq=function(a){return aO+c3+eq+a+aO+"/"+c3+eq},ag=function(a){a.write(dq("")),a.close();var b=a.parentWindow.Object;return a=null,b},dy=function(){var b,c=f0("iframe"),a="java"+c3+":";return c.style.display="none",cg.appendChild(c),c.src=a+"",b=c.contentWindow.document,b.open(),b.write(dq("document.F=Object")),b.close(),b.F},eg=function(){try{a0=document.domain&&new ActiveXObject("htmlfile")}catch(a){}eg=a0?ag(a0):dy();for(var b=eD.length;b--;){delete eg[gg][eD[b]]}return eg()};aK[gZ]=!0;var c8=Object.create||function(b,c){var a;return null!==b?(aC[gg]=gf(b),a=new aC,aC[gg]=null,a[gZ]=b):a=eg(),void 0===c?a:eE(a,c)},a6=cX("unscopables"),d1=Array.prototype;void 0==d1[a6]&&gh.f(d1,a6,{configurable:!0,value:c8(null)});var gx=function(a){d1[a6][a]=!0},bI=bh.includes;cB({target:"Array",proto:!0},{includes:function(a){return bI(this,a,arguments.length>1?arguments[1]:void 0)}}),gx("includes");var cL=Array.isArray||function(a){return"Array"==gs(a)},dD=function(a){return Object(f9(a))},aI=function(b,c,a){var d=fY(c);d in b?gh.f(b,d,gS(0,a)):b[d]=a},dK=cX("species"),ap=function(b,c){var a;return cL(b)&&(a=b.constructor,"function"!=typeof a||a!==Array&&!cL(a.prototype)?gu(a)&&(a=a[dK],null===a&&(a=void 0)):a=void 0),new (void 0===a?Array:a)(0===c?0:c)},ex=cX("species"),aw=function(a){return aV>=51||!fA(function(){var c=[],b=c.constructor={};return b[ex]=function(){return{foo:1}},1!==c[a](Boolean).foo})},aU=cX("isConcatSpreadable"),cD=9007199254740991,bm="Maximum allowed index exceeded",eW=aV>=51||!fA(function(){var a=[];return a[aU]=!1,a.concat()[0]!==a}),gT=aw("concat"),eJ=function(a){if(!gu(a)){return !1}var b=a[aU];return void 0!==b?!!b:cL(a)},cW=!eW||!gT;cB({target:"Array",proto:!0,forced:cW},{concat:function(k){var h,g,d,c,j,m=dD(this),b=ap(m,0),f=0;for(h=-1,d=arguments.length;d>h;h++){if(j=-1===h?m:arguments[h],eJ(j)){if(c=af(j.length),f+c>cD){throw TypeError(bm)}for(g=0;c>g;g++,f++){g in j&&aI(b,f,j[g])}}else{if(f>=cD){throw TypeError(bm)}aI(b,f++,j)}}return b.length=f,b}});var gH=function(b,c,a){if(bG(b),void 0===c){return b}switch(a){case 0:return function(){return b.call(c)};case 1:return function(d){return b.call(c,d)};case 2:return function(d,e){return b.call(c,d,e)};case 3:return function(d,f,e){return b.call(c,d,f,e)}}return function(){return b.apply(c,arguments)}},b2=[].push,cm=function(d){var h=1==d,c=2==d,k=3==d,j=4==d,b=6==d,f=7==d,g=5==d||b;return function(i,s,n,B){for(var r,q,a=dD(i),o=fR(a),A=gH(s,n,3),x=af(o.length),e=0,t=B||ap,z=h?t(i,x):c||f?t(i,0):void 0;x>e;e++){if((g||e in o)&&(r=o[e],q=A(r,e,a),d)){if(h){z[e]=q}else{if(q){switch(d){case 3:return !0;case 5:return r;case 6:return e;case 2:b2.call(z,r)}}else{switch(d){case 4:return !1;case 7:b2.call(z,r)}}}}}return b?-1:k||j?j:z}},g5={forEach:cm(0),map:cm(1),filter:cm(2),some:cm(3),every:cm(4),find:cm(5),findIndex:cm(6),filterOut:cm(7)},e7=g5.find,bV="find",b7=!0;bV in []&&Array(1)[bV](function(){b7=!1}),cB({target:"Array",proto:!0,forced:b7},{find:function(a){return e7(this,a,arguments.length>1?arguments[1]:void 0)}}),gx(bV);var fP=function(a){if(dQ(a)){throw TypeError("The method doesn't accept regular expressions")}return a},dP=cX("match"),bD=function(b){var c=/./;try{"/./"[b](c)}catch(a){try{return c[dP]=!1,"/./"[b](c)}catch(d){}}return !1};cB({target:"String",proto:!0,forced:!bD("includes")},{includes:function(a){return !!~(f9(this)+"").indexOf(fP(a),arguments.length>1?arguments[1]:void 0)}});var bd={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0},eP=g5.forEach,cN=ck("forEach"),cw=cN?[].forEach:function(a){return eP(this,a,arguments.length>1?arguments[1]:void 0)};for(var gJ in bd){var bv=fd[gJ],bO=bv&&bv.prototype;if(bO&&bO.forEach!==cw){try{f4(bO,"forEach",cw)}catch(dg){bO.forEach=cw}}}var fv=ed.trim,d7=fd.parseFloat,aZ=1/d7(gQ+"-0")!==-(1/0),e0=aZ?function(b){var c=fv(b+""),a=d7(c);return 0===a&&"-"==c.charAt(0)?-0:a}:d7;cB({global:!0,forced:parseFloat!=e0},{parseFloat:e0});var eC=gz.f,cd=function(a){return function(g){for(var c,j=gr(g),h=e2(j),b=h.length,d=0,f=[];b>d;){c=h[d++],(!f8||eC.call(j,c))&&f.push(a?[c,j[c]]:j[c])}return f}},em={entries:cd(!0),values:cd(!1)},aN=em.entries;cB({target:"Object",stat:!0},{entries:function(a){return aN(a)}});var f7=bh.indexOf,c1=[].indexOf,gY=!!c1&&1/[1].indexOf(1,-0)<0,aB=ck("indexOf");cB({target:"Array",proto:!0,forced:gY||!aB},{indexOf:function(a){return gY?c1.apply(this,arguments)||0:f7(this,a,arguments.length>1?arguments[1]:void 0)}});var dl=[],ad=dl.sort,dw=fA(function(){dl.sort(void 0)}),ec=fA(function(){dl.sort(null)}),c5=ck("sort"),a5=dw||!ec||!c5;cB({target:"Array",proto:!0,forced:a5},{sort:function(a){return void 0===a?ad.call(dD(this)):ad.call(dD(this),bG(a))}});var dY=Math.floor,gm="".replace,bF=/\$([$&'`]|\d{1,2}|<[^>]*>)/g,cI=/\$([$&'`]|\d{1,2})/g,dA=function(k,h,g,d,c,j){var m=g+k.length,b=d.length,f=cI;return void 0!==c&&(c=dD(c),f=bF),gm.call(j,f,function(i,e){var p;switch(e.charAt(0)){case"$":return"$";case"&":return k;case"`":return h.slice(0,g);case"'":return h.slice(m);case"<":p=c[e.slice(1,-1)];break;default:var o=+e;if(0===o){return i}if(o>b){var n=dY(o/10);return 0===n?i:b>=n?void 0===d[n-1]?e.charAt(1):d[n-1]+e.charAt(1):i}p=d[o-1]}return void 0===p?"":p})},aH=Math.max,dI=Math.min,aj=function(a){return void 0===a?a:a+""};b8("replace",2,function(d,g,c,j){var h=j.REGEXP_REPLACE_SUBSTITUTES_UNDEFINED_CAPTURE,b=j.REPLACE_KEEPS_$0,f=h?"$":"$0";return[function(k,m){var l=f9(this),e=void 0==k?void 0:k[d];return void 0!==e?e.call(k,l,m):g.call(l+"",k,m)},function(B,E){if(!h&&b||"string"==typeof E&&-1===E.indexOf(f)){var C=c(g,B,this,E);if(C.done){return C.value}}var G=gf(B),M=this+"",I="function"==typeof E;I||(E+="");var A=G.global;if(A){var L=G.unicode;G.lastIndex=0}for(var K=[];;){var D=bx(G,M);if(null===D){break}if(K.push(D),!A){break}var J=D[0]+"";""===J&&(G.lastIndex=gN(M,af(G.lastIndex),L))}for(var z="",N=0,F=0;F=N&&(z+=M.slice(N,s)+a,N=s+o.length)}return z+M.slice(N)}]});var eu=Object.assign,ar=Object.defineProperty,aT=!eu||fA(function(){if(f8&&1!==eu({b:1},eu(ar({},"a",{enumerable:!0,get:function(){ar(this,"b",{value:3,enumerable:!1})}}),{b:2})).b){return !0}var b={},c={},a=Symbol(),d="abcdefghijklmnopqrst";return b[a]=7,d.split("").forEach(function(e){c[e]=e}),7!=eu({},b)[a]||e2(eu({},c)).join("")!=d})?function(w,m){for(var j=dD(w),f=arguments.length,d=1,q=g1.f,x=gz.f;f>d;){for(var b,g=fR(arguments[d++]),p=q?e2(g).concat(q(g)):e2(g),k=p.length,v=0;k>v;){b=p[v++],(!f8||x.call(g,b))&&(j[b]=g[b])}}return j}:eu;cB({target:"Object",stat:!0,forced:Object.assign!==aT},{assign:aT});var cA=g5.filter,bl=aw("filter");cB({target:"Array",proto:!0,forced:!bl},{filter:function(a){return cA(this,a,arguments.length>1?arguments[1]:void 0)}});var eT=Object.is||function(a,b){return a===b?0!==a||1/a===1/b:a!=a&&b!=b};b8("search",1,function(b,c,a){return[function(f){var d=f9(this),g=void 0==f?void 0:f[b];return void 0!==g?g.call(f,d):RegExp(f)[b](d+"")},function(e){var i=a(c,e,this);if(i.done){return i.value}var h=gf(e),d=this+"",f=h.lastIndex;eT(f,0)||(h.lastIndex=0);var g=bx(h,d);return eT(h.lastIndex,f)||(h.lastIndex=f),null===g?-1:g.index}]});var gP=ed.trim,eG=fd.parseInt,cT=/^[+-]?0[Xx]/,gE=8!==eG(gQ+"08")||22!==eG(gQ+"0x16"),b0=gE?function(b,c){var a=gP(b+"");return eG(a,c>>>0||(cT.test(a)?16:10))}:eG;cB({global:!0,forced:parseInt!=b0},{parseInt:b0});var cj=g5.map,g4=aw("map");cB({target:"Array",proto:!0,forced:!g4},{map:function(a){return cj(this,a,arguments.length>1?arguments[1]:void 0)}});var e4=g5.findIndex,bS="findIndex",b4=!0;bS in []&&Array(1)[bS](function(){b4=!1}),cB({target:"Array",proto:!0,forced:b4},{findIndex:function(a){return e4(this,a,arguments.length>1?arguments[1]:void 0)}}),gx(bS);var fH=function(a){if(!gu(a)&&null!==a){throw TypeError("Can't set "+(a+"")+" as a prototype")}return a},dM=Object.setPrototypeOf||("__proto__" in {}?function(){var b,c=!1,a={};try{b=Object.getOwnPropertyDescriptor(Object.prototype,"__proto__").set,b.call(a,[]),c=a instanceof Array}catch(d){}return function(e,f){return gf(e),fH(f),c?b.call(e,f):e.__proto__=f,e}}():void 0),bz=function(b,c,a){var f,d;return dM&&"function"==typeof(f=c.constructor)&&f!==a&&gu(d=f.prototype)&&d!==a.prototype&&dM(b,d),b},bc=cX("species"),eO=function(b){var c=bB(b),a=gh.f;f8&&c&&!c[bc]&&a(c,bc,{configurable:!0,get:function(){return this}})},dU=gh.f,cM=bY.f,cv=c2.set,gI=cX("match"),bu=fd.RegExp,bN=bu.prototype,df=/a/g,fs=/a/g,d6=new bu(df)!==df,aY=dr.UNSUPPORTED_Y,eB=f8&&dZ("RegExp",!d6||aY||fA(function(){return fs[gI]=!1,bu(df)!=df||bu(fs)==fs||"/a/i"!=bu(df,"i")}));if(eB){for(var cc=function(d,g){var c,j=this instanceof cc,h=dQ(d),b=void 0===g;if(!j&&h&&d.constructor===cc&&b){return d}d6?h&&!b&&(d=d.source):d instanceof cc&&(b&&(g=c6.call(d)),d=d.source),aY&&(c=!!g&&g.indexOf("y")>-1,c&&(g=g.replace(/y/g,"")));var f=bz(d6?new bu(d,g):bu(d,g),j?this:bN,cc);return aY&&c&&cv(f,{sticky:c}),f},el=(function(a){a in cc||dU(cc,a,{configurable:!0,get:function(){return bu[a]},set:function(b){bu[a]=b}})}),aM=cM(bu),f5=0;aM.length>f5;){el(aM[f5++])}bN.constructor=cc,cc.prototype=bN,a2(fd,"RegExp",cc)}eO("RegExp");var c0="toString",gX=RegExp.prototype,aA=gX[c0],dk=fA(function(){return"/a/b"!=aA.call({source:"a",flags:"b"})}),ac=aA.name!=c0;(dk||ac)&&a2(RegExp.prototype,c0,function(){var b=gf(this),c=b.source+"",a=b.flags,d=(void 0===a&&b instanceof RegExp&&!("flags" in gX)?c6.call(b):a)+"";return"/"+c+"/"+d},{unsafe:!0});var dv=cX("toStringTag"),eb={};eb[dv]="z";var c4=eb+""=="[object z]",a4=cX("toStringTag"),dX="Arguments"==gs(function(){return arguments}()),gj=function(b,c){try{return b[c]}catch(a){}},bE=c4?gs:function(b){var c,a,d;return void 0===b?"Undefined":null===b?"Null":"string"==typeof(a=gj(c=Object(b),a4))?a:dX?gs(c):"Object"==(d=gs(c))&&"function"==typeof c.callee?"Arguments":d},cH=c4?{}.toString:function(){return"[object "+bE(this)+"]"};c4||a2(Object.prototype,"toString",cH,{unsafe:!0});var dz=aw("slice"),aG=cX("species"),dH=[].slice,ah=Math.max;cB({target:"Array",proto:!0,forced:!dz},{slice:function(k,h){var g,d,c,j=gr(this),m=af(j.length),b=aQ(k,m),f=aQ(void 0===h?m:h,m);if(cL(j)&&(g=j.constructor,"function"!=typeof g||g!==Array&&!cL(g.prototype)?gu(g)&&(g=g[aG],null===g&&(g=void 0)):g=void 0,g===Array||void 0===g)){return dH.call(j,b,f)}for(d=new (void 0===g?Array:g)(ah(f-b,0)),c=0;f>b;b++,c++){b in j&&aI(d,c,j[b])}return d.length=c,d}});var er,aq,aS,cz=!fA(function(){function a(){}return a.prototype.constructor=null,Object.getPrototypeOf(new a)!==a.prototype}),bk=ek("IE_PROTO"),eS=Object.prototype,gO=cz?Object.getPrototypeOf:function(a){return a=dD(a),gd(a,bk)?a[bk]:"function"==typeof a.constructor&&a instanceof a.constructor?a.constructor.prototype:a instanceof Object?eS:null},eF=cX("iterator"),cS=!1,gD=function(){return this};[].keys&&(aS=[].keys(),"next" in aS?(aq=gO(gO(aS)),aq!==Object.prototype&&(er=aq)):cS=!0);var bZ=void 0==er||fA(function(){var a={};return er[eF].call(a)!==a});bZ&&(er={}),gd(er,eF)||f4(er,eF,gD);var ch={IteratorPrototype:er,BUGGY_SAFARI_ITERATORS:cS},g3=gh.f,e3=cX("toStringTag"),bR=function(b,c,a){b&&!gd(b=a?b:b.prototype,e3)&&g3(b,e3,{configurable:!0,value:c})},b3=ch.IteratorPrototype,fE=function(b,c,a){var d=c+" Iterator";return b.prototype=c8(b3,{next:gS(1,a)}),bR(b,d,!1),b},dL=ch.IteratorPrototype,by=ch.BUGGY_SAFARI_ITERATORS,bj=cX("iterator"),eV="keys",d0="values",cV="entries",cC=function(){return this},gR=function(G,A,w,m,k,D,H){fE(w,A,m);var b,q,C,x=function(a){if(a===k&&y){return y}if(!by&&a in z){return z[a]}switch(a){case eV:return function(){return new w(this,a)};case d0:return function(){return new w(this,a)};case cV:return function(){return new w(this,a)}}return function(){return new w(this)}},F=A+" Iterator",B=!1,z=G.prototype,j=z[bj]||z["@@iterator"]||k&&z[k],y=!by&&j||x(k),E="Array"==A?z.entries||j:j;if(E&&(b=gO(E.call(new G)),dL!==Object.prototype&&b.next&&(gO(b)!==dL&&(dM?dM(b,dL):"function"!=typeof b[bj]&&f4(b,bj,cC)),bR(b,F,!0))),k==d0&&j&&j.name!==d0&&(B=!0,y=function(){return j.call(this)}),z[bj]!==y&&f4(z,bj,y),k){if(q={values:x(d0),keys:D?y:x(eV),entries:x(cV)},H){for(C in q){!by&&!B&&C in z||a2(z,C,q[C])}}else{cB({target:A,proto:!0,forced:by||B},q)}}return q},bC="Array Iterator",bU=c2.set,dp=c2.getterFor(bC),fN=gR(Array,"Array",function(a,b){bU(this,{type:bC,target:gr(a),index:0,kind:b})},function(){var b=dp(this),c=b.target,a=b.kind,d=b.index++;return !c||d>=c.length?(b.target=void 0,{value:void 0,done:!0}):"keys"==a?{value:d,done:!1}:"values"==a?{value:c[d],done:!1}:{value:[d,c[d]],done:!1}},"values");gx("keys"),gx("values"),gx("entries");var ef=cX("iterator"),a3=cX("toStringTag"),e6=fN.values;for(var eI in bd){var cl=fd[eI],ew=cl&&cl.prototype;if(ew){if(ew[ef]!==e6){try{f4(ew,ef,e6)}catch(dg){ew[ef]=e6}}if(ew[a3]||f4(ew,a3,eI),bd[eI]){for(var aR in fN){if(ew[aR]!==fN[aR]){try{f4(ew,aR,fN[aR])}catch(dg){ew[aR]=fN[aR]}}}}}}var gv=aw("splice"),c7=Math.max,g2=Math.min,aF=9007199254740991,ds="Maximum allowed length exceeded";cB({target:"Array",proto:!0,forced:!gv},{splice:function(w,m){var j,f,d,q,x,b,g=dD(this),p=af(g.length),k=aQ(w,p),v=arguments.length;if(0===v?j=f=0:1===v?(j=0,f=p-k):(j=v-2,f=g2(c7(aE(m),0),p-k)),p+j-f>aF){throw TypeError(ds)}for(d=ap(g,f),q=0;f>q;q++){x=k+q,x in g&&aI(d,q,g[x])}if(d.length=f,f>j){for(q=k;p-f>q;q++){x=q+f,b=q+j,x in g?g[b]=g[x]:delete g[b]}for(q=p;q>p-f+j;q--){delete g[q-1]}}else{if(j>f){for(q=p-f;q>k;q--){x=q+f-1,b=q+j-1,x in g?g[b]=g[x]:delete g[b]}}}for(q=0;j>q;q++){g[q+k]=arguments[q+2]}return g.length=p-f+j,d}});var am=bY.f,dC=gp.f,ej=gh.f,db=ed.trim,bb="Number",d3=fd[bb],gC=d3.prototype,bM=gs(c8(gC))==bb,cR=function(p){var j,h,f,d,m,q,b,g,k=fY(p,!1);if("string"==typeof k&&k.length>2){if(k=db(k),j=k.charCodeAt(0),43===j||45===j){if(h=k.charCodeAt(2),88===h||120===h){return NaN}}else{if(48===j){switch(k.charCodeAt(1)){case 66:case 98:f=2,d=49;break;case 79:case 111:f=8,d=55;break;default:return +k}for(m=k.slice(2),q=m.length,b=0;q>b;b++){if(g=m.charCodeAt(b),48>g||g>d){return NaN}}return parseInt(m,f)}}}return +k};if(dZ(bb,!d3(" 0o1")||!d3("0b1")||d3("+0x1"))){for(var dF,aL=function(b){var c=arguments.length<1?0:b,a=this;return a instanceof aL&&(bM?fA(function(){gC.valueOf.call(a)}):gs(a)!=bb)?bz(new d3(cR(c)),a,aL):cR(c)},dO=f8?am(d3):"MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger,fromString,range".split(","),av=0;dO.length>av;av++){gd(d3,dF=dO[av])&&!gd(aL,dF)&&ej(aL,dF,dC(d3,dF))}aL.prototype=gC,gC.constructor=aL,a2(fd,bb,aL)}var ez=[].reverse,az=[1,2];cB({target:"Array",proto:!0,forced:az+""==az.reverse()+""},{reverse:function(){return cL(this)&&(this.length=this.length),ez.call(this)}});var aX="1.18.3",cG=4;try{var bs=fc["default"].fn.dropdown.Constructor.VERSION;void 0!==bs&&(cG=parseInt(bs,10))}catch(eY){}try{var gW=bootstrap.Tooltip.VERSION;void 0!==gW&&(cG=parseInt(gW,10))}catch(eY){}var eL={3:{iconsPrefix:"glyphicon",icons:{paginationSwitchDown:"glyphicon-collapse-down icon-chevron-down",paginationSwitchUp:"glyphicon-collapse-up icon-chevron-up",refresh:"glyphicon-refresh icon-refresh",toggleOff:"glyphicon-list-alt icon-list-alt",toggleOn:"glyphicon-list-alt icon-list-alt",columns:"glyphicon-th icon-th",detailOpen:"glyphicon-plus icon-plus",detailClose:"glyphicon-minus icon-minus",fullscreen:"glyphicon-fullscreen",search:"glyphicon-search",clearSearch:"glyphicon-trash"},classes:{buttonsPrefix:"btn",buttons:"default",buttonsGroup:"btn-group",buttonsDropdown:"btn-group",pull:"pull",inputGroup:"input-group",inputPrefix:"input-",input:"form-control",paginationDropdown:"btn-group dropdown",dropup:"dropup",dropdownActive:"active",paginationActive:"active",buttonActive:"active"},html:{toolbarDropdown:['"],toolbarDropdownItem:'
  • ',toolbarDropdownSeparator:'
  • ',pageDropdown:['"],pageDropdownItem:'
    ',dropdownCaret:'',pagination:['
      ',"
    "],paginationItem:'
  • %s
  • ',icon:'',inputGroup:'
    %s%s
    ',searchInput:'',searchButton:'',searchClearButton:''}},4:{iconsPrefix:"fa",icons:{paginationSwitchDown:"fa-caret-square-down",paginationSwitchUp:"fa-caret-square-up",refresh:"fa-sync",toggleOff:"fa-toggle-off",toggleOn:"fa-toggle-on",columns:"fa-th-list",detailOpen:"fa-plus",detailClose:"fa-minus",fullscreen:"fa-arrows-alt",search:"fa-search",clearSearch:"fa-trash"},classes:{buttonsPrefix:"btn",buttons:"secondary",buttonsGroup:"btn-group",buttonsDropdown:"btn-group",pull:"float",inputGroup:"btn-group",inputPrefix:"form-control-",input:"form-control",paginationDropdown:"btn-group dropdown",dropup:"dropup",dropdownActive:"active",paginationActive:"active",buttonActive:"active"},html:{toolbarDropdown:['"],toolbarDropdownItem:'',pageDropdown:['"],pageDropdownItem:'%s',toolbarDropdownSeparator:'',dropdownCaret:'',pagination:['
      ',"
    "],paginationItem:'
  • %s
  • ',icon:'',inputGroup:'
    %s
    %s
    ',searchInput:'',searchButton:'',searchClearButton:''}},5:{iconsPrefix:"fa",icons:{paginationSwitchDown:"fa-caret-square-down",paginationSwitchUp:"fa-caret-square-up",refresh:"fa-sync",toggleOff:"fa-toggle-off",toggleOn:"fa-toggle-on",columns:"fa-th-list",detailOpen:"fa-plus",detailClose:"fa-minus",fullscreen:"fa-arrows-alt",search:"fa-search",clearSearch:"fa-trash"},classes:{buttonsPrefix:"btn",buttons:"secondary",buttonsGroup:"btn-group",buttonsDropdown:"btn-group",pull:"float",inputGroup:"btn-group",inputPrefix:"form-control-",input:"form-control",paginationDropdown:"btn-group dropdown",dropup:"dropup",dropdownActive:"active",paginationActive:"active",buttonActive:"active"},html:{dataToggle:"data-bs-toggle",toolbarDropdown:['"],toolbarDropdownItem:'',pageDropdown:['"],pageDropdownItem:'%s',toolbarDropdownSeparator:'',dropdownCaret:'',pagination:['
      ',"
    "],paginationItem:'
  • %s
  • ',icon:'',inputGroup:'
    %s
    %s
    ',searchInput:'',searchButton:'',searchClearButton:''}}}[cG],cY={id:void 0,firstLoad:!0,height:void 0,classes:"table table-bordered table-hover",buttons:{},theadClasses:"",striped:!1,headerStyle:function(a){return{}},rowStyle:function(a,b){return{}},rowAttributes:function(a,b){return{}},undefinedText:"-",locale:void 0,virtualScroll:!1,virtualScrollItemHeight:void 0,sortable:!0,sortClass:void 0,silentSort:!0,sortName:void 0,sortOrder:void 0,sortReset:!1,sortStable:!1,rememberOrder:!1,serverSort:!0,customSort:void 0,columns:[[]],data:[],url:void 0,method:"get",cache:!0,contentType:"application/json",dataType:"json",ajax:void 0,ajaxOptions:{},queryParams:function(a){return a},queryParamsType:"limit",responseHandler:function(a){return a},totalField:"total",totalNotFilteredField:"totalNotFiltered",dataField:"rows",footerField:"footer",pagination:!1,paginationParts:["pageInfo","pageSize","pageList"],showExtendedPagination:!1,paginationLoop:!0,sidePagination:"client",totalRows:0,totalNotFiltered:0,pageNumber:1,pageSize:10,pageList:[10,25,50,100],paginationHAlign:"right",paginationVAlign:"bottom",paginationDetailHAlign:"left",paginationPreText:"‹",paginationNextText:"›",paginationSuccessivelySize:5,paginationPagesBySide:1,paginationUseIntermediate:!1,search:!1,searchHighlight:!1,searchOnEnterKey:!1,strictSearch:!1,searchSelector:!1,visibleSearch:!1,showButtonIcons:!0,showButtonText:!1,showSearchButton:!1,showSearchClearButton:!1,trimOnSearch:!0,searchAlign:"right",searchTimeOut:500,searchText:"",customSearch:void 0,showHeader:!0,showFooter:!1,footerStyle:function(a){return{}},searchAccentNeutralise:!1,showColumns:!1,showSearch:!1,showPageGo:!1,showColumnsToggleAll:!1,showColumnsSearch:!1,minimumCountColumns:1,showPaginationSwitch:!1,showRefresh:!1,showToggle:!1,showFullscreen:!1,smartDisplay:!0,escape:!1,filterOptions:{filterAlgorithm:"and"},idField:void 0,selectItemName:"btSelectItem",clickToSelect:!1,ignoreClickToSelectOn:function(a){var b=a.tagName;return["A","BUTTON"].includes(b)},singleSelect:!1,checkboxHeader:!0,maintainMetaData:!1,multipleSelectRow:!1,uniqueId:void 0,cardView:!1,detailView:!1,detailViewIcon:!0,detailViewByClick:!1,detailViewAlign:"left",detailFormatter:function(a,b){return""},detailFilter:function(a,b){return !0},toolbar:void 0,toolbarAlign:"left",buttonsToolbar:void 0,buttonsAlign:"right",buttonsOrder:["search","paginationSwitch","refresh","toggle","fullscreen","columns"],buttonsPrefix:eL.classes.buttonsPrefix,buttonsClass:eL.classes.buttons,icons:eL.icons,iconSize:void 0,iconsPrefix:eL.iconsPrefix,loadingFontSize:"auto",loadingTemplate:function(a){return'\n '.concat(a,'\n \n \n ')},onAll:function(a,b){return !1},onClickCell:function(b,c,a,d){return !1},onDblClickCell:function(b,c,a,d){return !1},onClickRow:function(a,b){return !1},onDblClickRow:function(a,b){return !1},onSort:function(a,b){return !1},onCheck:function(a){return !1},onUncheck:function(a){return !1},onCheckAll:function(a){return !1},onUncheckAll:function(a){return !1},onCheckSome:function(a){return !1},onUncheckSome:function(a){return !1},onLoadSuccess:function(a){return !1},onLoadError:function(a){return !1},onColumnSwitch:function(a,b){return !1},onPageChange:function(a,b){return !1},onSearch:function(a){return !1},onShowSearch:function(){return !1},onToggle:function(a){return !1},onPreBody:function(a){return !1},onPostBody:function(){return !1},onPostHeader:function(){return !1},onPostFooter:function(){return !1},onExpandRow:function(b,c,a){return !1},onCollapseRow:function(a,b){return !1},onRefreshOptions:function(a){return !1},onRefresh:function(a){return !1},onResetView:function(){return !1},onScrollBody:function(){return !1}},gM={formatLoadingMessage:function(){return"Loading, please wait"},formatRecordsPerPage:function(a){return"".concat(a," rows per page")},formatShowingRows:function(b,c,a,d){return void 0!==d&&d>0&&d>a?"Showing ".concat(b," to ").concat(c," of ").concat(a," rows (filtered from ").concat(d," total rows)"):"Showing ".concat(b," to ").concat(c," of ").concat(a," rows")},formatSRPaginationPreText:function(){return"previous page"},formatSRPaginationPageText:function(a){return"to page ".concat(a)},formatSRPaginationNextText:function(){return"next page"},formatDetailPagination:function(a){return"Showing ".concat(a," rows")},formatSearch:function(){return"Search"},formatShowSearch:function(){return"Show Search"},formatPageGo:function(){return"Go"},formatClearSearch:function(){return"Clear Search"},formatNoMatches:function(){return"No matching records found"},formatPaginationSwitch:function(){return"Hide/Show pagination"},formatPaginationSwitchDown:function(){return"Show pagination"},formatPaginationSwitchUp:function(){return"Hide pagination"},formatRefresh:function(){return"Refresh"},formatToggle:function(){return"Toggle"},formatToggleOn:function(){return"Show card view"},formatToggleOff:function(){return"Hide card view"},formatColumns:function(){return"Columns"},formatColumnsToggleAll:function(){return"Toggle all"},formatFullscreen:function(){return"Fullscreen"},formatAllRows:function(){return"All"}},b6={field:void 0,title:void 0,titleTooltip:void 0,"class":void 0,width:void 0,widthUnit:"px",rowspan:void 0,colspan:void 0,align:void 0,halign:void 0,falign:void 0,valign:void 0,cellStyle:void 0,radio:!1,checkbox:!1,checkboxEnabled:!0,clickToSelect:!0,showSelectTitle:!1,sortable:!1,sortName:void 0,order:"asc",sorter:void 0,visible:!0,ignore:!1,switchable:!0,cardVisible:!0,searchable:!0,formatter:void 0,footerFormatter:void 0,detailFormatter:void 0,searchFormatter:!0,searchHighlightFormatter:!1,escape:!1,events:void 0},cq=["getOptions","refreshOptions","getData","getSelections","load","append","prepend","remove","removeAll","insertRow","updateRow","getRowByUniqueId","updateByUniqueId","removeByUniqueId","updateCell","updateCellByUniqueId","showRow","hideRow","getHiddenRows","showColumn","hideColumn","getVisibleColumns","getHiddenColumns","showAllColumns","hideAllColumns","mergeCells","checkAll","uncheckAll","checkInvert","check","uncheck","checkBy","uncheckBy","refresh","destroy","resetView","showLoading","hideLoading","togglePagination","toggleFullscreen","toggleView","resetSearch","filterBy","scrollTo","getScrollPosition","selectPage","prevPage","nextPage","toggleDetailView","expandRow","collapseRow","expandRowByUniqueId","collapseRowByUniqueId","expandAllRows","collapseAllRows","updateColumnTitle","updateFormatText"],ab={"all.bs.table":"onAll","click-row.bs.table":"onClickRow","dbl-click-row.bs.table":"onDblClickRow","click-cell.bs.table":"onClickCell","dbl-click-cell.bs.table":"onDblClickCell","sort.bs.table":"onSort","check.bs.table":"onCheck","uncheck.bs.table":"onUncheck","check-all.bs.table":"onCheckAll","uncheck-all.bs.table":"onUncheckAll","check-some.bs.table":"onCheckSome","uncheck-some.bs.table":"onUncheckSome","load-success.bs.table":"onLoadSuccess","load-error.bs.table":"onLoadError","column-switch.bs.table":"onColumnSwitch","page-change.bs.table":"onPageChange","search.bs.table":"onSearch","toggle.bs.table":"onToggle","pre-body.bs.table":"onPreBody","post-body.bs.table":"onPostBody","post-header.bs.table":"onPostHeader","post-footer.bs.table":"onPostFooter","expand-row.bs.table":"onExpandRow","collapse-row.bs.table":"onCollapseRow","refresh-options.bs.table":"onRefreshOptions","reset-view.bs.table":"onResetView","refresh.bs.table":"onRefresh","scroll-body.bs.table":"onScrollBody"};Object.assign(cY,gM);var fb={VERSION:aX,THEME:"bootstrap".concat(cG),CONSTANTS:eL,DEFAULTS:cY,COLUMN_DEFAULTS:b6,METHODS:cq,EVENTS:ab,LOCALES:{en:gM,"en-US":gM}},bX=fA(function(){e2(1)});cB({target:"Object",stat:!0,forced:bX},{keys:function(a){return e2(dD(a))}});var b9=gp.f,fT="".startsWith,dR=Math.min,bH=bD("startsWith"),a9=!bH&&!!function(){var a=b9(String.prototype,"startsWith");return a&&!a.writable}();cB({target:"String",proto:!0,forced:!a9&&!bH},{startsWith:function(b){var c=f9(this)+"";fP(b);var a=af(dR(arguments.length>1?arguments[1]:void 0,c.length)),d=b+"";return fT?fT.call(c,d,a):c.slice(a,a+d.length)===d}});var eN=gp.f,dT="".endsWith,cK=Math.min,cu=bD("endsWith"),gG=!cu&&!!function(){var a=eN(String.prototype,"endsWith");return a&&!a.writable}();cB({target:"String",proto:!0,forced:!gG&&!cu},{endsWith:function(d){var f=f9(this)+"";fP(d);var c=arguments.length>1?arguments[1]:void 0,h=af(f.length),g=void 0===c?h:cK(af(c),h),b=d+"";return dT?dT.call(f,b,g):f.slice(g-b.length,g)===b}});var br={getSearchInput:function(a){return"string"==typeof a.options.searchSelector?fc["default"](a.options.searchSelector):a.$toolbar.find(".search input")},sprintf:function(d){for(var g=arguments.length,c=Array(g>1?g-1:0),j=1;g>j;j++){c[j-1]=arguments[j]}var h=!0,b=0,f=d.replace(/%s/g,function(){var a=c[b++];return void 0===a?(h=!1,""):a});return h?f:""},isObject:function(a){return a instanceof Object&&!Array.isArray(a)},isEmptyObject:function(){var a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return 0===Object.entries(a).length&&a.constructor===Object},isNumeric:function(a){return !isNaN(parseFloat(a))&&isFinite(a)},getFieldTitle:function(d,f){var c,h=fg(d);try{for(h.s();!(c=h.n()).done;){var g=c.value;if(g.field===f){return g.title}}}catch(b){h.e(b)}finally{h.f()}return""},setFieldIndex:function(k){var F,B=0,y=[],x=fg(k[0]);try{for(x.s();!(F=x.n()).done;){var J=F.value;B+=J.colspan||1}}catch(q){x.e(q)}finally{x.f()}for(var v=0;vA;A++){y[v][A]=!1}}for(var H=0;HI;I++){for(var z=0;w>z;z++){y[H+I][D+z]=!0}}}}catch(q){j.e(q)}finally{j.f()}}},normalizeAccent:function(a){return"string"!=typeof a?a:a.normalize("NFD").replace(/[\u0300-\u036f]/g,"")},updateFieldGroup:function(y){var q,k,g=(q=[]).concat.apply(q,fp(y)),b=fg(y);try{for(b.s();!(k=b.n()).done;){var w,z=k.value,j=fg(z);try{for(j.s();!(w=j.n()).done;){var v=w.value;if(v.colspanGroup>1){for(var m=0,x=function(a){var c=g.find(function(d){return d.fieldIndex===a});c.visible&&m++},r=v.colspanIndex;r0}}}catch(p){j.e(p)}finally{j.f()}}}catch(p){b.e(p)}finally{b.f()}},getScrollBarWidth:function(){if(void 0===this.cachedWidth){var b=fc["default"]("
    ").addClass("fixed-table-scroll-inner"),c=fc["default"]("
    ").addClass("fixed-table-scroll-outer");c.append(b),fc["default"]("body").append(c);var a=b[0].offsetWidth;c.css("overflow","scroll");var d=b[0].offsetWidth;a===d&&(d=c[0].clientWidth),c.remove(),this.cachedWidth=a-d}return this.cachedWidth},calculateObjectValue:function(p,i,d,b){var k=i;if("string"==typeof i){var q=i.split(".");if(q.length>1){k=window;var f,j=fg(q);try{for(j.s();!(f=j.n()).done;){var g=f.value;k=k[g]}}catch(m){j.e(m)}finally{j.f()}}else{k=window[i]}}return null!==k&&"object"===fD(k)?k:"function"==typeof k?k.apply(p,d||[]):!k&&"string"==typeof i&&this.sprintf.apply(this,[i].concat(fp(d)))?this.sprintf.apply(this,[i].concat(fp(d))):b},compareObjects:function(d,h,c){var k=Object.keys(d),j=Object.keys(h);if(c&&k.length!==j.length){return !1}for(var b=0,f=k;b/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/`/g,"`"):a},unescapeHTML:function(a){return"string"==typeof a?a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/`/g,"`"):a},getRealDataAttr:function(d){for(var g=0,c=Object.entries(d);gtd,>th").each(function(e,r){for(var v=fc["default"](r),k=+v.attr("colspan")||1,q=+v.attr("rowspan")||1,m=e;d[j]&&d[j][m];m++){}for(var t=m;m+k>t;t++){for(var p=j;j+q>p;p++){d[p]||(d[p]=[]),d[p][t]=!0}}var o=b[m].field;i[o]=v.html().trim(),i["_".concat(o,"_id")]=v.attr("id"),i["_".concat(o,"_class")]=v.attr("class"),i["_".concat(o,"_rowspan")]=v.attr("rowspan"),i["_".concat(o,"_colspan")]=v.attr("colspan"),i["_".concat(o,"_title")]=v.attr("title"),i["_".concat(o,"_data")]=a.getRealDataAttr(v.data()),i["_".concat(o,"_style")]=v.attr("style")}),f.push(i)}),f},sort:function(d,f,c,h,g,b){return(void 0===d||null===d)&&(d=""),(void 0===f||null===f)&&(f=""),h&&d===f&&(d=g,f=b),this.isNumeric(d)&&this.isNumeric(f)?(d=parseFloat(d),f=parseFloat(f),f>d?-1*c:d>f?c:0):d===f?0:("string"!=typeof d&&(d=""+d),-1===d.localeCompare(f)?-1*c:c)},getEventName:function(a){var b=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return b=b||"".concat(+new Date).concat(~~(1000000*Math.random())),"".concat(a,"-").concat(b)},hasDetailViewIcon:function(a){return a.detailView&&a.detailViewIcon&&!a.cardView},getDetailViewIndexOffset:function(a){return this.hasDetailViewIcon(a)&&"right"!==a.detailViewAlign?1:0},checkAutoMergeCells:function(d){var h,c=fg(d);try{for(c.s();!(h=c.n()).done;){for(var k=h.value,j=0,b=Object.keys(k);jc&&b++;for(var f=g;d>f;f++){k[f]&&m.push(k[f])}return{topOffset:c,bottomOffset:j,rowsAbove:b,rows:m}}},{key:"checkChanges",value:function(c,d){var b=d!==this.cache[c];return this.cache[c]=d,b}},{key:"getExtra",value:function(c,d){var b=document.createElement("tr");return b.className="virtual-scroll-".concat(c),d&&(b.style.height="".concat(d,"px")),b.outerHTML}}]),a}(),d5=function(){function a(d,c){fw(this,a),this.options=c,this.$el=fc["default"](d),this.$el_=this.$el.clone(),this.timeoutId_=0,this.timeoutFooter_=0}return fQ(a,[{key:"init",value:function(){this.initConstants(),this.initLocale(),this.initContainer(),this.initTable(),this.initHeader(),this.initData(),this.initHiddenRows(),this.initToolbar(),this.initPagination(),this.initBody(),this.initSearchText(),this.initServer()}},{key:"initConstants",value:function(){var c=this.options;this.constants=fb.CONSTANTS,this.constants.theme=fc["default"].fn.bootstrapTable.theme,this.constants.dataToggle=this.constants.html.dataToggle||"data-toggle";var d=c.buttonsPrefix?"".concat(c.buttonsPrefix,"-"):"";this.constants.buttonsClass=[c.buttonsPrefix,d+c.buttonsClass,br.sprintf("".concat(d,"%s"),c.iconSize)].join(" ").trim(),this.buttons=br.calculateObjectValue(this,c.buttons,[],{}),"object"!==fD(this.buttons)&&(this.buttons={}),"string"==typeof c.icons&&(c.icons=br.calculateObjectValue(null,c.icons))}},{key:"initLocale",value:function(){if(this.options.locale){var c=fc["default"].fn.bootstrapTable.locales,d=this.options.locale.split(/-|_/);d[0]=d[0].toLowerCase(),d[1]&&(d[1]=d[1].toUpperCase()),c[this.options.locale]?fc["default"].extend(this.options,c[this.options.locale]):c[d.join("-")]?fc["default"].extend(this.options,c[d.join("-")]):c[d[0]]&&fc["default"].extend(this.options,c[d[0]])}}},{key:"initContainer",value:function(){var d=["top","both"].includes(this.options.paginationVAlign)?'
    ':"",f=["bottom","both"].includes(this.options.paginationVAlign)?'
    ':"",c=br.calculateObjectValue(this.options,this.options.loadingTemplate,[this.options.formatLoadingMessage()]);this.$container=fc["default"]('\n
    \n
    \n ').concat(d,'\n
    \n
    \n
    \n
    \n ').concat(c,'\n
    \n
    \n \n
    \n ').concat(f,"\n
    \n ")),this.$container.insertAfter(this.$el),this.$tableContainer=this.$container.find(".fixed-table-container"),this.$tableHeader=this.$container.find(".fixed-table-header"),this.$tableBody=this.$container.find(".fixed-table-body"),this.$tableLoading=this.$container.find(".fixed-table-loading"),this.$tableFooter=this.$el.find("tfoot"),this.options.buttonsToolbar?this.$toolbar=fc["default"]("body").find(this.options.buttonsToolbar):this.$toolbar=this.$container.find(".fixed-table-toolbar"),this.$pagination=this.$container.find(".fixed-table-pagination"),this.$tableBody.append(this.$el),this.$container.after('
    '),this.$el.addClass(this.options.classes),this.$tableLoading.addClass(this.options.classes),this.options.striped&&this.$el.addClass("table-striped"),this.options.height&&(this.$tableContainer.addClass("fixed-height"),this.options.showFooter&&this.$tableContainer.addClass("has-footer"),this.options.classes.split(" ").includes("table-bordered")&&(this.$tableBody.append('
    '),this.$tableBorder=this.$tableBody.find(".fixed-table-border"),this.$tableLoading.addClass("fixed-table-border")),this.$tableFooter=this.$container.find(".fixed-table-footer"))}},{key:"initTable",value:function(){var d=this,c=[];if(this.$header=this.$el.find(">thead"),this.$header.length?this.options.theadClasses&&this.$header.addClass(this.options.theadClasses):this.$header=fc["default"]('')).appendTo(this.$el),this._headerTrClasses=[],this._headerTrStyles=[],this.$header.find("tr").each(function(g,i){var h=fc["default"](i),f=[];h.find("th").each(function(k,l){var j=fc["default"](l);void 0!==j.data("field")&&j.data("field","".concat(j.data("field"))),f.push(fc["default"].extend({},{title:j.html(),"class":j.attr("class"),titleTooltip:j.attr("title"),rowspan:j.attr("rowspan")?+j.attr("rowspan"):void 0,colspan:j.attr("colspan")?+j.attr("colspan"):void 0},j.data()))}),c.push(f),h.attr("class")&&d._headerTrClasses.push(h.attr("class")),h.attr("style")&&d._headerTrStyles.push(h.attr("style"))}),Array.isArray(this.options.columns[0])||(this.options.columns=[this.options.columns]),this.options.columns=fc["default"].extend(!0,[],c,this.options.columns),this.columns=[],this.fieldsColumnsIndex=[],br.setFieldIndex(this.options.columns),this.options.columns.forEach(function(f,g){f.forEach(function(j,k){var h=fc["default"].extend({},a.COLUMN_DEFAULTS,j);void 0!==h.fieldIndex&&(d.columns[h.fieldIndex]=h,d.fieldsColumnsIndex[h.field]=h.fieldIndex),d.options.columns[g][k]=h})}),!this.options.data.length){var e=br.trToData(this.columns,this.$el.find(">tbody>tr"));e.length&&(this.options.data=e,this.fromHtml=!0)}this.options.pagination&&"server"!==this.options.sidePagination||(this.footerData=br.trToData(this.columns,this.$el.find(">tfoot>tr"))),this.footerData&&this.$el.find("tfoot").html(""),!this.options.showFooter||this.options.cardView?this.$tableFooter.hide():this.$tableFooter.show()}},{key:"initHeader",value:function(){var d=this,f={},c=[];this.header={fields:[],styles:[],classes:[],formatters:[],detailFormatters:[],events:[],sorters:[],sortNames:[],cellStyles:[],searchables:[]},br.updateFieldGroup(this.options.columns),this.options.columns.forEach(function(k,j){var h=[];h.push(""));var i="";if(0===j&&br.hasDetailViewIcon(d.options)){var e=d.options.columns.length>1?' rowspan="'.concat(d.options.columns.length,'"'):"";i='\n
    \n ')}i&&"right"!==d.options.detailViewAlign&&h.push(i),k.forEach(function(G,D){var B=br.sprintf(' class="%s"',G["class"]),F=G.widthUnit,L=parseFloat(G.width),H=br.sprintf("text-align: %s; ",G.halign?G.halign:G.align),A=br.sprintf("text-align: %s; ",G.align),K=br.sprintf("vertical-align: %s; ",G.valign);if(K+=br.sprintf("width: %s; ",!G.checkbox&&!G.radio||L?L?L+F:void 0:G.showSelectTitle?void 0:"36px"),void 0!==G.fieldIndex||G.visible){var J=br.calculateObjectValue(null,d.options.headerStyle,[G]),C=[],I="";if(J&&J.css){for(var z=0,M=Object.entries(J.css);z0?" data-not-first-th":"",">"),h.push(br.sprintf('
    ',d.options.sortable&&G.sortable?"sortable both":""));var o=d.options.escape?br.escapeHTML(G.title):G.title,s=o;G.checkbox&&(o="",!d.options.singleSelect&&d.options.checkboxHeader&&(o=''),d.header.stateField=G.field),G.radio&&(o="",d.header.stateField=G.field),!o&&G.showSelectTitle&&(o+=s),h.push(o),h.push("
    "),h.push('
    '),h.push("
    "),h.push("")}}),i&&"right"===d.options.detailViewAlign&&h.push(i),h.push(""),h.length>3&&c.push(h.join(""))}),this.$header.html(c.join("")),this.$header.find("th[data-field]").each(function(h,e){fc["default"](e).data(f[fc["default"](e).data("field")])}),this.$container.off("click",".th-inner").on("click",".th-inner",function(j){var h=fc["default"](j.currentTarget);return d.options.detailView&&!h.parent().hasClass("bs-checkbox")&&h.closest(".bootstrap-table")[0]!==d.$container[0]?!1:void (d.options.sortable&&h.parent().data().sortable&&d.onSort(j))}),this.$header.children().children().off("keypress").on("keypress",function(j){if(d.options.sortable&&fc["default"](j.currentTarget).data().sortable){var h=j.keyCode||j.which;13===h&&d.onSort(j)}});var g=br.getEventName("resize.bootstrap-table",this.$el.attr("id"));fc["default"](window).off(g),!this.options.showHeader||this.options.cardView?(this.$header.hide(),this.$tableHeader.hide(),this.$tableLoading.css("top",0)):(this.$header.show(),this.$tableHeader.show(),this.$tableLoading.css("top",this.$header.outerHeight()+1),this.getCaret(),fc["default"](window).on(g,function(){return d.resetView()})),this.$selectAll=this.$header.find('[name="btSelectAll"]'),this.$selectAll.off("click").on("click",function(j){j.stopPropagation();var h=fc["default"](j.currentTarget).prop("checked");d[h?"checkAll":"uncheckAll"](),d.updateSelected()})}},{key:"initData",value:function(c,d){"append"===d?this.options.data=this.options.data.concat(c):"prepend"===d?this.options.data=[].concat(c).concat(this.options.data):(c=c||br.deepCopy(this.options.data),this.options.data=Array.isArray(c)?c:c[this.options.dataField]),this.data=fp(this.options.data),this.options.sortReset&&(this.unsortedData=fp(this.data)),"server"!==this.options.sidePagination&&this.initSort()}},{key:"initSort",value:function(){var d=this,f=this.options.sortName,c="desc"===this.options.sortOrder?-1:1,h=this.header.fields.indexOf(this.options.sortName),g=0;-1!==h?(this.options.sortStable&&this.data.forEach(function(i,j){i.hasOwnProperty("_position")||(i._position=j)}),this.options.customSort?br.calculateObjectValue(this.options,this.options.customSort,[this.options.sortName,this.options.sortOrder,this.data]):this.data.sort(function(m,i){d.header.sortNames[h]&&(f=d.header.sortNames[h]);var j=br.getItemField(m,f,d.options.escape),k=br.getItemField(i,f,d.options.escape),e=br.calculateObjectValue(d.header,d.header.sorters[h],[j,k,m,i]);return void 0!==e?d.options.sortStable&&0===e?c*(m._position-i._position):c*e:br.sort(j,k,c,d.options.sortStable,m._position,i._position)}),void 0!==this.options.sortClass&&(clearTimeout(g),g=setTimeout(function(){d.$el.removeClass(d.options.sortClass);var i=d.$header.find('[data-field="'.concat(d.options.sortName,'"]')).index();d.$el.find("tr td:nth-child(".concat(i+1,")")).addClass(d.options.sortClass)},250))):this.options.sortReset&&(this.data=fp(this.unsortedData))}},{key:"onSort",value:function(f){var g=f.type,d=f.currentTarget,j="keypress"===g?fc["default"](d):fc["default"](d).parent(),h=this.$header.find("th").eq(j.index());if(this.$header.add(this.$header_).find("span.order").remove(),this.options.sortName===j.data("field")){var c=this.options.sortOrder;void 0===c?this.options.sortOrder="asc":"asc"===c?this.options.sortOrder="desc":"desc"===this.options.sortOrder&&(this.options.sortOrder=this.options.sortReset?void 0:"asc"),void 0===this.options.sortOrder&&(this.options.sortName=void 0)}else{this.options.sortName=j.data("field"),this.options.rememberOrder?this.options.sortOrder="asc"===j.data("order")?"desc":"asc":this.options.sortOrder=this.columns[this.fieldsColumnsIndex[j.data("field")]].sortOrder||this.columns[this.fieldsColumnsIndex[j.data("field")]].order}return this.trigger("sort",this.options.sortName,this.options.sortOrder),j.add(h).data("order",this.options.sortOrder),this.getCaret(),"server"===this.options.sidePagination&&this.options.serverSort?(this.options.pageNumber=1,void this.initServer(this.options.silentSort)):(this.initSort(),void this.initBody())}},{key:"initToolbar",value:function(){var di,fn=this,ei=this.options,ee=[],ge=0,dn=0;this.$toolbar.find(".bs-bars").children().length&&fc["default"]("body").append(fc["default"](ei.toolbar)),this.$toolbar.html(""),("string"==typeof ei.toolbar||"object"===fD(ei.toolbar))&&fc["default"](br.sprintf('
    ',this.constants.classes.pull,ei.toolbarAlign)).appendTo(this.$toolbar).append(fc["default"](ei.toolbar)),ee=['
    ')],"string"==typeof ei.buttonsOrder&&(ei.buttonsOrder=ei.buttonsOrder.replace(/\[|\]| |'/g,"").split(",")),this.buttons=Object.assign(this.buttons,{search:{text:ei.formatSearch(),icon:ei.icons.search,render:!1,event:this.toggleShowSearch,attributes:{"aria-label":ei.formatShowSearch(),title:ei.formatShowSearch()}},paginationSwitch:{text:ei.pagination?ei.formatPaginationSwitchUp():ei.formatPaginationSwitchDown(),icon:ei.pagination?ei.icons.paginationSwitchDown:ei.icons.paginationSwitchUp,render:!1,event:this.togglePagination,attributes:{"aria-label":ei.formatPaginationSwitch(),title:ei.formatPaginationSwitch()}},refresh:{text:ei.formatRefresh(),icon:ei.icons.refresh,render:!1,event:this.refresh,attributes:{"aria-label":ei.formatRefresh(),title:ei.formatRefresh()}},toggle:{text:ei.formatToggle(),icon:ei.icons.toggleOff,render:!1,event:this.toggleView,attributes:{"aria-label":ei.formatToggleOn(),title:ei.formatToggleOn()}},fullscreen:{text:ei.formatFullscreen(),icon:ei.icons.fullscreen,render:!1,event:this.toggleFullscreen,attributes:{"aria-label":ei.formatFullscreen(),title:ei.formatFullscreen()}},columns:{render:!1,html:function s(){var e=[];if(e.push('
    \n \n ").concat(fn.constants.html.toolbarDropdown[0])),ei.showColumnsSearch&&(e.push(br.sprintf(fn.constants.html.toolbarDropdownItem,br.sprintf('',fn.constants.classes.input,ei.formatSearch()))),e.push(fn.constants.html.toolbarDropdownSeparator)),ei.showColumnsToggleAll){var d=fn.getVisibleColumns().length===fn.columns.filter(function(f){return !fn.isSelectionColumn(f)}).length;e.push(br.sprintf(fn.constants.html.toolbarDropdownItem,br.sprintf(' %s',d?'checked="checked"':"",ei.formatColumnsToggleAll()))),e.push(fn.constants.html.toolbarDropdownSeparator)}var c=0;return fn.columns.forEach(function(f){f.visible&&c++}),fn.columns.forEach(function(g,j){if(!fn.isSelectionColumn(g)&&(!ei.cardView||g.cardVisible)&&!g.ignore){var f=g.visible?' checked="checked"':"",h=c<=ei.minimumCountColumns&&f?' disabled="disabled"':"";g.switchable&&(e.push(br.sprintf(fn.constants.html.toolbarDropdownItem,br.sprintf(' %s',g.field,j,f,h,g.title))),dn++)}}),e.push(fn.constants.html.toolbarDropdown[1],"
    "),e.join("")}}});for(var eo={},ft=0,fa=Object.entries(this.buttons);ft"}eo[fo]=ea;var ct="show".concat(fo.charAt(0).toUpperCase()).concat(fo.substring(1)),es=ei[ct];!(!fi.hasOwnProperty("render")||fi.hasOwnProperty("render")&&fi.render)||void 0!==es&&es!==!0||(ei[ct]=!0),ei.buttonsOrder.includes(fo)||ei.buttonsOrder.push(fo)}var ai,Q=fg(ei.buttonsOrder);try{for(Q.s();!(ai=Q.n()).done;){var ce=ai.value,ae=ei["show".concat(ce.charAt(0).toUpperCase()).concat(ce.substring(1))];ae&&ee.push(eo[ce])}}catch(be){Q.e(be)}finally{Q.f()}ee.push("
    "),(this.showToolbar||ee.length>2)&&this.$toolbar.append(ee.join("")),ei.showSearch&&this.$toolbar.find('button[name="showSearch"]').off("click").on("click",function(){return fn.toggleShowSearch()});for(var cn=0,co=Object.entries(this.buttons);cn'),v=dt;if(ei.showSearchButton||ei.showSearchClearButton){var bn=(ei.showSearchButton?J:"")+(ei.showSearchClearButton?cs:"");v=ei.search?br.sprintf(this.constants.html.inputGroup,dt,bn):bn}ee.push(br.sprintf('\n
    \n %s\n
    \n '),v)),this.$toolbar.append(ee.join(""));var ba=br.getSearchInput(this);ei.showSearchButton?(this.$toolbar.find(".search button[name=search]").off("click").on("click",function(){clearTimeout(ge),ge=setTimeout(function(){fn.onSearch({currentTarget:ba})},ei.searchTimeOut)}),ei.searchOnEnterKey&&ao(ba)):ao(ba),ei.showSearchClearButton&&this.$toolbar.find(".search button[name=clearSearch]").click(function(){fn.resetSearch()})}else{if("string"==typeof ei.searchSelector){var i=br.getSearchInput(this);ao(i)}}}},{key:"onSearch",value:function(){var d=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},f=d.currentTarget,c=d.firedByInitSearchText,h=arguments.length>1&&void 0!==arguments[1]?arguments[1]:!0;if(void 0!==f&&fc["default"](f).length&&h){var g=fc["default"](f).val().trim();if(this.options.trimOnSearch&&fc["default"](f).val()!==g&&fc["default"](f).val(g),this.searchText===g){return}(f===br.getSearchInput(this)[0]||fc["default"](f).hasClass("search-input"))&&(this.searchText=g,this.options.searchText=g)}c||(this.options.pageNumber=1),this.initSearch(),c?"client"===this.options.sidePagination&&this.updatePagination():this.updatePagination(),this.trigger("search",this.searchText)}},{key:"initSearch",value:function(){var d=this;if(this.filterOptions=this.filterOptions||this.options.filterOptions,"server"!==this.options.sidePagination){if(this.options.customSearch){return this.data=br.calculateObjectValue(this.options,this.options.customSearch,[this.options.data,this.searchText,this.filterColumns]),void (this.options.sortReset&&(this.unsortedData=fp(this.data)))}var f=this.searchText&&(this.fromHtml?br.escapeHTML(this.searchText):this.searchText).toLowerCase(),c=br.isEmptyObject(this.filterColumns)?null:this.filterColumns;this.options.searchAccentNeutralise&&(f=br.normalizeAccent(f)),"function"==typeof this.filterOptions.filterAlgorithm?this.data=this.options.data.filter(function(h){return d.filterOptions.filterAlgorithm.apply(null,[h,c])}):"string"==typeof this.filterOptions.filterAlgorithm&&(this.data=c?this.options.data.filter(function(j){var l=d.filterOptions.filterAlgorithm;if("and"===l){for(var k in c){if(Array.isArray(c[k])&&!c[k].includes(j[k])||!Array.isArray(c[k])&&j[k]!==c[k]){return !1}}}else{if("or"===l){var h=!1;for(var i in c){(Array.isArray(c[i])&&c[i].includes(j[i])||!Array.isArray(c[i])&&j[i]===c[i])&&(h=!0)}return h}}return !0}):fp(this.options.data));var g=this.getVisibleFields();this.data=f?this.data.filter(function(n,k){for(var A=0;A|=<|>=|>|<)(?:\s+)?(-?\d+)?|(-?\d+)?(\s+)?(<=|=>|=<|>=|>|<))/gm,x=C.exec(d.searchText),w=!1;if(x){var j=x[1]||"".concat(x[5],"l"),t=x[2]||x[3],B=parseInt(m,10),z=parseInt(t,10);switch(j){case">":case"z;break;case"<":case">l":w=z>B;break;case"<=":case"=<":case">=l":case"=>l":w=z>=B;break;case">=":case"=>":case"<=l":case"==z}}if(w||"".concat(m).toLowerCase().includes(f)){return !0}}}}}return !1}):this.data,this.options.sortReset&&(this.unsortedData=fp(this.data)),this.initSort()}}},{key:"initPagination",value:function(){var O=this,K=this.options;if(!K.pagination){return void this.$pagination.hide()}this.$pagination.show();var G,F,T,C,D,I,Q,L=[],B=!1,P=this.getData({includeHiddenRows:!1}),N=K.pageList;if("string"==typeof N&&(N=N.replace(/\[|\]| /g,"").toLowerCase().split(",")),N=N.map(function(c){return"string"==typeof c?c.toLowerCase()===K.formatAllRows().toLowerCase()||["all","unlimited"].includes(c.toLowerCase())?K.formatAllRows():+c:c}),this.paginationParts=K.paginationParts,"string"==typeof this.paginationParts&&(this.paginationParts=this.paginationParts.replace(/\[|\]| |'/g,"").split(",")),"server"!==K.sidePagination&&(K.totalRows=P.length),this.totalPages=0,K.totalRows&&(K.pageSize===K.formatAllRows()&&(K.pageSize=K.totalRows,B=!0),this.totalPages=~~((K.totalRows-1)/K.pageSize)+1,K.totalPages=this.totalPages),this.totalPages>0&&K.pageNumber>this.totalPages&&(K.pageNumber=this.totalPages),this.pageFrom=(K.pageNumber-1)*K.pageSize+1,this.pageTo=K.pageNumber*K.pageSize,this.pageTo>K.totalRows&&(this.pageTo=K.totalRows),this.options.pagination&&"server"!==this.options.sidePagination&&(this.options.totalNotFiltered=this.options.data.length),this.options.showExtendedPagination||(this.options.totalNotFiltered=void 0),(this.paginationParts.includes("pageInfo")||this.paginationParts.includes("pageInfoShort")||this.paginationParts.includes("pageSize"))&&L.push('
    ')),this.paginationParts.includes("pageInfo")||this.paginationParts.includes("pageInfoShort")){var E=this.paginationParts.includes("pageInfoShort")?K.formatDetailPagination(K.totalRows):K.formatShowingRows(this.pageFrom,this.pageTo,K.totalRows,K.totalNotFiltered);L.push('\n '.concat(E,"\n "))}if(this.paginationParts.includes("pageSize")){L.push('
    ');var M=['
    \n \n ").concat(this.constants.html.pageDropdown[0])];N.forEach(function(c,e){if(!K.smartDisplay||0===e||N[e-1]")),L.push(K.formatRecordsPerPage(M.join("")))}if((this.paginationParts.includes("pageInfo")||this.paginationParts.includes("pageInfoShort")||this.paginationParts.includes("pageSize"))&&L.push("
    "),this.paginationParts.includes("pageList")){L.push('
    '),br.sprintf(this.constants.html.pagination[0],br.sprintf(" pagination-%s",K.iconSize)),br.sprintf(this.constants.html.paginationItem," page-pre",K.formatSRPaginationPreText(),K.paginationPreText)),this.totalPagesthis.totalPages-F&&(F=F-(K.paginationSuccessivelySize-(this.totalPages-F))+1),1>F&&(F=1),T>this.totalPages&&(T=this.totalPages);var A=Math.round(K.paginationPagesBySide/2),R=function(c){var d=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";return br.sprintf(O.constants.html.paginationItem,d+(c===K.pageNumber?" ".concat(O.constants.classes.paginationActive):""),K.formatSRPaginationPageText(c),c)};if(F>1){var H=K.paginationPagesBySide;for(H>=F&&(H=F-1),G=1;H>=G;G++){L.push(R(G))}F-1===H+1?(G=F-1,L.push(R(G))):F-1>H&&(F-2*K.paginationPagesBySide>K.paginationPagesBySide&&K.paginationUseIntermediate?(G=Math.round((F-A)/2+A),L.push(R(G," page-intermediate"))):L.push(br.sprintf(this.constants.html.paginationItem," page-first-separator disabled","","...")))}for(G=F;T>=G;G++){L.push(R(G))}if(this.totalPages>T){var q=this.totalPages-(K.paginationPagesBySide-1);for(T>=q&&(q=T+1),T+1===q-1?(G=T+1,L.push(R(G))):q>T+1&&(this.totalPages-T>2*K.paginationPagesBySide&&K.paginationUseIntermediate?(G=Math.round((this.totalPages-A-T)/2+T),L.push(R(G," page-intermediate"))):L.push(br.sprintf(this.constants.html.paginationItem," page-last-separator disabled","","..."))),G=q;G<=this.totalPages;G++){L.push(R(G))}}L.push(br.sprintf(this.constants.html.paginationItem," page-next",K.formatSRPaginationNextText(),K.paginationNextText)),L.push(this.constants.html.pagination[1],"
    ")}this.$pagination.html(L.join(""));var z=["bottom","both"].includes(K.paginationVAlign)?" ".concat(this.constants.classes.dropup):"";if(this.$pagination.last().find(".page-list > div").addClass(z),!K.onlyInfoPagination&&(C=this.$pagination.find(".page-list a"),D=this.$pagination.find(".page-pre"),I=this.$pagination.find(".page-next"),Q=this.$pagination.find(".page-item").not(".page-next, .page-pre, .page-last-separator, .page-first-separator"),this.totalPages<=1&&this.$pagination.find("div.pagination").hide(),K.smartDisplay&&(N.length<2||K.totalRows<=N[0])&&this.$pagination.find("span.page-list").hide(),this.$pagination[this.getData().length?"show":"hide"](),K.paginationLoop||(1===K.pageNumber&&D.addClass("disabled"),K.pageNumber===this.totalPages&&I.addClass("disabled")),B&&(K.pageSize=K.formatAllRows()),C.off("click").on("click",function(c){return O.onPageListChange(c)}),D.off("click").on("click",function(c){return O.onPagePre(c)}),I.off("click").on("click",function(c){return O.onPageNext(c)}),Q.off("click").on("click",function(c){return O.onPageNumber(c)}),this.options.showPageGo)){var j=this,t=this.$pagination.find("ul.pagination"),J=t.find("li.pageGo");J.length||(J=fl('
  • '+br.sprintf('',this.options.pageNumber)+('
  • ").appendTo(t),J.find("button").click(function(){var c=parseInt(J.find("input").val())||1;(1>c||c>j.options.totalPages)&&(c=1),j.selectPage(c)}))}}},{key:"updatePagination",value:function(c){c&&fc["default"](c.currentTarget).hasClass("disabled")||(this.options.maintainMetaData||this.resetRows(),this.initPagination(),this.trigger("page-change",this.options.pageNumber,this.options.pageSize),"server"===this.options.sidePagination?this.initServer():this.initBody())}},{key:"onPageListChange",value:function(c){c.preventDefault();var d=fc["default"](c.currentTarget);return d.parent().addClass(this.constants.classes.dropdownActive).siblings().removeClass(this.constants.classes.dropdownActive),this.options.pageSize=d.text().toUpperCase()===this.options.formatAllRows().toUpperCase()?this.options.formatAllRows():+d.text(),this.$toolbar.find(".page-size").text(this.options.pageSize),this.updatePagination(c),!1}},{key:"onPagePre",value:function(c){return c.preventDefault(),this.options.pageNumber-1===0?this.options.pageNumber=this.options.totalPages:this.options.pageNumber--,this.updatePagination(c),!1}},{key:"onPageNext",value:function(c){return c.preventDefault(),this.options.pageNumber+1>this.options.totalPages?this.options.pageNumber=1:this.options.pageNumber++,this.updatePagination(c),!1}},{key:"onPageNumber",value:function(c){return c.preventDefault(),this.options.pageNumber!==+fc["default"](c.currentTarget).text()?(this.options.pageNumber=+fc["default"](c.currentTarget).text(),this.updatePagination(c),!1):void 0}},{key:"initRow",value:function(G,X,M,L){var ae=this,J=[],Q={},Z=[],U="",F={},Y=[];if(!(br.findIndex(this.hiddenRows,G)>-1)){if(Q=br.calculateObjectValue(this.options,this.options.rowStyle,[G,X],Q),Q&&Q.css){for(var W=0,K=Object.entries(Q.css);W"),this.options.cardView&&J.push('
    '));var A="";return br.hasDetailViewIcon(this.options)&&(A="",br.calculateObjectValue(null,this.options.detailFilter,[X,G])&&(A+='\n \n '.concat(br.sprintf(this.constants.html.icon,this.options.iconsPrefix,this.options.icons.detailOpen),"\n \n ")),A+=""),A&&"right"!==this.options.detailViewAlign&&J.push(A),this.header.fields.forEach(function(eo,dt){var dn="",ee=br.getItemField(G,eo,ae.options.escape),es="",de="",fe={},fa="",di=ae.header.classes[dt],et="",da="",fi="",ea="",co="",ct="",r=ae.columns[dt];if((!ae.fromHtml&&!ae.autoMergeCells||void 0!==ee||r.checkbox||r.radio)&&r.visible&&(!ae.options.cardView||r.cardVisible)){if(r.escape&&(ee=br.escapeHTML(ee)),Z.concat([ae.header.styles[dt]]).length&&(da+="".concat(Z.concat([ae.header.styles[dt]]).join("; "))),G["_".concat(eo,"_style")]&&(da+="".concat(G["_".concat(eo,"_style")])),da&&(et=' style="'.concat(da,'"')),G["_".concat(eo,"_id")]&&(fa=br.sprintf(' id="%s"',G["_".concat(eo,"_id")])),G["_".concat(eo,"_class")]&&(di=br.sprintf(' class="%s"',G["_".concat(eo,"_class")])),G["_".concat(eo,"_rowspan")]&&(ea=br.sprintf(' rowspan="%s"',G["_".concat(eo,"_rowspan")])),G["_".concat(eo,"_colspan")]&&(co=br.sprintf(' colspan="%s"',G["_".concat(eo,"_colspan")])),G["_".concat(eo,"_title")]&&(ct=br.sprintf(' title="%s"',G["_".concat(eo,"_title")])),fe=br.calculateObjectValue(ae.header,ae.header.cellStyles[dt],[ee,G,X,eo],fe),fe.classes&&(di=' class="'.concat(fe.classes,'"')),fe.css){for(var cs=[],ei=0,an=Object.entries(fe.css);ei$1",t=es&&/<(?=.*? .*?\/ ?>|br|hr|input|!--|wbr)[a-z]+.*?>|<([a-z]+).*?<\/\1>/i.test(es);if(t){var bo=(new DOMParser).parseFromString(""+es,"text/html").documentElement.textContent,en=bo.replace(ci,cn);be=es.replace(RegExp("(>\\s*)(".concat(bo,")(\\s*)"),"gm"),"$1".concat(en,"$3"))}else{be=(""+es).replace(ci,cn)}es=br.calculateObjectValue(r,r.searchHighlightFormatter,[es,ae.searchText],be)}if(G["_".concat(eo,"_data")]&&!br.isEmptyObject(G["_".concat(eo,"_data")])){for(var fn=0,ao=Object.entries(G["_".concat(eo,"_data")]);fn'):'"))+'")+(ae.header.formatters[dt]&&"string"==typeof es?es:"")+(ae.options.cardView?"
    ":""),G[ae.header.stateField]=es===!0||!!ee||es&&es.checked}else{if(ae.options.cardView){var at=ae.options.showHeader?'").concat(br.getFieldTitle(ae.columns,eo),""):"";dn='
    '.concat(at,'").concat(es,"
    "),ae.options.smartDisplay&&""===es&&(dn='
    ')}else{dn="").concat(es,"")}}J.push(dn)}}),A&&"right"===this.options.detailViewAlign&&J.push(A),this.options.cardView&&J.push("
    "),J.push(""),J.join("")}}},{key:"initBody",value:function(m){var j=this,h=this.getData();this.trigger("pre-body",h),this.$body=this.$el.find(">tbody"),this.$body.length||(this.$body=fc["default"]("").appendTo(this.$el)),this.options.pagination&&"server"!==this.options.sidePagination||(this.pageFrom=1,this.pageTo=h.length);var f=[],d=fc["default"](document.createDocumentFragment()),k=!1;this.autoMergeCells=br.checkAutoMergeCells(h.slice(this.pageFrom-1,this.pageTo));for(var p=this.pageFrom-1;p'.concat(br.sprintf('%s',this.getVisibleFields().length+br.getDetailViewIndexOffset(this.options),this.options.formatNoMatches()),"")),m||this.scrollTo(0),this.initBodyEvent(),this.updateSelected(),this.initFooter(),this.resetView(),"server"!==this.options.sidePagination&&(this.options.totalRows=h.length),this.trigger("post-body",h)}},{key:"initBodyEvent",value:function(){var c=this;this.$body.find("> tr[data-index] > td").off("click dblclick").on("click dblclick",function(v){var p=fc["default"](v.currentTarget),k=p.parent(),j=fc["default"](v.target).parents(".card-views").children(),y=fc["default"](v.target).parents(".card-view"),A=k.data("index"),g=c.data[A],m=c.options.cardView?j.index(y):p[0].cellIndex,x=c.getVisibleFields(),q=x[m-br.getDetailViewIndexOffset(c.options)],z=c.columns[c.fieldsColumnsIndex[q]],w=br.getItemField(g,q,c.options.escape);if(!p.find(".detail-icon").length){if(c.trigger("click"===v.type?"click-cell":"dbl-click-cell",q,w,g,p),c.trigger("click"===v.type?"click-row":"dbl-click-row",g,k,q),"click"===v.type&&c.options.clickToSelect&&z.clickToSelect&&!br.calculateObjectValue(c.options,c.options.ignoreClickToSelectOn,[v.target])){var t=k.find(br.sprintf('[name="%s"]',c.options.selectItemName));t.length&&t[0].click()}"click"===v.type&&c.options.detailViewByClick&&c.toggleDetailView(A,c.header.detailFormatters[c.fieldsColumnsIndex[q]])}}).off("mousedown").on("mousedown",function(d){c.multipleSelectRowCtrlKey=d.ctrlKey||d.metaKey,c.multipleSelectRowShiftKey=d.shiftKey}),this.$body.find("> tr[data-index] > td > .detail-icon").off("click").on("click",function(d){return d.preventDefault(),c.toggleDetailView(fc["default"](d.currentTarget).parent().parent().data("index")),!1}),this.$selectItem=this.$body.find(br.sprintf('[name="%s"]',this.options.selectItemName)),this.$selectItem.off("click").on("click",function(f){f.stopImmediatePropagation();var d=fc["default"](f.currentTarget);c._toggleCheck(d.prop("checked"),d.data("index"))}),this.header.events.forEach(function(j,f){var l=j;if(l){"string"==typeof l&&(l=br.calculateObjectValue(null,l));var k=c.header.fields[f],d=c.getVisibleFields().indexOf(k);if(-1!==d){d+=br.getDetailViewIndexOffset(c.options);var g=function(n){if(!l.hasOwnProperty(n)){return"continue"}var m=l[n];c.$body.find(">tr:not(.no-records-found)").each(function(v,p){var q=fc["default"](p),e=q.find(c.options.cardView?".card-views>.card-view":">td").eq(d),t=n.indexOf(" "),o=n.substring(0,t),i=n.substring(t+1);e.find(i).off(o).on(o,function(w){var x=q.data("index"),r=c.data[x],u=r[k];m.apply(c,[w,u,r,x])})})};for(var h in l){g(h)}}}})}},{key:"initServer",value:function(x,p,k){var g=this,f={},v=this.header.fields.indexOf(this.options.sortName),y={searchText:this.searchText,sortName:this.options.sortName,sortOrder:this.options.sortOrder};if(this.header.sortNames[v]&&(y.sortName=this.header.sortNames[v]),this.options.pagination&&"server"===this.options.sidePagination&&(y.pageSize=this.options.pageSize===this.options.formatAllRows()?this.options.totalRows:this.options.pageSize,y.pageNumber=this.options.pageNumber),!this.options.firstLoad&&!firstLoadTable.includes(this.options.id)){return void firstLoadTable.push(this.options.id)}if(k||this.options.url||this.options.ajax){if("limit"===this.options.queryParamsType&&(y={search:y.searchText,sort:y.sortName,order:y.sortOrder},this.options.pagination&&"server"===this.options.sidePagination&&(y.offset=this.options.pageSize===this.options.formatAllRows()?0:this.options.pageSize*(this.options.pageNumber-1),y.limit=this.options.pageSize===this.options.formatAllRows()?this.options.totalRows:this.options.pageSize,0===y.limit&&delete y.limit)),this.options.search&&"server"===this.options.sidePagination&&this.columns.filter(function(c){return !c.searchable}).length){y.searchable=[];var d,j=fg(this.columns);try{for(j.s();!(d=j.n()).done;){var q=d.value;!q.checkbox&&q.searchable&&(this.options.visibleSearch&&q.visible||!this.options.visibleSearch)&&y.searchable.push(q.field)}}catch(m){j.e(m)}finally{j.f()}}if(br.isEmptyObject(this.filterColumnsPartial)||(y.filter=JSON.stringify(this.filterColumnsPartial,null)),fc["default"].extend(y,p||{}),f=br.calculateObjectValue(this.options,this.options.queryParams,[y],f),f!==!1){x||this.showLoading();var w=fc["default"].extend({},br.calculateObjectValue(null,this.options.ajaxOptions),{type:this.options.method,url:k||this.options.url,data:"application/json"===this.options.contentType&&"post"===this.options.method?JSON.stringify(f):f,cache:this.options.cache,contentType:this.options.contentType,dataType:this.options.dataType,success:function(l,h,n){var c=br.calculateObjectValue(g.options,g.options.responseHandler,[l,n],l);g.load(c),g.trigger("load-success",c,n&&n.status,n),x||g.hideLoading(),"server"===g.options.sidePagination&&c[g.options.totalField]>0&&!c[g.options.dataField].length&&g.updatePagination()},error:function(h){var c=[];"server"===g.options.sidePagination&&(c={},c[g.options.totalField]=0,c[g.options.dataField]=[]),g.load(c),g.trigger("load-error",h&&h.status,h),x||g.$tableLoading.hide()}});return this.options.ajax?br.calculateObjectValue(this,this.options.ajax,[w],null):(this._xhr&&4!==this._xhr.readyState&&this._xhr.abort(),this._xhr=fc["default"].ajax(w)),f}}}},{key:"initSearchText",value:function(){if(this.options.search&&(this.searchText="",""!==this.options.searchText)){var c=br.getSearchInput(this);c.val(this.options.searchText),this.onSearch({currentTarget:c,firedByInitSearchText:!0})}}},{key:"getCaret",value:function(){var c=this;this.$header.find("th").each(function(f,d){fc["default"](d).find(".sortable").removeClass("desc asc").addClass(fc["default"](d).data("field")===c.options.sortName?c.options.sortOrder:"both")})}},{key:"updateSelected",value:function(){var c=this.$selectItem.filter(":enabled").length&&this.$selectItem.filter(":enabled").length===this.$selectItem.filter(":enabled").filter(":checked").length;this.$selectAll.add(this.$selectAll_).prop("checked",c),this.$selectItem.each(function(d,f){fc["default"](f).closest("tr")[fc["default"](f).prop("checked")?"addClass":"removeClass"]("selected")})}},{key:"updateRows",value:function(){var c=this;this.$selectItem.each(function(f,d){c.data[fc["default"](d).data("index")][c.header.stateField]=fc["default"](d).prop("checked")})}},{key:"resetRows",value:function(){var d,f=fg(this.data);try{for(f.s();!(d=f.n()).done;){var c=d.value;this.$selectAll.prop("checked",!1),this.$selectItem.prop("checked",!1),this.header.stateField&&(c[this.header.stateField]=!1)}}catch(g){f.e(g)}finally{f.f()}this.initHiddenRows()}},{key:"trigger",value:function(e){for(var d,j,h="".concat(e,".bs.table"),c=arguments.length,f=Array(c>1?c-1:0),g=1;c>g;g++){f[g-1]=arguments[g]}(d=this.options)[a.EVENTS[h]].apply(d,[].concat(f,[this])),this.$el.trigger(fc["default"].Event(h,{sender:this}),f),(j=this.options).onAll.apply(j,[h].concat([].concat(f,[this]))),this.$el.trigger(fc["default"].Event("all.bs.table",{sender:this}),[h,f])}},{key:"resetHeader",value:function(){var c=this;clearTimeout(this.timeoutId_),this.timeoutId_=setTimeout(function(){return c.fitHeader()},this.$el.is(":hidden")?100:0)}},{key:"fitHeader",value:function(){var x=this;if(this.$el.is(":hidden")){return void (this.timeoutId_=setTimeout(function(){return x.fitHeader()},100))}var p=this.$tableBody.get(0),k=p.scrollWidth>p.clientWidth&&p.scrollHeight>p.clientHeight+this.$header.outerHeight()?br.getScrollBarWidth():0;this.$el.css("margin-top",-this.$header.outerHeight());var g=fc["default"](":focus");if(g.length>0){var f=g.parents("th");if(f.length>0){var v=f.attr("data-field");if(void 0!==v){var y=this.$header.find("[data-field='".concat(v,"']"));y.length>0&&y.find(":input").addClass("focus-temp")}}}this.$header_=this.$header.clone(!0,!0),this.$selectAll_=this.$header_.find('[name="btSelectAll"]'),this.$tableHeader.css("margin-right",k).find("table").css("width",this.$el.outerWidth()).html("").attr("class",this.$el.attr("class")).append(this.$header_),this.$tableLoading.css("width",this.$el.outerWidth());var d=fc["default"](".focus-temp:visible:eq(0)");d.length>0&&(d.focus(),this.$header.find(".focus-temp").removeClass("focus-temp")),this.$header.find("th[data-field]").each(function(h,c){x.$header_.find(br.sprintf('th[data-field="%s"]',fc["default"](c).data("field"))).data(fc["default"](c).data())});for(var j=this.getVisibleFields(),q=this.$header_.find("th"),m=this.$body.find(">tr:not(.no-records-found,.virtual-scroll-top)").eq(0);m.length&&m.find('>td[colspan]:not([colspan="1"])').length;){m=m.next()}var w=m.find("> *").length;m.find("> *").each(function(A,l){var C=fc["default"](l);if(br.hasDetailViewIcon(x.options)&&(0===A&&"right"!==x.options.detailViewAlign||A===w-1&&"right"===x.options.detailViewAlign)){var B=q.filter(".detail"),c=B.innerWidth()-B.find(".fht-cell").width();return void B.find(".fht-cell").width(C.innerWidth()-c)}var u=A-br.getDetailViewIndexOffset(x.options),z=x.$header_.find(br.sprintf('th[data-field="%s"]',j[u]));z.length>1&&(z=fc["default"](q[C[0].cellIndex]));var t=z.innerWidth()-z.find(".fht-cell").width();z.find(".fht-cell").width(C.innerWidth()-t)}),this.horizontalScroll(),this.trigger("post-header")}},{key:"initFooter",value:function(){if(this.options.showFooter&&!this.options.cardView){var s=this.getData(),H=[],D="";br.hasDetailViewIcon(this.options)&&(D='
    '),D&&"right"!==this.options.detailViewAlign&&H.push(D);var A,z=fg(this.columns);try{for(z.s();!(A=z.n()).done;){var L=A.value,v="",C="",J=[],E={},q=br.sprintf(' class="%s"',L["class"]);if(L.visible&&(!(this.footerData&&this.footerData.length>0)||L.field in this.footerData[0])){if(this.options.cardView&&!L.cardVisible){return}if(v=br.sprintf("text-align: %s; ",L.falign?L.falign:L.align),C=br.sprintf("vertical-align: %s; ",L.valign),E=br.calculateObjectValue(null,this.options.footerStyle,[L]),E&&E.css){for(var I=0,G=Object.entries(E.css);I0&&(B=this.footerData[0]["_".concat(L.field,"_colspan")]||0),B&&H.push(' colspan="'.concat(B,'" ')),H.push(">"),H.push('
    ');var j="";this.footerData&&this.footerData.length>0&&(j=this.footerData[0][L.field]||""),H.push(br.calculateObjectValue(L,L.footerFormatter,[s,j],j)),H.push("
    "),H.push('
    '),H.push("
    "),H.push("")}}}catch(k){z.e(k)}finally{z.f()}D&&"right"===this.options.detailViewAlign&&H.push(D),this.options.height||this.$tableFooter.length||(this.$el.append(""),this.$tableFooter=this.$el.find("tfoot")),this.$tableFooter.find("tr").length||this.$tableFooter.html("
    "),this.$tableFooter.find("tr").html(H.join("")),this.trigger("post-footer",this.$tableFooter)}}},{key:"fitFooter",value:function(){var f=this;if(this.$el.is(":hidden")){return void setTimeout(function(){return f.fitFooter()},100)}var g=this.$tableBody.get(0),d=g.scrollWidth>g.clientWidth&&g.scrollHeight>g.clientHeight+this.$header.outerHeight()?br.getScrollBarWidth():0;this.$tableFooter.css("margin-right",d).find("table").css("width",this.$el.outerWidth()).attr("class",this.$el.attr("class"));var j=this.$tableFooter.find("th"),h=this.$body.find(">tr:first-child:not(.no-records-found)");for(j.find(".fht-cell").width("auto");h.length&&h.find('>td[colspan]:not([colspan="1"])').length;){h=h.next()}var c=h.find("> *").length;h.find("> *").each(function(q,m){var t=fc["default"](m);if(br.hasDetailViewIcon(f.options)&&(0===q&&"left"===f.options.detailViewAlign||q===c-1&&"right"===f.options.detailViewAlign)){var n=j.filter(".detail"),p=n.innerWidth()-n.find(".fht-cell").width();return void n.find(".fht-cell").width(t.innerWidth()-p)}var k=j.eq(q),u=k.innerWidth()-k.find(".fht-cell").width();k.find(".fht-cell").width(t.innerWidth()-u)}),this.horizontalScroll()}},{key:"horizontalScroll",value:function(){var c=this;this.$tableBody.off("scroll").on("scroll",function(){var d=c.$tableBody.scrollLeft();c.options.showHeader&&c.options.height&&c.$tableHeader.scrollLeft(d),c.options.showFooter&&!c.options.cardView&&c.$tableFooter.scrollLeft(d),c.trigger("scroll-body",c.$tableBody)})}},{key:"getVisibleFields",value:function(){var f,g=[],d=fg(this.header.fields);try{for(d.s();!(f=d.n()).done;){var j=f.value,h=this.columns[this.fieldsColumnsIndex[j]];h&&h.visible&&g.push(j)}}catch(c){d.e(c)}finally{d.f()}return g}},{key:"initHiddenRows",value:function(){this.hiddenRows=[]}},{key:"getOptions",value:function(){var c=fc["default"].extend({},this.options);return delete c.data,fc["default"].extend(!0,{},c)}},{key:"refreshOptions",value:function(c){br.compareObjects(this.options,c,!0)||(this.options=fc["default"].extend(this.options,c),this.trigger("refresh-options",this.options),this.destroy(),this.init())}},{key:"getData",value:function(d){var f=this,c=this.options.data;if(!(this.searchText||this.options.customSearch||void 0!==this.options.sortName||this.enableCustomSort)&&br.isEmptyObject(this.filterColumns)&&br.isEmptyObject(this.filterColumnsPartial)||d&&d.unfiltered||(c=this.data),d&&d.useCurrentPage&&(c=c.slice(this.pageFrom-1,this.pageTo)),d&&!d.includeHiddenRows){var g=this.getHiddenRows();c=c.filter(function(e){return -1===br.findIndex(g,e)})}return d&&d.formatted&&c.forEach(function(k){for(var j=0,q=Object.entries(k);j=0;c--){var g=this.options.data[c];(g.hasOwnProperty(d.field)||"$index"===d.field)&&(!g.hasOwnProperty(d.field)&&"$index"===d.field&&d.values.includes(c)||d.values.includes(g[d.field]))&&(f++,this.options.data.splice(c,1))}f&&("server"===this.options.sidePagination&&(this.options.totalRows-=f,this.data=fp(this.options.data)),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0))}},{key:"removeAll",value:function(){this.options.data.length>0&&(this.options.data.splice(0,this.options.data.length),this.initSearch(),this.initPagination(),this.initBody(!0))}},{key:"insertRow",value:function(c){c.hasOwnProperty("index")&&c.hasOwnProperty("row")&&(this.options.data.splice(c.index,0,c.row),this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0))}},{key:"updateRow",value:function(f){var g,d=Array.isArray(f)?f:[f],j=fg(d);try{for(j.s();!(g=j.n()).done;){var h=g.value;h.hasOwnProperty("index")&&h.hasOwnProperty("row")&&(h.hasOwnProperty("replace")&&h.replace?this.options.data[h.index]=h.row:fc["default"].extend(this.options.data[h.index],h.row))}}catch(c){j.e(c)}finally{j.f()}this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)}},{key:"getRowByUniqueId",value:function(f){var j,d,l,k=this.options.uniqueId,c=this.options.data.length,g=f,h=null;for(j=c-1;j>=0;j--){if(d=this.options.data[j],d.hasOwnProperty(k)){l=d[k]}else{if(!d._data||!d._data.hasOwnProperty(k)){continue}l=d._data[k]}if("string"==typeof l?g=""+g:"number"==typeof l&&(+l===l&&l%1===0?g=parseInt(g):l===+l&&0!==l&&(g=parseFloat(g))),l===g){h=d;break}}return h}},{key:"updateByUniqueId",value:function(f){var h,d=Array.isArray(f)?f:[f],k=fg(d);try{for(k.s();!(h=k.n()).done;){var j=h.value;if(j.hasOwnProperty("id")&&j.hasOwnProperty("row")){var c=this.options.data.indexOf(this.getRowByUniqueId(j.id));-1!==c&&(j.hasOwnProperty("replace")&&j.replace?this.options.data[c]=j.row:fc["default"].extend(this.options.data[c],j.row))}}}catch(g){k.e(g)}finally{k.f()}this.initSearch(),this.initPagination(),this.initSort(),this.initBody(!0)}},{key:"removeByUniqueId",value:function(d){var f=this.options.data.length,c=this.getRowByUniqueId(d);c&&this.options.data.splice(this.options.data.indexOf(c),1),f!==this.options.data.length&&("server"===this.options.sidePagination&&(this.options.totalRows-=1,this.data=fp(this.options.data)),this.initSearch(),this.initPagination(),this.initBody(!0))}},{key:"updateCell",value:function(c){c.hasOwnProperty("index")&&c.hasOwnProperty("field")&&c.hasOwnProperty("value")&&(this.data[c.index][c.field]=c.value,c.reinit!==!1&&(this.initSort(),this.initBody(!0)))}},{key:"updateCellByUniqueId",value:function(d){var f=this,c=Array.isArray(d)?d:[d];c.forEach(function(h){var g=h.id,k=h.field,j=h.value,e=f.options.data.indexOf(f.getRowByUniqueId(g));-1!==e&&(f.options.data[e][k]=j)}),d.reinit!==!1&&(this.initSort(),this.initBody(!0))}},{key:"showRow",value:function(c){this._toggleRow(c,!0)}},{key:"hideRow",value:function(c){this._toggleRow(c,!1)}},{key:"_toggleRow",value:function(d,f){var c;if(d.hasOwnProperty("index")?c=this.getData()[d.index]:d.hasOwnProperty("uniqueId")&&(c=this.getRowByUniqueId(d.uniqueId)),c){var g=br.findIndex(this.hiddenRows,c);f||-1!==g?f&&g>-1&&this.hiddenRows.splice(g,1):this.hiddenRows.push(c),this.initBody(!0),this.initPagination()}}},{key:"getHiddenRows",value:function(f){if(f){return this.initHiddenRows(),this.initBody(!0),void this.initPagination()}var h,d=this.getData(),k=[],j=fg(d);try{for(j.s();!(h=j.n()).done;){var c=h.value;this.hiddenRows.includes(c)&&k.push(c)}}catch(g){j.e(g)}finally{j.f()}return this.hiddenRows=k,k}},{key:"showColumn",value:function(d){var f=this,c=Array.isArray(d)?d:[d];c.forEach(function(e){f._toggleColumn(f.fieldsColumnsIndex[e],!0,!0)})}},{key:"hideColumn",value:function(d){var f=this,c=Array.isArray(d)?d:[d];c.forEach(function(e){f._toggleColumn(f.fieldsColumnsIndex[e],!1,!0)})}},{key:"_toggleColumn",value:function(d,f,c){if(-1!==d&&this.columns[d].visible!==f&&(this.columns[d].visible=f,this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns)){var g=this.$toolbar.find('.keep-open input:not(".toggle-all")').prop("disabled",!1);c&&g.filter(br.sprintf('[value="%s"]',d)).prop("checked",f),g.filter(":checked").length<=this.options.minimumCountColumns&&g.filter(":checked").prop("disabled",!0)}}},{key:"getVisibleColumns",value:function(){var c=this;return this.columns.filter(function(d){return d.visible&&!c.isSelectionColumn(d)})}},{key:"getHiddenColumns",value:function(){return this.columns.filter(function(c){var d=c.visible;return !d})}},{key:"isSelectionColumn",value:function(c){return c.radio||c.checkbox}},{key:"showAllColumns",value:function(){this._toggleAllColumns(!0)}},{key:"hideAllColumns",value:function(){this._toggleAllColumns(!1)}},{key:"_toggleAllColumns",value:function(f){var h,d=this,k=fg(this.columns.slice().reverse());try{for(k.s();!(h=k.n()).done;){var j=h.value;if(j.switchable){if(!f&&this.options.showColumns&&this.getVisibleColumns().length===this.options.minimumCountColumns){continue}j.visible=f}}}catch(c){k.e(c)}finally{k.f()}if(this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns){var g=this.$toolbar.find('.keep-open input[type="checkbox"]:not(".toggle-all")').prop("disabled",!1);f?g.prop("checked",f):g.get().reverse().forEach(function(i){g.filter(":checked").length>d.options.minimumCountColumns&&fc["default"](i).prop("checked",f)}),g.filter(":checked").length<=this.options.minimumCountColumns&&g.filter(":checked").prop("disabled",!0)}}},{key:"mergeCells",value:function(m){var j,h,f=m.index,d=this.getVisibleFields().indexOf(m.field),k=m.rowspan||1,p=m.colspan||1,c=this.$body.find(">tr");d+=br.getDetailViewIndexOffset(this.options);var g=c.eq(f).find(">td").eq(d);if(!(0>f||0>d||f>=this.data.length)){for(j=f;f+k>j;j++){for(h=d;d+p>h;h++){c.eq(j).find(">td").eq(h).hide()}}g.attr("rowspan",k).attr("colspan",p).show()}}},{key:"checkAll",value:function(){this._toggleCheckAll(!0)}},{key:"uncheckAll",value:function(){this._toggleCheckAll(!1)}},{key:"_toggleCheckAll",value:function(d){var f=this.getSelections();this.$selectAll.add(this.$selectAll_).prop("checked",d),this.$selectItem.filter(":enabled").prop("checked",d),this.updateRows(),this.updateSelected();var c=this.getSelections();return d?void this.trigger("check-all",c,f):void this.trigger("uncheck-all",c,f)}},{key:"checkInvert",value:function(){var c=this.$selectItem.filter(":enabled"),d=c.filter(":checked");c.each(function(f,g){fc["default"](g).prop("checked",!fc["default"](g).prop("checked"))}),this.updateRows(),this.updateSelected(),this.trigger("uncheck-some",d),d=this.getSelections(),this.trigger("check-some",d)}},{key:"check",value:function(c){this._toggleCheck(!0,c)}},{key:"uncheck",value:function(c){this._toggleCheck(!1,c)}},{key:"_toggleCheck",value:function(A,v){var p=this.$selectItem.filter('[data-index="'.concat(v,'"]')),k=this.data[v];if(p.is(":radio")||this.options.singleSelect||this.options.multipleSelectRow&&!this.multipleSelectRowCtrlKey&&!this.multipleSelectRowShiftKey){var j,y=fg(this.options.data);try{for(y.s();!(j=y.n()).done;){var g=j.value;g[this.header.stateField]=!1}}catch(m){y.e(m)}finally{y.f()}this.$selectItem.filter(":checked").not(p).prop("checked",!1)}if(k[this.header.stateField]=A,this.options.multipleSelectRow){if(this.multipleSelectRowShiftKey&&this.multipleSelectRowLastSelectedIndex>=0){for(var x=this.multipleSelectRowLastSelectedIndexs;s++){this.data[s][this.header.stateField]=!0,this.$selectItem.filter('[data-index="'.concat(s,'"]')).prop("checked",!0)}}this.multipleSelectRowCtrlKey=!1,this.multipleSelectRowShiftKey=!1,this.multipleSelectRowLastSelectedIndex=A?v:-1}p.prop("checked",A),this.updateSelected(),this.trigger(A?"check":"uncheck",this.data[v],p)}},{key:"checkBy",value:function(c){this._toggleCheckBy(!0,c)}},{key:"uncheckBy",value:function(c){this._toggleCheckBy(!1,c)}},{key:"_toggleCheckBy",value:function(d,f){var c=this;if(f.hasOwnProperty("field")&&f.hasOwnProperty("values")){var g=[];this.data.forEach(function(i,e){if(!i.hasOwnProperty(f.field)){return !1}if(f.values.includes(i[f.field])){var h=c.$selectItem.filter(":enabled").filter(br.sprintf('[data-index="%s"]',e));if(h=d?h.not(":checked"):h.filter(":checked"),!h.length){return}h.prop("checked",d),i[c.header.stateField]=d,g.push(i),c.trigger(d?"check":"uncheck",i,h)}}),this.updateSelected(),this.trigger(d?"check-some":"uncheck-some",g)}}},{key:"refresh",value:function(c){c&&c.url&&(this.options.url=c.url),c&&c.pageNumber&&(this.options.pageNumber=c.pageNumber),c&&c.pageSize&&(this.options.pageSize=c.pageSize),table.rememberSelecteds={},table.rememberSelectedIds={},this.trigger("refresh",this.initServer(c&&c.silent,c&&c.query,c&&c.url))}},{key:"destroy",value:function(){this.$el.insertBefore(this.$container),fc["default"](this.options.toolbar).insertBefore(this.$el),this.$container.next().remove(),this.$container.remove(),this.$el.html(this.$el_.html()).css("margin-top","0").attr("class",this.$el_.attr("class")||"")}},{key:"resetView",value:function(f){var j=0;if(f&&f.height&&(this.options.height=f.height),this.$selectAll.prop("checked",this.$selectItem.length>0&&this.$selectItem.length===this.$selectItem.filter(":checked").length),this.$tableContainer.toggleClass("has-card-view",this.options.cardView),!this.options.cardView&&this.options.showHeader&&this.options.height?(this.$tableHeader.show(),this.resetHeader(),j+=this.$header.outerHeight(!0)+1):(this.$tableHeader.hide(),this.trigger("post-header")),!this.options.cardView&&this.options.showFooter&&(this.$tableFooter.show(),this.fitFooter(),this.options.height&&(j+=this.$tableFooter.outerHeight(!0))),this.$container.hasClass("fullscreen")){this.$tableContainer.css("height",""),this.$tableContainer.css("width","")}else{if(this.options.height){this.$tableBorder&&(this.$tableBorder.css("width",""),this.$tableBorder.css("height",""));var d=this.$toolbar.outerHeight(!0),l=this.$pagination.outerHeight(!0),k=this.options.height-d-l,c=this.$tableBody.find(">table"),g=c.outerHeight();if(this.$tableContainer.css("height","".concat(k,"px")),this.$tableBorder&&c.is(":visible")){var h=k-g-2;this.$tableBody[0].scrollWidth-this.$tableBody.innerWidth()&&(h-=br.getScrollBarWidth()),this.$tableBorder.css("width","".concat(c.outerWidth(),"px")),this.$tableBorder.css("height","".concat(h,"px"))}}}this.options.cardView?(this.$el.css("margin-top","0"),this.$tableContainer.css("padding-bottom","0"),this.$tableFooter.hide()):(this.getCaret(),this.$tableContainer.css("padding-bottom","".concat(j,"px"))),this.trigger("reset-view")}},{key:"showLoading",value:function(){this.$tableLoading.toggleClass("open",!0);var c=this.options.loadingFontSize;"auto"===this.options.loadingFontSize&&(c=0.04*this.$tableLoading.width(),c=Math.max(12,c),c=Math.min(32,c),c="".concat(c,"px")),this.$tableLoading.find(".loading-text").css("font-size",c)}},{key:"hideLoading",value:function(){this.$tableLoading.toggleClass("open",!1)}},{key:"toggleShowSearch",value:function(){this.$el.parents(".select-table").siblings().slideToggle()}},{key:"togglePagination",value:function(){this.options.pagination=!this.options.pagination;var c=this.options.showButtonIcons?this.options.pagination?this.options.icons.paginationSwitchDown:this.options.icons.paginationSwitchUp:"",d=this.options.showButtonText?this.options.pagination?this.options.formatPaginationSwitchUp():this.options.formatPaginationSwitchDown():"";this.$toolbar.find('button[name="paginationSwitch"]').html("".concat(br.sprintf(this.constants.html.icon,this.options.iconsPrefix,c)," ").concat(d)),this.updatePagination()}},{key:"toggleFullscreen",value:function(){this.$el.closest(".bootstrap-table").toggleClass("fullscreen"),this.resetView()}},{key:"toggleView",value:function(){this.options.cardView=!this.options.cardView,this.initHeader();var c=this.options.showButtonIcons?this.options.cardView?this.options.icons.toggleOn:this.options.icons.toggleOff:"",d=this.options.showButtonText?this.options.cardView?this.options.formatToggleOff():this.options.formatToggleOn():"";this.$toolbar.find('button[name="toggle"]').html("".concat(br.sprintf(this.constants.html.icon,this.options.iconsPrefix,c)," ").concat(d)),this.initBody(),this.trigger("toggle",this.options.cardView)}},{key:"resetSearch",value:function(c){var d=br.getSearchInput(this);d.val(c||""),this.onSearch({currentTarget:d})}},{key:"filterBy",value:function(c,d){this.filterOptions=br.isEmptyObject(d)?this.options.filterOptions:fc["default"].extend(this.options.filterOptions,d),this.filterColumns=br.isEmptyObject(c)?{}:c,this.options.pageNumber=1,this.initSearch(),this.updatePagination()}},{key:"scrollTo",value:function b(c){var d={unit:"px",value:0};"object"===fD(c)?d=Object.assign(d,c):"string"==typeof c&&"bottom"===c?d.value=this.$tableBody[0].scrollHeight:("string"==typeof c||"number"==typeof c)&&(d.value=c);var f=d.value;"rows"===d.unit&&(f=0,this.$body.find("> tr:lt(".concat(d.value,")")).each(function(g,h){f+=fc["default"](h).outerHeight(!0)})),this.$tableBody.scrollTop(f)}},{key:"getScrollPosition",value:function(){return this.$tableBody.scrollTop()}},{key:"selectPage",value:function(c){c>0&&c<=this.options.totalPages&&(this.options.pageNumber=c,this.updatePagination())}},{key:"prevPage",value:function(){this.options.pageNumber>1&&(this.options.pageNumber--,this.updatePagination())}},{key:"nextPage",value:function(){this.options.pageNumber tr[data-index="%s"]',d));c.next().is("tr.detail-view")?this.collapseRow(d):this.expandRow(d,f),this.resetView()}},{key:"expandRow",value:function(f,h){var d=this.data[f],k=this.$body.find(br.sprintf('> tr[data-index="%s"][data-has-detail-view]',f));if(!k.next().is("tr.detail-view")){this.options.detailViewIcon&&k.find("a.detail-icon").html(br.sprintf(this.constants.html.icon,this.options.iconsPrefix,this.options.icons.detailClose)),k.after(br.sprintf('',k.children("td").length));var j=k.next().find("td"),c=h||this.options.detailFormatter,g=br.calculateObjectValue(this.options,c,[f,d,j],"");1===j.length&&j.append(g),this.trigger("expand-row",f,d,j)}}},{key:"expandRowByUniqueId",value:function(c){var d=this.getRowByUniqueId(c);d&&this.expandRow(this.data.indexOf(d))}},{key:"collapseRow",value:function(d){var f=this.data[d],c=this.$body.find(br.sprintf('> tr[data-index="%s"][data-has-detail-view]',d));c.next().is("tr.detail-view")&&(this.options.detailViewIcon&&c.find("a.detail-icon").html(br.sprintf(this.constants.html.icon,this.options.iconsPrefix,this.options.icons.detailOpen)),this.trigger("collapse-row",d,f,c.next()),c.next().remove())}},{key:"collapseRowByUniqueId",value:function(c){var d=this.getRowByUniqueId(c);d&&this.collapseRow(this.data.indexOf(d))}},{key:"expandAllRows",value:function(){for(var c=this.$body.find("> tr[data-index][data-has-detail-view]"),d=0;d tr[data-index][data-has-detail-view]"),d=0;d1?d-1:0),f=1;d>f;f++){g[f-1]=arguments[f]}var b;return this.each(function(j,k){var h=fc["default"](k).data("bootstrap.table"),i=fc["default"].extend({},d5.DEFAULTS,fc["default"](k).data(),"object"===fD(c)&&c);if("string"==typeof c){var a;if(!fb.METHODS.includes(c)){throw Error("Unknown method: ".concat(c))}if(!h){return}b=(a=h)[c].apply(a,g),"destroy"===c&&fc["default"](k).removeData("bootstrap.table")}h||(h=new fc["default"].BootstrapTable(k,i),fc["default"](k).data("bootstrap.table",h),h.init())}),void 0===b?this:b},fc["default"].fn.bootstrapTable.Constructor=d5,fc["default"].fn.bootstrapTable.theme=fb.THEME,fc["default"].fn.bootstrapTable.VERSION=fb.VERSION,fc["default"].fn.bootstrapTable.defaults=d5.DEFAULTS,fc["default"].fn.bootstrapTable.columnDefaults=d5.COLUMN_DEFAULTS,fc["default"].fn.bootstrapTable.events=d5.EVENTS,fc["default"].fn.bootstrapTable.locales=d5.LOCALES,fc["default"].fn.bootstrapTable.methods=d5.METHODS,fc["default"].fn.bootstrapTable.utils=br,fc["default"](function(){fc["default"]('[data-toggle="table"]').bootstrapTable()}),d5});var TABLE_EVENTS="all.bs.table click-cell.bs.table dbl-click-cell.bs.table click-row.bs.table dbl-click-row.bs.table sort.bs.table check.bs.table uncheck.bs.table onUncheck check-all.bs.table uncheck-all.bs.table check-some.bs.table uncheck-some.bs.table load-success.bs.table load-error.bs.table column-switch.bs.table page-change.bs.table search.bs.table toggle.bs.table show-search.bs.table expand-row.bs.table collapse-row.bs.table refresh-options.bs.table reset-view.bs.table refresh.bs.table",firstLoadTable=[],union=function(a,b){return $.isPlainObject(b)?addRememberRow(a,b):$.isArray(b)?$.each(b,function(d,c){$.isPlainObject(c)?addRememberRow(a,c):-1==$.inArray(c,a)&&(a[a.length]=c)}):-1==$.inArray(b,a)&&(a[a.length]=b),a},difference=function(b,c){if($.isPlainObject(c)){removeRememberRow(b,c)}else{if($.isArray(c)){$.each(c,function(f,d){if($.isPlainObject(d)){removeRememberRow(b,d)}else{var g=$.inArray(d,b);-1!=g&&b.splice(g,1)}})}else{var a=$.inArray(c,b);-1!=a&&b.splice(a,1)}}return b},_={union:union,difference:difference}; \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/auto-refresh/bootstrap-table-auto-refresh.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/auto-refresh/bootstrap-table-auto-refresh.js new file mode 100644 index 000000000..662e81677 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/auto-refresh/bootstrap-table-auto-refresh.js @@ -0,0 +1,93 @@ +/** + * @author: Alec Fenichel + * @webSite: https://fenichelar.com + * @update: zhixin wen + */ + +const Utils = $.fn.bootstrapTable.utils + +$.extend($.fn.bootstrapTable.defaults, { + autoRefresh: false, + autoRefreshInterval: 60, + autoRefreshSilent: true, + autoRefreshStatus: true, + autoRefreshFunction: null +}) + +$.extend($.fn.bootstrapTable.defaults.icons, { + autoRefresh: { + bootstrap3: 'glyphicon-time icon-time', + materialize: 'access_time', + 'bootstrap-table': 'icon-clock' + }[$.fn.bootstrapTable.theme] || 'fa-clock' +}) + +$.extend($.fn.bootstrapTable.locales, { + formatAutoRefresh () { + return 'Auto Refresh' + } +}) + +$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales) + +$.BootstrapTable = class extends $.BootstrapTable { + init (...args) { + super.init(...args) + + if (this.options.autoRefresh && this.options.autoRefreshStatus) { + this.setupRefreshInterval() + } + } + + initToolbar (...args) { + if (this.options.autoRefresh) { + this.buttons = Object.assign(this.buttons, { + autoRefresh: { + html: ` + + `, + event: this.toggleAutoRefresh + } + }) + } + + super.initToolbar(...args) + } + + toggleAutoRefresh () { + if (this.options.autoRefresh) { + if (this.options.autoRefreshStatus) { + clearInterval(this.options.autoRefreshFunction) + this.$toolbar.find('>.columns .auto-refresh') + .removeClass(this.constants.classes.buttonActive) + } else { + this.setupRefreshInterval() + this.$toolbar.find('>.columns .auto-refresh') + .addClass(this.constants.classes.buttonActive) + } + this.options.autoRefreshStatus = !this.options.autoRefreshStatus + } + } + + destroy () { + if (this.options.autoRefresh && this.options.autoRefreshStatus) { + clearInterval(this.options.autoRefreshFunction) + } + + super.destroy() + } + + setupRefreshInterval () { + this.options.autoRefreshFunction = setInterval(() => { + if (!this.options.autoRefresh || !this.options.autoRefreshStatus) { + return + } + this.refresh({ silent: this.options.autoRefreshSilent }) + }, this.options.autoRefreshInterval * 1000) + } +} diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.js new file mode 100644 index 000000000..d5ee88737 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/columns/bootstrap-table-fixed-columns.js @@ -0,0 +1,404 @@ +/** + * @author zhixin wen + */ + +const Utils = $.fn.bootstrapTable.utils + +// Reasonable defaults +const PIXEL_STEP = 10 +const LINE_HEIGHT = 40 +const PAGE_HEIGHT = 800 + +function normalizeWheel (event) { + let sX = 0 // spinX + let sY = 0 // spinY + let pX = 0 // pixelX + let pY = 0 // pixelY + + // Legacy + if ('detail' in event) { sY = event.detail } + if ('wheelDelta' in event) { sY = -event.wheelDelta / 120 } + if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120 } + if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120 } + + // side scrolling on FF with DOMMouseScroll + if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) { + sX = sY + sY = 0 + } + + pX = sX * PIXEL_STEP + pY = sY * PIXEL_STEP + + if ('deltaY' in event) { pY = event.deltaY } + if ('deltaX' in event) { pX = event.deltaX } + + if ((pX || pY) && event.deltaMode) { + if (event.deltaMode === 1) { // delta in LINE units + pX *= LINE_HEIGHT + pY *= LINE_HEIGHT + } else { // delta in PAGE units + pX *= PAGE_HEIGHT + pY *= PAGE_HEIGHT + } + } + + // Fall-back if spin cannot be determined + if (pX && !sX) { sX = (pX < 1) ? -1 : 1 } + if (pY && !sY) { sY = (pY < 1) ? -1 : 1 } + + return { + spinX: sX, + spinY: sY, + pixelX: pX, + pixelY: pY + } +} + +$.extend($.fn.bootstrapTable.defaults, { + fixedColumns: false, + fixedNumber: 0, + fixedRightNumber: 0 +}) + +$.BootstrapTable = class extends $.BootstrapTable { + + fixedColumnsSupported () { + return this.options.fixedColumns && + !this.options.detailView && + !this.options.cardView + } + + initContainer () { + super.initContainer() + + if (!this.fixedColumnsSupported()) { + return + } + + if (this.options.fixedNumber) { + this.$tableContainer.append('
    ') + this.$fixedColumns = this.$tableContainer.find('.fixed-columns') + } + + if (this.options.fixedRightNumber) { + this.$tableContainer.append('
    ') + this.$fixedColumnsRight = this.$tableContainer.find('.fixed-columns-right') + } + } + + initBody (...args) { + super.initBody(...args) + + if (this.$fixedColumns && this.$fixedColumns.length) { + this.$fixedColumns.toggle(this.fixedColumnsSupported()) + } + if (this.$fixedColumnsRight && this.$fixedColumnsRight.length) { + this.$fixedColumnsRight.toggle(this.fixedColumnsSupported()) + } + + if (!this.fixedColumnsSupported()) { + return + } + + if (this.options.showHeader && this.options.height) { + return + } + + this.initFixedColumnsBody() + this.initFixedColumnsEvents() + } + + trigger (...args) { + super.trigger(...args) + + if (!this.fixedColumnsSupported()) { + return + } + + if (args[0] === 'post-header') { + this.initFixedColumnsHeader() + } else if (args[0] === 'scroll-body') { + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedBody.scrollTop(this.$tableBody.scrollTop()) + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + this.$fixedBodyRight.scrollTop(this.$tableBody.scrollTop()) + } + } + } + + updateSelected () { + super.updateSelected() + + if (!this.fixedColumnsSupported()) { + return + } + + this.$tableBody.find('tr').each((i, el) => { + const $el = $(el) + const index = $el.data('index') + const classes = $el.attr('class') + + const inputSelector = `[name="${this.options.selectItemName}"]` + const $input = $el.find(inputSelector) + + if (typeof index === undefined) { + return + } + + const updateFixedBody = ($fixedHeader, $fixedBody) => { + const $tr = $fixedBody.find(`tr[data-index="${index}"]`) + + $tr.attr('class', classes) + + if ($input.length) { + $tr.find(inputSelector).prop('checked', $input.prop('checked')) + } + + if (this.$selectAll.length) { + $fixedHeader.add($fixedBody) + .find('[name="btSelectAll"]') + .prop('checked', this.$selectAll.prop('checked')) + } + } + + if (this.$fixedBody && this.options.fixedNumber) { + updateFixedBody(this.$fixedHeader, this.$fixedBody) + } + + if (this.$fixedBodyRight && this.options.fixedRightNumber) { + updateFixedBody(this.$fixedHeaderRight, this.$fixedBodyRight) + } + }) + } + + hideLoading () { + super.hideLoading() + + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedColumns.find('.fixed-table-loading').hide() + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + this.$fixedColumnsRight.find('.fixed-table-loading').hide() + } + } + + initFixedColumnsHeader () { + if (this.options.height) { + this.needFixedColumns = this.$tableHeader.outerWidth(true) < this.$tableHeader.find('table').outerWidth(true) + } else { + this.needFixedColumns = this.$tableBody.outerWidth(true) < this.$tableBody.find('table').outerWidth(true) + } + + const initFixedHeader = ($fixedColumns, isRight) => { + $fixedColumns.find('.fixed-table-header').remove() + $fixedColumns.append(this.$tableHeader.clone(true)) + + $fixedColumns.css({ + width: this.getFixedColumnsWidth(isRight) + }) + return $fixedColumns.find('.fixed-table-header') + } + + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedHeader = initFixedHeader(this.$fixedColumns) + this.$fixedHeader.css('margin-right', '') + } else if (this.$fixedColumns) { + this.$fixedColumns.html('').css('width', '') + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + this.$fixedHeaderRight = initFixedHeader(this.$fixedColumnsRight, true) + this.$fixedHeaderRight.scrollLeft(this.$fixedHeaderRight.find('table').width()) + } else if (this.$fixedColumnsRight) { + this.$fixedColumnsRight.html('').css('width', '') + } + + this.initFixedColumnsBody() + this.initFixedColumnsEvents() + } + + initFixedColumnsBody () { + const initFixedBody = ($fixedColumns, $fixedHeader) => { + $fixedColumns.find('.fixed-table-body').remove() + $fixedColumns.append(this.$tableBody.clone(true)) + + const $fixedBody = $fixedColumns.find('.fixed-table-body') + + const tableBody = this.$tableBody.get(0) + const scrollHeight = tableBody.scrollWidth > tableBody.clientWidth ? + Utils.getScrollBarWidth() : 0 + const height = this.$tableContainer.outerHeight(true) - scrollHeight - 1 + + $fixedColumns.css({ + height + }) + + $fixedBody.css({ + height: height - $fixedHeader.height() + }) + + return $fixedBody + } + + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedBody = initFixedBody(this.$fixedColumns, this.$fixedHeader) + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + this.$fixedBodyRight = initFixedBody(this.$fixedColumnsRight, this.$fixedHeaderRight) + this.$fixedBodyRight.scrollLeft(this.$fixedBodyRight.find('table').width()) + this.$fixedBodyRight.css('overflow-y', this.options.height ? 'auto' : 'hidden') + } + } + + getFixedColumnsWidth (isRight) { + let visibleFields = this.getVisibleFields() + let width = 0 + let fixedNumber = this.options.fixedNumber + let marginRight = 0 + + if (isRight) { + visibleFields = visibleFields.reverse() + fixedNumber = this.options.fixedRightNumber + marginRight = parseInt(this.$tableHeader.css('margin-right'), 10) + } + + for (let i = 0; i < fixedNumber; i++) { + width += this.$header.find(`th[data-field="${visibleFields[i]}"]`).outerWidth(true) + } + + return width + marginRight + 1 + } + + initFixedColumnsEvents () { + const toggleHover = (e, toggle) => { + const tr = `tr[data-index="${$(e.currentTarget).data('index')}"]` + let $trs = this.$tableBody.find(tr) + + if (this.$fixedBody) { + $trs = $trs.add(this.$fixedBody.find(tr)) + } + if (this.$fixedBodyRight) { + $trs = $trs.add(this.$fixedBodyRight.find(tr)) + } + + $trs.css('background-color', toggle ? $(e.currentTarget).css('background-color') : '') + } + + this.$tableBody.find('tr').hover(e => { + toggleHover(e, true) + }, e => { + toggleHover(e, false) + }) + + const isFirefox = typeof navigator !== 'undefined' && + navigator.userAgent.toLowerCase().indexOf('firefox') > -1 + const mousewheel = isFirefox ? 'DOMMouseScroll' : 'mousewheel' + const updateScroll = (e, fixedBody) => { + const normalized = normalizeWheel(e) + const deltaY = Math.ceil(normalized.pixelY) + const top = this.$tableBody.scrollTop() + deltaY + + if ( + deltaY < 0 && top > 0 || + deltaY > 0 && top < fixedBody.scrollHeight - fixedBody.clientHeight + ) { + e.preventDefault() + } + + this.$tableBody.scrollTop(top) + if (this.$fixedBody) { + this.$fixedBody.scrollTop(top) + } + if (this.$fixedBodyRight) { + this.$fixedBodyRight.scrollTop(top) + } + } + + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedBody.find('tr').hover(e => { + toggleHover(e, true) + }, e => { + toggleHover(e, false) + }) + + this.$fixedBody[0].addEventListener(mousewheel, e => { + updateScroll(e, this.$fixedBody[0]) + }) + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + this.$fixedBodyRight.find('tr').hover(e => { + toggleHover(e, true) + }, e => { + toggleHover(e, false) + }) + + this.$fixedBodyRight.off('scroll').on('scroll', () => { + const top = this.$fixedBodyRight.scrollTop() + + this.$tableBody.scrollTop(top) + if (this.$fixedBody) { + this.$fixedBody.scrollTop(top) + } + }) + } + + if (this.options.filterControl) { + $(this.$fixedColumns).off('keyup change').on('keyup change', e => { + const $target = $(e.target) + const value = $target.val() + const field = $target.parents('th').data('field') + const $coreTh = this.$header.find(`th[data-field="${field}"]`) + + if ($target.is('input')) { + $coreTh.find('input').val(value) + } else if ($target.is('select')) { + const $select = $coreTh.find('select') + + $select.find('option[selected]').removeAttr('selected') + $select.find(`option[value="${value}"]`).attr('selected', true) + } + + this.triggerSearch() + }) + } + } + + renderStickyHeader () { + if (!this.options.stickyHeader) { + return + } + + this.$stickyContainer = this.$container.find('.sticky-header-container') + super.renderStickyHeader() + + if (this.needFixedColumns && this.options.fixedNumber) { + this.$fixedColumns.css('z-index', 101) + .find('.sticky-header-container') + .css('right', '') + .width(this.$fixedColumns.outerWidth()) + } + + if (this.needFixedColumns && this.options.fixedRightNumber) { + const $stickyHeaderContainerRight = this.$fixedColumnsRight.find('.sticky-header-container') + + this.$fixedColumnsRight.css('z-index', 101) + $stickyHeaderContainerRight.css('left', '') + .scrollLeft($stickyHeaderContainerRight.find('.table').outerWidth()) + .width(this.$fixedColumnsRight.outerWidth()) + } + } + + matchPositionX () { + if (!this.options.stickyHeader) { + return + } + + this.$stickyContainer.eq(0).scrollLeft(this.$tableBody.scrollLeft()) + } +} diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js new file mode 100644 index 000000000..a33bd170a --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/custom-view/bootstrap-table-custom-view.js @@ -0,0 +1,108 @@ +/** + * @author zhixin wen + */ + +const Utils = $.fn.bootstrapTable.utils + +$.extend($.fn.bootstrapTable.defaults, { + customView: false, + showCustomView: false, + showCustomViewButton: false +}) + +$.extend($.fn.bootstrapTable.defaults.icons, { + customView: { + bootstrap3: 'glyphicon glyphicon-eye-open', + bootstrap5: 'bi-eye', + bootstrap4: 'fa fa-eye', + semantic: 'fa fa-eye', + foundation: 'fa fa-eye', + bulma: 'fa fa-eye', + materialize: 'remove_red_eye' + }[$.fn.bootstrapTable.theme] || 'fa-eye' +}) + +$.extend($.fn.bootstrapTable.defaults, { + onCustomViewPostBody () { + return false + }, + onCustomViewPreBody () { + return false + } +}) + +$.extend($.fn.bootstrapTable.locales, { + formatToggleCustomView () { + return 'Toggle custom view' + } +}) +$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales) + +$.fn.bootstrapTable.methods.push('toggleCustomView') + +$.extend($.fn.bootstrapTable.Constructor.EVENTS, { + 'custom-view-post-body.bs.table': 'onCustomViewPostBody', + 'custom-view-pre-body.bs.table': 'onCustomViewPreBody' +}) + +$.BootstrapTable = class extends $.BootstrapTable { + + init () { + this.showCustomView = this.options.showCustomView + + super.init() + } + + initToolbar (...args) { + if (this.options.customView && this.options.showCustomViewButton) { + this.buttons = Object.assign(this.buttons, { + customView: { + text: this.options.formatToggleCustomView(), + icon: this.options.icons.customView, + event: this.toggleCustomView, + attributes: { + 'aria-label': this.options.formatToggleCustomView(), + title: this.options.formatToggleCustomView() + } + } + }) + } + + super.initToolbar(...args) + } + + initBody () { + super.initBody() + + if (!this.options.customView) { + return + } + + const $table = this.$el + const $customViewContainer = this.$container.find('.fixed-table-custom-view') + + $table.hide() + $customViewContainer.hide() + if (!this.options.customView || !this.showCustomView) { + $table.show() + return + } + + const data = this.getData().slice(this.pageFrom - 1, this.pageTo) + const value = Utils.calculateObjectValue(this, this.options.customView, [data], '') + + this.trigger('custom-view-pre-body', data, value) + if ($customViewContainer.length === 1) { + $customViewContainer.show().html(value) + } else { + this.$tableBody.after(`
    ${value}
    `) + } + + this.trigger('custom-view-post-body', data, value) + } + + toggleCustomView () { + this.showCustomView = !this.showCustomView + this.initBody() + } +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.css b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.css new file mode 100644 index 000000000..7cd449720 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.css @@ -0,0 +1,663 @@ +/*! X-editable - v1.5.1 +* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery +* http://github.com/vitalets/x-editable +* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ +.editableform { + margin-bottom: 0; /* overwrites bootstrap margin */ +} + +.editableform .control-group { + margin-bottom: 0; /* overwrites bootstrap margin */ + white-space: nowrap; /* prevent wrapping buttons on new line */ + line-height: 20px; /* overwriting bootstrap line-height. See #133 */ +} + +/* + BS3 width:1005 for inputs breaks editable form in popup + See: https://github.com/vitalets/x-editable/issues/393 +*/ +.editableform .form-control { + width: auto; +} + +.editable-buttons { + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + vertical-align: top; + margin-left: 7px; + /* inline-block emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-buttons.editable-buttons-bottom { + display: block; + margin-top: 7px; + margin-left: 0; +} + +.editable-input { + vertical-align: top; + display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ + width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ + white-space: normal; /* reset white-space decalred in parent*/ + /* display-inline emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-buttons .editable-cancel { + margin-left: 7px; +} + +/*for jquery-ui buttons need set height to look more pretty*/ +.editable-buttons button.ui-button-icon-only { + height: 24px; + width: 30px; +} + +.editableform-loading { + background: url('loading.gif') center center no-repeat; + height: 25px; + width: auto; + min-width: 25px; +} + +.editable-inline .editableform-loading { + background-position: left 5px; +} + + .editable-error-block { + max-width: 300px; + margin: 5px 0 0 0; + width: auto; + white-space: normal; +} + +/*add padding for jquery ui*/ +.editable-error-block.ui-state-error { + padding: 3px; +} + +.editable-error { + color: red; +} + +/* ---- For specific types ---- */ + +.editableform .editable-date { + padding: 0; + margin: 0; + float: left; +} + +/* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */ +.editable-inline .add-on .icon-th { + margin-top: 3px; + margin-left: 1px; +} + + +/* checklist vertical alignment */ +.editable-checklist label input[type="checkbox"], +.editable-checklist label span { + vertical-align: middle; + margin: 0; +} + +.editable-checklist label { + white-space: nowrap; +} + +/* set exact width of textarea to fit buttons toolbar */ +.editable-wysihtml5 { + width: 566px; + height: 250px; +} + +/* clear button shown as link in date inputs */ +.editable-clear { + clear: both; + font-size: 0.9em; + text-decoration: none; + text-align: right; +} + +/* IOS-style clear button for text inputs */ +.editable-clear-x { + background: url('clear.png') center center no-repeat; + display: block; + width: 13px; + height: 13px; + position: absolute; + opacity: 0.6; + z-index: 100; + + top: 50%; + right: 6px; + margin-top: -6px; + +} + +.editable-clear-x:hover { + opacity: 1; +} + +.editable-pre-wrapped { + white-space: pre-wrap; +} +.editable-container.editable-popup { + max-width: none !important; /* without this rule poshytip/tooltip does not stretch */ +} + +.editable-container.popover { + width: auto; /* without this rule popover does not stretch */ +} + +.editable-container.editable-inline { + display: inline-block; + vertical-align: middle; + width: auto; + /* inline-block emulation for IE7*/ + zoom: 1; + *display: inline; +} + +.editable-container.ui-widget { + font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */ + z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */ +} +.editable-click, +a.editable-click, +a.editable-click:hover { + text-decoration: none; + border-bottom: dashed 1px #0088cc; +} + +.editable-click.editable-disabled, +a.editable-click.editable-disabled, +a.editable-click.editable-disabled:hover { + color: #585858; + cursor: default; + border-bottom: none; +} + +.editable-empty, .editable-empty:hover, .editable-empty:focus{ + font-style: italic; + color: #DD1144; + /* border-bottom: none; */ + text-decoration: none; +} + +.editable-unsaved { + font-weight: bold; +} + +.editable-unsaved:after { +/* content: '*'*/ +} + +.editable-bg-transition { + -webkit-transition: background-color 1400ms ease-out; + -moz-transition: background-color 1400ms ease-out; + -o-transition: background-color 1400ms ease-out; + -ms-transition: background-color 1400ms ease-out; + transition: background-color 1400ms ease-out; +} + +/*see https://github.com/vitalets/x-editable/issues/139 */ +.form-horizontal .editable +{ + padding-top: 5px; + display:inline-block; +} + + +/*! + * Datepicker for Bootstrap + * + * Copyright 2012 Stefan Petre + * Improvements by Andrew Rowls + * Licensed under the Apache License v2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + */ +.datepicker { + padding: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + direction: ltr; + /*.dow { + border-top: 1px solid #ddd !important; + }*/ + +} +.datepicker-inline { + width: 220px; +} +.datepicker.datepicker-rtl { + direction: rtl; +} +.datepicker.datepicker-rtl table tr td span { + float: right; +} +.datepicker-dropdown { + top: 0; + left: 0; +} +.datepicker-dropdown:before { + content: ''; + display: inline-block; + border-left: 7px solid transparent; + border-right: 7px solid transparent; + border-bottom: 7px solid #ccc; + border-bottom-color: rgba(0, 0, 0, 0.2); + position: absolute; + top: -7px; + left: 6px; +} +.datepicker-dropdown:after { + content: ''; + display: inline-block; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #ffffff; + position: absolute; + top: -6px; + left: 7px; +} +.datepicker > div { + display: none; +} +.datepicker.days div.datepicker-days { + display: block; +} +.datepicker.months div.datepicker-months { + display: block; +} +.datepicker.years div.datepicker-years { + display: block; +} +.datepicker table { + margin: 0; +} +.datepicker td, +.datepicker th { + text-align: center; + width: 20px; + height: 20px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: none; +} +.table-striped .datepicker table tr td, +.table-striped .datepicker table tr th { + background-color: transparent; +} +.datepicker table tr td.day:hover { + background: #eeeeee; + cursor: pointer; +} +.datepicker table tr td.old, +.datepicker table tr td.new { + color: #999999; +} +.datepicker table tr td.disabled, +.datepicker table tr td.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td.today, +.datepicker table tr td.today:hover, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today.disabled:hover { + background-color: #fde19a; + background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); + background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); + background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); + background-image: linear-gradient(top, #fdd49a, #fdf59a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); + border-color: #fdf59a #fdf59a #fbed50; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #000; +} +.datepicker table tr td.today:hover, +.datepicker table tr td.today:hover:hover, +.datepicker table tr td.today.disabled:hover, +.datepicker table tr td.today.disabled:hover:hover, +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active, +.datepicker table tr td.today.disabled, +.datepicker table tr td.today:hover.disabled, +.datepicker table tr td.today.disabled.disabled, +.datepicker table tr td.today.disabled:hover.disabled, +.datepicker table tr td.today[disabled], +.datepicker table tr td.today:hover[disabled], +.datepicker table tr td.today.disabled[disabled], +.datepicker table tr td.today.disabled:hover[disabled] { + background-color: #fdf59a; +} +.datepicker table tr td.today:active, +.datepicker table tr td.today:hover:active, +.datepicker table tr td.today.disabled:active, +.datepicker table tr td.today.disabled:hover:active, +.datepicker table tr td.today.active, +.datepicker table tr td.today:hover.active, +.datepicker table tr td.today.disabled.active, +.datepicker table tr td.today.disabled:hover.active { + background-color: #fbf069 \9; +} +.datepicker table tr td.today:hover:hover { + color: #000; +} +.datepicker table tr td.today.active:hover { + color: #fff; +} +.datepicker table tr td.range, +.datepicker table tr td.range:hover, +.datepicker table tr td.range.disabled, +.datepicker table tr td.range.disabled:hover { + background: #eeeeee; + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today, +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today.disabled:hover { + background-color: #f3d17a; + background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a)); + background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a); + background-image: -o-linear-gradient(top, #f3c17a, #f3e97a); + background-image: linear-gradient(top, #f3c17a, #f3e97a); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); + border-color: #f3e97a #f3e97a #edde34; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; +} +.datepicker table tr td.range.today:hover, +.datepicker table tr td.range.today:hover:hover, +.datepicker table tr td.range.today.disabled:hover, +.datepicker table tr td.range.today.disabled:hover:hover, +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active, +.datepicker table tr td.range.today.disabled, +.datepicker table tr td.range.today:hover.disabled, +.datepicker table tr td.range.today.disabled.disabled, +.datepicker table tr td.range.today.disabled:hover.disabled, +.datepicker table tr td.range.today[disabled], +.datepicker table tr td.range.today:hover[disabled], +.datepicker table tr td.range.today.disabled[disabled], +.datepicker table tr td.range.today.disabled:hover[disabled] { + background-color: #f3e97a; +} +.datepicker table tr td.range.today:active, +.datepicker table tr td.range.today:hover:active, +.datepicker table tr td.range.today.disabled:active, +.datepicker table tr td.range.today.disabled:hover:active, +.datepicker table tr td.range.today.active, +.datepicker table tr td.range.today:hover.active, +.datepicker table tr td.range.today.disabled.active, +.datepicker table tr td.range.today.disabled:hover.active { + background-color: #efe24b \9; +} +.datepicker table tr td.selected, +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected.disabled:hover { + background-color: #9e9e9e; + background-image: -moz-linear-gradient(top, #b3b3b3, #808080); + background-image: -ms-linear-gradient(top, #b3b3b3, #808080); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080)); + background-image: -webkit-linear-gradient(top, #b3b3b3, #808080); + background-image: -o-linear-gradient(top, #b3b3b3, #808080); + background-image: linear-gradient(top, #b3b3b3, #808080); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); + border-color: #808080 #808080 #595959; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.selected:hover, +.datepicker table tr td.selected:hover:hover, +.datepicker table tr td.selected.disabled:hover, +.datepicker table tr td.selected.disabled:hover:hover, +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active, +.datepicker table tr td.selected.disabled, +.datepicker table tr td.selected:hover.disabled, +.datepicker table tr td.selected.disabled.disabled, +.datepicker table tr td.selected.disabled:hover.disabled, +.datepicker table tr td.selected[disabled], +.datepicker table tr td.selected:hover[disabled], +.datepicker table tr td.selected.disabled[disabled], +.datepicker table tr td.selected.disabled:hover[disabled] { + background-color: #808080; +} +.datepicker table tr td.selected:active, +.datepicker table tr td.selected:hover:active, +.datepicker table tr td.selected.disabled:active, +.datepicker table tr td.selected.disabled:hover:active, +.datepicker table tr td.selected.active, +.datepicker table tr td.selected:hover.active, +.datepicker table tr td.selected.disabled.active, +.datepicker table tr td.selected.disabled:hover.active { + background-color: #666666 \9; +} +.datepicker table tr td.active, +.datepicker table tr td.active:hover, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td.active:hover, +.datepicker table tr td.active:hover:hover, +.datepicker table tr td.active.disabled:hover, +.datepicker table tr td.active.disabled:hover:hover, +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active, +.datepicker table tr td.active.disabled, +.datepicker table tr td.active:hover.disabled, +.datepicker table tr td.active.disabled.disabled, +.datepicker table tr td.active.disabled:hover.disabled, +.datepicker table tr td.active[disabled], +.datepicker table tr td.active:hover[disabled], +.datepicker table tr td.active.disabled[disabled], +.datepicker table tr td.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td.active:active, +.datepicker table tr td.active:hover:active, +.datepicker table tr td.active.disabled:active, +.datepicker table tr td.active.disabled:hover:active, +.datepicker table tr td.active.active, +.datepicker table tr td.active:hover.active, +.datepicker table tr td.active.disabled.active, +.datepicker table tr td.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span { + display: block; + width: 23%; + height: 54px; + line-height: 54px; + float: left; + margin: 1%; + cursor: pointer; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; +} +.datepicker table tr td span:hover { + background: #eeeeee; +} +.datepicker table tr td span.disabled, +.datepicker table tr td span.disabled:hover { + background: none; + color: #999999; + cursor: default; +} +.datepicker table tr td span.active, +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active.disabled:hover { + background-color: #006dcc; + background-image: -moz-linear-gradient(top, #0088cc, #0044cc); + background-image: -ms-linear-gradient(top, #0088cc, #0044cc); + background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); + background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); + background-image: -o-linear-gradient(top, #0088cc, #0044cc); + background-image: linear-gradient(top, #0088cc, #0044cc); + background-repeat: repeat-x; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); + border-color: #0044cc #0044cc #002a80; + border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); + filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); + color: #fff; + text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); +} +.datepicker table tr td span.active:hover, +.datepicker table tr td span.active:hover:hover, +.datepicker table tr td span.active.disabled:hover, +.datepicker table tr td span.active.disabled:hover:hover, +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active, +.datepicker table tr td span.active.disabled, +.datepicker table tr td span.active:hover.disabled, +.datepicker table tr td span.active.disabled.disabled, +.datepicker table tr td span.active.disabled:hover.disabled, +.datepicker table tr td span.active[disabled], +.datepicker table tr td span.active:hover[disabled], +.datepicker table tr td span.active.disabled[disabled], +.datepicker table tr td span.active.disabled:hover[disabled] { + background-color: #0044cc; +} +.datepicker table tr td span.active:active, +.datepicker table tr td span.active:hover:active, +.datepicker table tr td span.active.disabled:active, +.datepicker table tr td span.active.disabled:hover:active, +.datepicker table tr td span.active.active, +.datepicker table tr td span.active:hover.active, +.datepicker table tr td span.active.disabled.active, +.datepicker table tr td span.active.disabled:hover.active { + background-color: #003399 \9; +} +.datepicker table tr td span.old, +.datepicker table tr td span.new { + color: #999999; +} +.datepicker th.datepicker-switch { + width: 145px; +} +.datepicker thead tr:first-child th, +.datepicker tfoot tr th { + cursor: pointer; +} +.datepicker thead tr:first-child th:hover, +.datepicker tfoot tr th:hover { + background: #eeeeee; +} +.datepicker .cw { + font-size: 10px; + width: 12px; + padding: 0 2px 0 5px; + vertical-align: middle; +} +.datepicker thead tr:first-child th.cw { + cursor: default; + background-color: transparent; +} +.input-append.date .add-on i, +.input-prepend.date .add-on i { + display: block; + cursor: pointer; + width: 16px; + height: 16px; +} +.input-daterange input { + text-align: center; +} +.input-daterange input:first-child { + -webkit-border-radius: 3px 0 0 3px; + -moz-border-radius: 3px 0 0 3px; + border-radius: 3px 0 0 3px; +} +.input-daterange input:last-child { + -webkit-border-radius: 0 3px 3px 0; + -moz-border-radius: 0 3px 3px 0; + border-radius: 0 3px 3px 0; +} +.input-daterange .add-on { + display: inline-block; + width: auto; + min-width: 16px; + height: 18px; + padding: 4px 5px; + font-weight: normal; + line-height: 18px; + text-align: center; + text-shadow: 0 1px 0 #ffffff; + vertical-align: middle; + background-color: #eeeeee; + border: 1px solid #ccc; + margin-left: -5px; + margin-right: -5px; +} \ No newline at end of file diff --git a/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.min.js b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.min.js new file mode 100644 index 000000000..e2703aee8 --- /dev/null +++ b/ruoyi-admin/src/main/resources/static/ajax/libs/bootstrap-table/extensions/editable/bootstrap-editable.min.js @@ -0,0 +1,7 @@ +/*! X-editable - v1.5.1 +* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery +* http://github.com/vitalets/x-editable +* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ +!function(a){"use strict";var b=function(b,c){this.options=a.extend({},a.fn.editableform.defaults,c),this.$div=a(b),this.options.scope||(this.options.scope=this)};b.prototype={constructor:b,initInput:function(){this.input=this.options.input,this.value=this.input.str2value(this.options.value),this.input.prerender()},initTemplate:function(){this.$form=a(a.fn.editableform.template)},initButtons:function(){var b=this.$form.find(".editable-buttons");b.append(a.fn.editableform.buttons),"bottom"===this.options.showbuttons&&b.addClass("editable-buttons-bottom")},render:function(){this.$loading=a(a.fn.editableform.loading),this.$div.empty().append(this.$loading),this.initTemplate(),this.options.showbuttons?this.initButtons():this.$form.find(".editable-buttons").remove(),this.showLoading(),this.isSaving=!1,this.$div.triggerHandler("rendering"),this.initInput(),this.$form.find("div.editable-input").append(this.input.$tpl),this.$div.append(this.$form),a.when(this.input.render()).then(a.proxy(function(){if(this.options.showbuttons||this.input.autosubmit(),this.$form.find(".editable-cancel").click(a.proxy(this.cancel,this)),this.input.error)this.error(this.input.error),this.$form.find(".editable-submit").attr("disabled",!0),this.input.$input.attr("disabled",!0),this.$form.submit(function(a){a.preventDefault()});else{this.error(!1),this.input.$input.removeAttr("disabled"),this.$form.find(".editable-submit").removeAttr("disabled");var b=null===this.value||void 0===this.value||""===this.value?this.options.defaultValue:this.value;this.input.value2input(b),this.$form.submit(a.proxy(this.submit,this))}this.$div.triggerHandler("rendered"),this.showForm(),this.input.postrender&&this.input.postrender()},this))},cancel:function(){this.$div.triggerHandler("cancel")},showLoading:function(){var a,b;this.$form?(a=this.$form.outerWidth(),b=this.$form.outerHeight(),a&&this.$loading.width(a),b&&this.$loading.height(b),this.$form.hide()):(a=this.$loading.parent().width(),a&&this.$loading.width(a)),this.$loading.show()},showForm:function(a){this.$loading.hide(),this.$form.show(),a!==!1&&this.input.activate(),this.$div.triggerHandler("show")},error:function(b){var c,d=this.$form.find(".control-group"),e=this.$form.find(".editable-error-block");if(b===!1)d.removeClass(a.fn.editableform.errorGroupClass),e.removeClass(a.fn.editableform.errorBlockClass).empty().hide();else{if(b){c=(""+b).split("\n");for(var f=0;f").text(c[f]).html();b=c.join("
    ")}d.addClass(a.fn.editableform.errorGroupClass),e.addClass(a.fn.editableform.errorBlockClass).html(b).show()}},submit:function(b){b.stopPropagation(),b.preventDefault();var c=this.input.input2value(),d=this.validate(c);if("object"===a.type(d)&&void 0!==d.newValue){if(c=d.newValue,this.input.value2input(c),"string"==typeof d.msg)return this.error(d.msg),this.showForm(),void 0}else if(d)return this.error(d),this.showForm(),void 0;if(!this.options.savenochange&&this.input.value2str(c)==this.input.value2str(this.value))return this.$div.triggerHandler("nochange"),void 0;var e=this.input.value2submit(c);this.isSaving=!0,a.when(this.save(e)).done(a.proxy(function(a){this.isSaving=!1;var b="function"==typeof this.options.success?this.options.success.call(this.options.scope,a,c):null;return b===!1?(this.error(!1),this.showForm(!1),void 0):"string"==typeof b?(this.error(b),this.showForm(),void 0):(b&&"object"==typeof b&&b.hasOwnProperty("newValue")&&(c=b.newValue),this.error(!1),this.value=c,this.$div.triggerHandler("save",{newValue:c,submitValue:e,response:a}),void 0)},this)).fail(a.proxy(function(a){this.isSaving=!1;var b;b="function"==typeof this.options.error?this.options.error.call(this.options.scope,a,c):"string"==typeof a?a:a.responseText||a.statusText||"Unknown error!",this.error(b),this.showForm()},this))},save:function(b){this.options.pk=a.fn.editableutils.tryParseJson(this.options.pk,!0);var c,d="function"==typeof this.options.pk?this.options.pk.call(this.options.scope):this.options.pk,e=!!("function"==typeof this.options.url||this.options.url&&("always"===this.options.send||"auto"===this.options.send&&null!==d&&void 0!==d));return e?(this.showLoading(),c={name:this.options.name||"",value:b,pk:d},"function"==typeof this.options.params?c=this.options.params.call(this.options.scope,c):(this.options.params=a.fn.editableutils.tryParseJson(this.options.params,!0),a.extend(c,this.options.params)),"function"==typeof this.options.url?this.options.url.call(this.options.scope,c):a.ajax(a.extend({url:this.options.url,data:c,type:"POST"},this.options.ajaxOptions))):void 0},validate:function(a){return void 0===a&&(a=this.value),"function"==typeof this.options.validate?this.options.validate.call(this.options.scope,a):void 0},option:function(a,b){a in this.options&&(this.options[a]=b),"value"===a&&this.setValue(b)},setValue:function(a,b){this.value=b?this.input.str2value(a):a,this.$form&&this.$form.is(":visible")&&this.input.value2input(this.value)}},a.fn.editableform=function(c){var d=arguments;return this.each(function(){var e=a(this),f=e.data("editableform"),g="object"==typeof c&&c;f||e.data("editableform",f=new b(this,g)),"string"==typeof c&&f[c].apply(f,Array.prototype.slice.call(d,1))})},a.fn.editableform.Constructor=b,a.fn.editableform.defaults={type:"text",url:null,params:null,name:null,pk:null,value:null,defaultValue:null,send:"auto",validate:null,success:null,error:null,ajaxOptions:null,showbuttons:!0,scope:null,savenochange:!1},a.fn.editableform.template='
    ',a.fn.editableform.loading='
    ',a.fn.editableform.buttons='',a.fn.editableform.errorGroupClass=null,a.fn.editableform.errorBlockClass="editable-error",a.fn.editableform.engine="jquery"}(window.jQuery),function(a){"use strict";a.fn.editableutils={inherit:function(a,b){var c=function(){};c.prototype=b.prototype,a.prototype=new c,a.prototype.constructor=a,a.superclass=b.prototype},setCursorPosition:function(a,b){if(a.setSelectionRange)a.setSelectionRange(b,b);else if(a.createTextRange){var c=a.createTextRange();c.collapse(!0),c.moveEnd("character",b),c.moveStart("character",b),c.select()}},tryParseJson:function(a,b){if("string"==typeof a&&a.length&&a.match(/^[\{\[].*[\}\]]$/))if(b)try{a=new Function("return "+a)()}catch(c){}finally{return a}else a=new Function("return "+a)();return a},sliceObj:function(b,c,d){var e,f,g={};if(!a.isArray(c)||!c.length)return g;for(var h=0;h").text(b).html()},itemsByValue:function(b,c,d){if(!c||null===b)return[];if("function"!=typeof d){var e=d||"value";d=function(a){return a[e]}}var f=a.isArray(b),g=[],h=this;return a.each(c,function(c,e){if(e.children)g=g.concat(h.itemsByValue(b,e.children,d));else if(f)a.grep(b,function(a){return a==(e&&"object"==typeof e?d(e):e)}).length&&g.push(e);else{var i=e&&"object"==typeof e?d(e):e;b==i&&g.push(e)}}),g},createInput:function(b){var c,d,e,f=b.type;return"date"===f&&("inline"===b.mode?a.fn.editabletypes.datefield?f="datefield":a.fn.editabletypes.dateuifield&&(f="dateuifield"):a.fn.editabletypes.date?f="date":a.fn.editabletypes.dateui&&(f="dateui"),"date"!==f||a.fn.editabletypes.date||(f="combodate")),"datetime"===f&&"inline"===b.mode&&(f="datetimefield"),"wysihtml5"!==f||a.fn.editabletypes[f]||(f="textarea"),"function"==typeof a.fn.editabletypes[f]?(c=a.fn.editabletypes[f],d=this.sliceObj(b,this.objectKeys(c.defaults)),e=new c(d)):(a.error("Unknown type: "+f),!1)},supportsTransitions:function(){var a=document.body||document.documentElement,b=a.style,c="transition",d=["Moz","Webkit","Khtml","O","ms"];if("string"==typeof b[c])return!0;c=c.charAt(0).toUpperCase()+c.substr(1);for(var e=0;e"),this.tip().is(this.innerCss)?this.tip().append(this.$form):this.tip().find(this.innerCss).append(this.$form),this.renderForm()},hide:function(a){if(this.tip()&&this.tip().is(":visible")&&this.$element.hasClass("editable-open")){if(this.$form.data("editableform").isSaving)return this.delayedHide={reason:a},void 0;this.delayedHide=!1,this.$element.removeClass("editable-open"),this.innerHide(),this.$element.triggerHandler("hidden",a||"manual")}},innerShow:function(){},innerHide:function(){},toggle:function(a){this.container()&&this.tip()&&this.tip().is(":visible")?this.hide():this.show(a)},setPosition:function(){},save:function(a,b){this.$element.triggerHandler("save",b),this.hide("save")},option:function(a,b){this.options[a]=b,a in this.containerOptions?(this.containerOptions[a]=b,this.setContainerOption(a,b)):(this.formOptions[a]=b,this.$form&&this.$form.editableform("option",a,b))},setContainerOption:function(a,b){this.call("option",a,b)},destroy:function(){this.hide(),this.innerDestroy(),this.$element.off("destroyed"),this.$element.removeData("editableContainer")},innerDestroy:function(){},closeOthers:function(b){a(".editable-open").each(function(c,d){if(d!==b&&!a(d).find(b).length){var e=a(d),f=e.data("editableContainer");f&&("cancel"===f.options.onblur?e.data("editableContainer").hide("onblur"):"submit"===f.options.onblur&&e.data("editableContainer").tip().find("form").submit())}})},activate:function(){this.tip&&this.tip().is(":visible")&&this.$form&&this.$form.data("editableform").input.activate()}},a.fn.editableContainer=function(d){var e=arguments;return this.each(function(){var f=a(this),g="editableContainer",h=f.data(g),i="object"==typeof d&&d,j="inline"===i.mode?c:b;h||f.data(g,h=new j(this,i)),"string"==typeof d&&h[d].apply(h,Array.prototype.slice.call(e,1))})},a.fn.editableContainer.Popup=b,a.fn.editableContainer.Inline=c,a.fn.editableContainer.defaults={value:null,placement:"top",autohide:!0,onblur:"cancel",anim:!1,mode:"popup"},jQuery.event.special.destroyed={remove:function(a){a.handler&&a.handler()}}}(window.jQuery),function(a){"use strict";a.extend(a.fn.editableContainer.Inline.prototype,a.fn.editableContainer.Popup.prototype,{containerName:"editableform",innerCss:".editable-inline",containerClass:"editable-container editable-inline",initContainer:function(){this.$tip=a(""),this.options.anim||(this.options.anim=0)},splitOptions:function(){this.containerOptions={},this.formOptions=this.options},tip:function(){return this.$tip},innerShow:function(){this.$element.hide(),this.tip().insertAfter(this.$element).show()},innerHide:function(){this.$tip.hide(this.options.anim,a.proxy(function(){this.$element.show(),this.innerDestroy()},this))},innerDestroy:function(){this.tip()&&this.tip().empty().remove()}})}(window.jQuery),function(a){"use strict";var b=function(b,c){this.$element=a(b),this.options=a.extend({},a.fn.editable.defaults,c,a.fn.editableutils.getConfigData(this.$element)),this.options.selector?this.initLive():this.init(),this.options.highlight&&!a.fn.editableutils.supportsTransitions()&&(this.options.highlight=!1)};b.prototype={constructor:b,init:function(){var b,c=!1;if(this.options.name=this.options.name||this.$element.attr("id"),this.options.scope=this.$element[0],this.input=a.fn.editableutils.createInput(this.options),this.input){switch(void 0===this.options.value||null===this.options.value?(this.value=this.input.html2value(a.trim(this.$element.html())),c=!0):(this.options.value=a.fn.editableutils.tryParseJson(this.options.value,!0),this.value="string"==typeof this.options.value?this.input.str2value(this.options.value):this.options.value),this.$element.addClass("editable"),"textarea"===this.input.type&&this.$element.addClass("editable-pre-wrapped"),"manual"!==this.options.toggle?(this.$element.addClass("editable-click"),this.$element.on(this.options.toggle+".editable",a.proxy(function(a){if(this.options.disabled||a.preventDefault(),"mouseenter"===this.options.toggle)this.show();else{var b="click"!==this.options.toggle;this.toggle(b)}},this))):this.$element.attr("tabindex",-1),"function"==typeof this.options.display&&(this.options.autotext="always"),this.options.autotext){case"always":b=!0;break;case"auto":b=!a.trim(this.$element.text()).length&&null!==this.value&&void 0!==this.value&&!c;break;default:b=!1}a.when(b?this.render():!0).then(a.proxy(function(){this.options.disabled?this.disable():this.enable(),this.$element.triggerHandler("init",this)},this))}},initLive:function(){var b=this.options.selector;this.options.selector=!1,this.options.autotext="never",this.$element.on(this.options.toggle+".editable",b,a.proxy(function(b){var c=a(b.target);c.data("editable")||(c.hasClass(this.options.emptyclass)&&c.empty(),c.editable(this.options).trigger(b))},this))},render:function(a){return this.options.display!==!1?this.input.value2htmlFinal?this.input.value2html(this.value,this.$element[0],this.options.display,a):"function"==typeof this.options.display?this.options.display.call(this.$element[0],this.value,a):this.input.value2html(this.value,this.$element[0]):void 0},enable:function(){this.options.disabled=!1,this.$element.removeClass("editable-disabled"),this.handleEmpty(this.isEmpty),"manual"!==this.options.toggle&&"-1"===this.$element.attr("tabindex")&&this.$element.removeAttr("tabindex")},disable:function(){this.options.disabled=!0,this.hide(),this.$element.addClass("editable-disabled"),this.handleEmpty(this.isEmpty),this.$element.attr("tabindex",-1)},toggleDisabled:function(){this.options.disabled?this.enable():this.disable()},option:function(b,c){return b&&"object"==typeof b?(a.each(b,a.proxy(function(b,c){this.option(a.trim(b),c)},this)),void 0):(this.options[b]=c,"disabled"===b?c?this.disable():this.enable():("value"===b&&this.setValue(c),this.container&&this.container.option(b,c),this.input.option&&this.input.option(b,c),void 0))},handleEmpty:function(b){this.options.display!==!1&&(this.isEmpty=void 0!==b?b:"function"==typeof this.input.isEmpty?this.input.isEmpty(this.$element):""===a.trim(this.$element.html()),this.options.disabled?this.isEmpty&&(this.$element.empty(),this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass)):this.isEmpty?(this.$element.html(this.options.emptytext),this.options.emptyclass&&this.$element.addClass(this.options.emptyclass)):this.options.emptyclass&&this.$element.removeClass(this.options.emptyclass))},show:function(b){if(!this.options.disabled){if(this.container){if(this.container.tip().is(":visible"))return}else{var c=a.extend({},this.options,{value:this.value,input:this.input});this.$element.editableContainer(c),this.$element.on("save.internal",a.proxy(this.save,this)),this.container=this.$element.data("editableContainer")}this.container.show(b)}},hide:function(){this.container&&this.container.hide()},toggle:function(a){this.container&&this.container.tip().is(":visible")?this.hide():this.show(a)},save:function(a,b){if(this.options.unsavedclass){var c=!1;c=c||"function"==typeof this.options.url,c=c||this.options.display===!1,c=c||void 0!==b.response,c=c||this.options.savenochange&&this.input.value2str(this.value)!==this.input.value2str(b.newValue),c?this.$element.removeClass(this.options.unsavedclass):this.$element.addClass(this.options.unsavedclass)}if(this.options.highlight){var d=this.$element,e=d.css("background-color");d.css("background-color",this.options.highlight),setTimeout(function(){"transparent"===e&&(e=""),d.css("background-color",e),d.addClass("editable-bg-transition"),setTimeout(function(){d.removeClass("editable-bg-transition")},1700)},10)}this.setValue(b.newValue,!1,b.response)},validate:function(){return"function"==typeof this.options.validate?this.options.validate.call(this,this.value):void 0},setValue:function(b,c,d){this.value=c?this.input.str2value(b):b,this.container&&this.container.option("value",this.value),a.when(this.render(d)).then(a.proxy(function(){this.handleEmpty()},this))},activate:function(){this.container&&this.container.activate()},destroy:function(){this.disable(),this.container&&this.container.destroy(),this.input.destroy(),"manual"!==this.options.toggle&&(this.$element.removeClass("editable-click"),this.$element.off(this.options.toggle+".editable")),this.$element.off("save.internal"),this.$element.removeClass("editable editable-open editable-disabled"),this.$element.removeData("editable")}},a.fn.editable=function(c){var d={},e=arguments,f="editable";switch(c){case"validate":return this.each(function(){var b,c=a(this),e=c.data(f);e&&(b=e.validate())&&(d[e.options.name]=b)}),d;case"getValue":return 2===arguments.length&&arguments[1]===!0?d=this.eq(0).data(f).value:this.each(function(){var b=a(this),c=b.data(f);c&&void 0!==c.value&&null!==c.value&&(d[c.options.name]=c.input.value2submit(c.value))}),d;case"submit":var g=arguments[1]||{},h=this,i=this.editable("validate");if(a.isEmptyObject(i)){var j={};if(1===h.length){var k=h.data("editable"),l={name:k.options.name||"",value:k.input.value2submit(k.value),pk:"function"==typeof k.options.pk?k.options.pk.call(k.options.scope):k.options.pk};"function"==typeof k.options.params?l=k.options.params.call(k.options.scope,l):(k.options.params=a.fn.editableutils.tryParseJson(k.options.params,!0),a.extend(l,k.options.params)),j={url:k.options.url,data:l,type:"POST"},g.success=g.success||k.options.success,g.error=g.error||k.options.error}else{var m=this.editable("getValue");j={url:g.url,data:m,type:"POST"}}j.success="function"==typeof g.success?function(a){g.success.call(h,a,g)}:a.noop,j.error="function"==typeof g.error?function(){g.error.apply(h,arguments)}:a.noop,g.ajaxOptions&&a.extend(j,g.ajaxOptions),g.data&&a.extend(j.data,g.data),a.ajax(j)}else"function"==typeof g.error&&g.error.call(h,i);return this}return this.each(function(){var d=a(this),g=d.data(f),h="object"==typeof c&&c;return h&&h.selector?(g=new b(this,h),void 0):(g||d.data(f,g=new b(this,h)),"string"==typeof c&&g[c].apply(g,Array.prototype.slice.call(e,1)),void 0)})},a.fn.editable.defaults={type:"text",disabled:!1,toggle:"click",emptytext:"Empty",autotext:"auto",value:null,display:null,emptyclass:"editable-empty",unsavedclass:"editable-unsaved",selector:null,highlight:"#FFFF80"}}(window.jQuery),function(a){"use strict";a.fn.editabletypes={};var b=function(){};b.prototype={init:function(b,c,d){this.type=b,this.options=a.extend({},d,c)},prerender:function(){this.$tpl=a(this.options.tpl),this.$input=this.$tpl,this.$clear=null,this.error=null},render:function(){},value2html:function(b,c){a(c)[this.options.escape?"text":"html"](a.trim(b))},html2value:function(b){return a("
    ").html(b).text()},value2str:function(a){return a},str2value:function(a){return a},value2submit:function(a){return a},value2input:function(a){this.$input.val(a)},input2value:function(){return this.$input.val()},activate:function(){this.$input.is(":visible")&&this.$input.focus()},clear:function(){this.$input.val(null)},escape:function(b){return a("
    ").text(b).html()},autosubmit:function(){},destroy:function(){},setClass:function(){this.options.inputclass&&this.$input.addClass(this.options.inputclass)},setAttr:function(a){void 0!==this.options[a]&&null!==this.options[a]&&this.$input.attr(a,this.options[a])},option:function(a,b){this.options[a]=b}},b.defaults={tpl:"",inputclass:null,escape:!0,scope:null,showbuttons:!0},a.extend(a.fn.editabletypes,{abstractinput:b})}(window.jQuery),function(a){"use strict";var b=function(){};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){var b=a.Deferred();return this.error=null,this.onSourceReady(function(){this.renderList(),b.resolve()},function(){this.error=this.options.sourceError,b.resolve()}),b.promise()},html2value:function(){return null},value2html:function(b,c,d,e){var f=a.Deferred(),g=function(){"function"==typeof d?d.call(c,b,this.sourceData,e):this.value2htmlFinal(b,c),f.resolve()};return null===b?g.call(this):this.onSourceReady(g,function(){f.resolve()}),f.promise()},onSourceReady:function(b,c){var d;if(a.isFunction(this.options.source)?(d=this.options.source.call(this.options.scope),this.sourceData=null):d=this.options.source,this.options.sourceCache&&a.isArray(this.sourceData))return b.call(this),void 0;try{d=a.fn.editableutils.tryParseJson(d,!1)}catch(e){return c.call(this),void 0}if("string"==typeof d){if(this.options.sourceCache){var f,g=d;if(a(document).data(g)||a(document).data(g,{}),f=a(document).data(g),f.loading===!1&&f.sourceData)return this.sourceData=f.sourceData,this.doPrepend(),b.call(this),void 0;if(f.loading===!0)return f.callbacks.push(a.proxy(function(){this.sourceData=f.sourceData,this.doPrepend(),b.call(this)},this)),f.err_callbacks.push(a.proxy(c,this)),void 0;f.loading=!0,f.callbacks=[],f.err_callbacks=[]}var h=a.extend({url:d,type:"get",cache:!1,dataType:"json",success:a.proxy(function(d){f&&(f.loading=!1),this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(f&&(f.sourceData=this.sourceData,a.each(f.callbacks,function(){this.call()})),this.doPrepend(),b.call(this)):(c.call(this),f&&a.each(f.err_callbacks,function(){this.call()}))},this),error:a.proxy(function(){c.call(this),f&&(f.loading=!1,a.each(f.err_callbacks,function(){this.call()}))},this)},this.options.sourceOptions);a.ajax(h)}else this.sourceData=this.makeArray(d),a.isArray(this.sourceData)?(this.doPrepend(),b.call(this)):c.call(this)},doPrepend:function(){null!==this.options.prepend&&void 0!==this.options.prepend&&(a.isArray(this.prependData)||(a.isFunction(this.options.prepend)&&(this.options.prepend=this.options.prepend.call(this.options.scope)),this.options.prepend=a.fn.editableutils.tryParseJson(this.options.prepend,!0),"string"==typeof this.options.prepend&&(this.options.prepend={"":this.options.prepend}),this.prependData=this.makeArray(this.options.prepend)),a.isArray(this.prependData)&&a.isArray(this.sourceData)&&(this.sourceData=this.prependData.concat(this.sourceData)))},renderList:function(){},value2htmlFinal:function(){},makeArray:function(b){var c,d,e,f,g=[];if(!b||"string"==typeof b)return null;if(a.isArray(b)){f=function(a,b){return d={value:a,text:b},c++>=2?!1:void 0};for(var h=0;h1&&(e.children&&(e.children=this.makeArray(e.children)),g.push(e))):g.push({value:e,text:e})}else a.each(b,function(a,b){g.push({value:a,text:b})});return g},option:function(a,b){this.options[a]=b,"source"===a&&(this.sourceData=null),"prepend"===a&&(this.prependData=null)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{source:null,prepend:!1,sourceError:"Error when loading list",sourceCache:!0,sourceOptions:null}),a.fn.editabletypes.list=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("text",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.renderClear(),this.setClass(),this.setAttr("placeholder")},activate:function(){this.$input.is(":visible")&&(this.$input.focus(),a.fn.editableutils.setCursorPosition(this.$input.get(0),this.$input.val().length),this.toggleClear&&this.toggleClear())},renderClear:function(){this.options.clear&&(this.$clear=a(''),this.$input.after(this.$clear).css("padding-right",24).keyup(a.proxy(function(b){if(!~a.inArray(b.keyCode,[40,38,9,13,27])){clearTimeout(this.t);var c=this;this.t=setTimeout(function(){c.toggleClear(b)},100)}},this)).parent().css("position","relative"),this.$clear.click(a.proxy(this.clear,this)))},postrender:function(){},toggleClear:function(){if(this.$clear){var a=this.$input.val().length,b=this.$clear.is(":visible");a&&!b&&this.$clear.show(),!a&&b&&this.$clear.hide()}},clear:function(){this.$clear.hide(),this.$input.val("").focus()}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:'',placeholder:null,clear:!0}),a.fn.editabletypes.text=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("textarea",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.abstractinput),a.extend(b.prototype,{render:function(){this.setClass(),this.setAttr("placeholder"),this.setAttr("rows"),this.$input.keydown(function(b){b.ctrlKey&&13===b.which&&a(this).closest("form").submit()})},activate:function(){a.fn.editabletypes.text.prototype.activate.call(this)}}),b.defaults=a.extend({},a.fn.editabletypes.abstractinput.defaults,{tpl:"",inputclass:"input-large",placeholder:null,rows:7}),a.fn.editabletypes.textarea=b}(window.jQuery),function(a){"use strict";var b=function(a){this.init("select",a,b.defaults)};a.fn.editableutils.inherit(b,a.fn.editabletypes.list),a.extend(b.prototype,{renderList:function(){this.$input.empty();var b=function(c,d){var e;if(a.isArray(d))for(var f=0;f",e),d[f].children))):(e.value=d[f].value,d[f].disabled&&(e.disabled=!0),c.append(a("