1 files modified
794 files added
New file |
| | |
| | | *.js linguist-language=java |
| | | *.css linguist-language=java |
| | | *.html linguist-language=java |
| | | *.btl linguist-language=java |
New file |
| | |
| | | # Compiled class file |
| | | *.class |
| | | *.iml |
| | | *.idea |
| | | target/ |
| | | logs/ |
| | | |
| | | # Log file |
| | | *.log |
| | | |
| | | # BlueJ files |
| | | *.ctxt |
| | | |
| | | # Mobile Tools for Java (J2ME) |
| | | .mtj.tmp/ |
| | | |
| | | # Package Files # |
| | | *.jar |
| | | *.war |
| | | *.ear |
| | | *.tar.gz |
| | | *.rar |
| | | |
| | | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
| | | hs_err_pid* |
| | | |
| | | *velocity.log* |
| | | |
| | | # Eclipse # |
| | | .classpath |
| | | .project |
| | | .settings/ |
| | | |
| | | .DS_Store |
| | | |
| | | _dockerCerts/ |
| | | |
| | | .factorypath |
| | | |
| | | node_modules/ |
| | | dist/ |
| | | package-lock.json |
| | | yarn.lock |
| | | |
| | | rebel.xml |
| | | |
| | | !DmJdbcDriver18.jar |
| | | !kingbase8-8.6.0.jar |
| | | |
| | | _images |
| | | _sql |
New file |
| | |
| | | Apache License |
| | | |
| | | Version 2.0, January 2004 |
| | | |
| | | http://www.apache.org/licenses/ |
| | | |
| | | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION |
| | | |
| | | 1. Definitions. |
| | | |
| | | "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. |
| | | |
| | | "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. |
| | | |
| | | "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. |
| | | |
| | | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. |
| | | |
| | | "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. |
| | | |
| | | "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. |
| | | |
| | | "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). |
| | | |
| | | "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. |
| | | |
| | | "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." |
| | | |
| | | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. |
| | | |
| | | 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. |
| | | |
| | | 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. |
| | | |
| | | 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: |
| | | |
| | | You must give any other recipients of the Work or Derivative Works a copy of this License; and |
| | | You must cause any modified files to carry prominent notices stating that You changed the files; and |
| | | You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and |
| | | If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. |
| | | |
| | | You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. |
| | | 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. |
| | | |
| | | 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. |
| | | |
| | | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. |
| | | |
| | | 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. |
| | | |
| | | 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. |
| | | |
| | | END OF TERMS AND CONDITIONS |
| | |
| | | ## blog-admin |
| | | <div align="center"> |
| | | <p align="center"> |
| | | <img src="./_web/public/logo.png" height="150" alt="logo"/> |
| | | </p> |
| | | </div> |
| | | |
| | | blog的后端代码 |
| | | ### 框架介绍 |
| | | |
| | | <div><h5>Snowy是一款基于国产密码算法后台权限管理系统,其中采用了SM2、SM3、SM4及签名验签,软件层面完全符合等保测评要求,让更多的人认识密码,使用密码。技术框架与密码结合,让前后分离“密”不可分。</h5></div> |
| | | <div><h4>结合SpringBoot+AntDesignVue开发,注释丰富,代码简洁。适配国产数据库(金仓、达梦)、主流数据库Mysql、Oracle、Mssql、Postgresql,小诺的产品一致追求简洁干净,一套代码搞定!同时支持国产中间件部署、麒麟操作系统、Windows、Linux部署使用。</h4></div> |
| | | <div align="center"><h5 align="center">Snowy谐音“小诺”,恰应小诺团队名称;意思为”下雪的、纯洁的“,寓意框架追求简洁至上,大道至简。</h5></div> |
| | | |
| | | <p align="center"> |
| | | <p align="center"> |
| | | <a href="https://gitee.com/xiaonuobase/snowy"> |
| | | <img src="https://gitee.com/xiaonuobase/snowy/badge/star.svg?theme=dark" alt="Gitee star"> |
| | | </a> |
| | | <a href="https://gitee.com/xiaonuobase/snowy"> |
| | | <img src="https://gitee.com/xiaonuobase/snowy/badge/fork.svg?theme=dark" alt="Gitee fork"> |
| | | </a> |
| | | <a href="https://www.antdv.com/docs/vue/introduce-cn/"> |
| | | <img src="https://img.shields.io/badge/vue-2.x-blue.svg" alt="bootstrap"> |
| | | </a> |
| | | <a href="https://www.antdv.com/docs/vue/introduce-cn/"> |
| | | <img src="https://img.shields.io/badge/vue--ant--design-1.5.6-blue.svg" alt="bootstrap"> |
| | | </a> |
| | | <a href="http://spring.io/projects/spring-boot"> |
| | | <img src="https://img.shields.io/badge/spring--boot-2.3.1-green.svg" alt="spring-boot"> |
| | | </a> |
| | | <a href="http://mp.baomidou.com"> |
| | | <img src="https://img.shields.io/badge/mybatis--plus-3.3.2-blue.svg" alt="mybatis-plus"> |
| | | </a> |
| | | <a href="./LICENSE"> |
| | | <img src="https://img.shields.io/badge/license-Apache%202-red" alt="license Apache 2.0"> |
| | | </a> |
| | | </p> |
| | | </p> |
| | | |
| | | ### 快速启动 |
| | | |
| | | 您的开发电脑需要安装:NodeJs(14.x)、npm或yarn(最新版)建议使用yarn、Mysql5.7、Jdk1.8、Maven3.6.3(最新版)、开发工具推荐idea |
| | | |
| | | * 启动前端:打开_web文件夹,进行依赖下载,运行npm install或yarn命令,再运行npm run serve或 yarn run serve |
| | | * 启动后端:打开application-local中配置数据库信息,运行SnowyApplication类即可启动 |
| | | * 浏览器访问:http://localhost:81 (默认前端端口为:81,后端端口为:82) |
| | | |
| | | ### 快速链接 |
| | | * 演示地址(superAdmin/123456):https://snowy.xiaonuo.vip |
| | | * 在线文档:https://doc.xiaonuo.vip |
| | | * layui单体版本:https://gitee.com/xiaonuobase/snowy-layui |
| | | * vue前后分离版本:https://gitee.com/xiaonuobase/snowy |
| | | * cloud微服务前后分离版本:https://gitee.com/xiaonuobase/snowy-cloud |
| | | * 我们的其他产品线同样开源,如需关注最新动态可加入QQ群聊探讨:[732230670](https://wpa.qq.com/msgrd?v=3&uin=732230670&_blank) |
| | | * 如果我们的产品能满足您的需求,很期待您给我们右上角点个 star |
| | | |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwJHENw&path=%7BshareItemLink%3A7qwJHENw%7D%2F"/> |
| | | |
| | | ### 密码分步:fire: |
| | | |
| | | | 功能 | 算法类型 | |
| | | | ---------------------- | ------------- | |
| | | | 登录 | SM2前端加密,后端解密 | |
| | | | 登录登出日志 | SM2对登录登出日志做签名完整性保护存储 | |
| | | | 操作日志 | SM2对操作日志做签名完整性保护存储 | |
| | | | Token | SM4(cbc模式)加密,Token不再曝光暴露 | |
| | | | 用户密码 | SM3完整性保护存储,登录时做完整性校验 | |
| | | | 用户手机号 | SM4(cbc模式)加解密使用字段脱敏 | |
| | | |
| | | ### 视频教程:fire: |
| | | |
| | | | 序号 | 链接地址 | |
| | | | ---------------------- | ------------- | |
| | | | 1 | [小诺开源技术团队及框架介绍](https://www.bilibili.com/video/BV1Yf4y1N7YU?from=search&seid=16730766915542181758) | |
| | | | 2 | [小诺框架Snowy基础环境介绍](https://www.bilibili.com/video/BV1yA411c7d3) | |
| | | | 3 | [Snowy代码下载及启动](https://www.bilibili.com/video/BV1SP4y1p7M8) | |
| | | | 4 | [Snowy生成一个完整的前后端模块](https://www.bilibili.com/video/BV1Ry4y1G7er) | |
| | | |
| | | 更新中。。。 |
| | | |
| | | ### 升级计划:fire: |
| | | |
| | | 2.0版本正在全力打造中 |
| | | |
| | | |
| | | ### 架构原理图 |
| | | * 业务架构 |
| | | <p align="center"> |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKTmEw&path=%7BshareItemLink%3A7qwKTmEw%7D%2F"/> |
| | | </p> |
| | | |
| | | * 应用架构 |
| | | <p align="center"> |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKfxFw&path=%7BshareItemLink%3A7qwKfxFw%7D%2F"/> |
| | | </p> |
| | | |
| | | * 数据架构 |
| | | <p align="center"> |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwKrjRw&path=%7BshareItemLink%3A7qwKrjRw%7D%2F"/> |
| | | </p> |
| | | |
| | | * 技术架构 |
| | | <p align="center"> |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwK4RoA&path=%7BshareItemLink%3A7qwK4RoA%7D%2F"/> |
| | | </p> |
| | | |
| | | * 部署架构 |
| | | <p align="center"> |
| | | <img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwLD35w&path=%7BshareItemLink%3A7qwLD35w%7D%2F"/> |
| | | </p> |
| | | |
| | | ### 效果图 |
| | | |
| | | <table> |
| | | <tr> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/111529_02708b11_1980003.png"/></td> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/111909_5957b35a_1980003.png"/></td> |
| | | </tr> |
| | | <tr> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/112254_5e8a3a0b_1980003.png"/></td> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/112510_90191be8_1980003.png"/></td> |
| | | </tr> |
| | | <tr> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/112640_73ea49e9_1980003.png"/></td> |
| | | <td><img src="https://images.gitee.com/uploads/images/2021/0413/112804_a21e5aef_1980003.png"/></td> |
| | | </tr> |
| | | </table> |
| | | |
| | | ### 框架亮点及优势 |
| | | |
| | | 1. 模块化架构设计,层次清晰,业务层推荐写到单独模块,框架升级不影响业务。 |
| | | ``` |
| | | 模块树 |
| | | ├─snowy ->项目工程 |
| | | │ ├─snowy-base ->框架基础模块 |
| | | │ ├─snowy-core ->核心模块 |
| | | │ ├─snowy-gen ->代码生成 |
| | | │ ├─snowy-system ->基础业务 |
| | | │ ├─snowy-main ->业务开始模块 |
| | | │ ├─业务 ->您的业务 |
| | | ``` |
| | | 2、独创前端字典翻译 |
| | | |
| | | 全部字典数据储存前端store,后端接口数据统一过滤器翻译 |
| | | |
| | | 下拉框,多选框等取值只需1行代码:('dictData'为过滤器名称,'sex'为字典类型code)返回数组字典 |
| | | ``` |
| | | this.$options.filters['dictData']('sex') |
| | | 或直接给值 |
| | | {{ code | dictData }} |
| | | ``` |
| | | |
| | | 列表数据中字典翻译:('code'为字典类型唯一code,'value'为待翻译的值)返回name |
| | | ``` |
| | | {{ code | dictType(value) }} |
| | | ``` |
| | | |
| | | 3、独创的数据权限范围机制 |
| | | |
| | | 数据范围的分配也来自于给用户单独分配的数据范围,最终决定用户有几个公司的数据范围的是,用户拥有的角色的数据范围 + 用户直接分配的数据范围 |
| | | |
| | | 若一个用户有多个角色,系统最终判定用户有哪些数据范围是以多个角色和用户数据范围的 并集 为准。 |
| | | |
| | | 仅通过注解就可以获取当前用户的数据范围,不强制联查sql可根据业务需求极其灵活的使用 |
| | | ``` |
| | | @DataScope |
| | | ``` |
| | | param类继承baseparam,使用param.getDadaScope即可获取到数据权限列表 |
| | | |
| | | ``` |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class SysUserParam extends BaseParam { |
| | | ``` |
| | | |
| | | 4、独创的文件预览系统 |
| | | |
| | | 支持txt.doc.docx.ppt.pptx.xls.xlsx.pdf.png.jpg.jpeg.bmp.gif等 |
| | | |
| | | 预览速度快,兼容性好,支持常见文本格式.只需在运行环境一键安装libreoffice即可,运行简单,操作方便。 |
| | | |
| | | ``` |
| | | #libreoffice文档在线预览配置 |
| | | # CentOS 下安装 libreoffice: |
| | | # 安装:yum -y install libreoffice |
| | | # Linux 中文字体乱码解决: |
| | | # 1、上传 C:\Windows\Fonts 下的字体到 /usr/share/fonts/windows 目录 |
| | | # 2、执行命令: chmod 644 /usr/share/fonts/windows/* && fc-cache -fv |
| | | jodconverter: |
| | | local: |
| | | #暂时关闭预览,启动时会有点慢 |
| | | enabled: false |
| | | #设置libreoffice主目录 linux地址如:/usr/lib64/libreoffice |
| | | office-home: C:\Program Files\LibreOffice |
| | | #开启多个libreoffice进程,每个端口对应一个进程 |
| | | port-numbers: 8100 |
| | | #libreoffice进程重启前的最大进程数 |
| | | max-tasks-per-process: 100 |
| | | ``` |
| | | 5、其他优势 |
| | | |
| | | 前后端分离架构,分离开发,分离部署,前后端互不影响。 |
| | | |
| | | 前端技术采用vue + antdvPro + axios。 |
| | | |
| | | 后端采用spring boot + mybatis-plus + hutool等,开源可靠。 |
| | | |
| | | 基于spring security(jwt) + 用户UUID双重认证。 |
| | | |
| | | 基于AOP实现的接口粒度的鉴权,最细粒度过滤权限资源。 |
| | | |
| | | 基于hibernate validator实现的校验框架,支持自定义校验注解。 |
| | | |
| | | 提供Request-No的响应header快速定位线上异常问题。 |
| | | |
| | | 在线用户可查,可在线踢人,同账号登录可同时在线,可单独在线(通过系统参数配置)。 |
| | | |
| | | 支持前端 + 后端在线代码生成。 |
| | | |
| | | 文件,短信,缓存,邮件等,利用接口封装,方便拓展。 |
| | | |
| | | 短信默认使用阿里云sms,缓存默认使用内存缓存。 |
| | | |
| | | ### 框架说明及后续补充 |
| | | |
| | | * 纯手研发搭建框架脚手架,在自己用的时候,也为各位小伙伴打下坚固的接私活利器。 |
| | | * 后续我们会行发多个版本,将适配多个数据库环境,国产化环境,并且根据多年经验会出相关系统中用到的案例,提供给大家使用! |
| | | * 如需了解我们更多,请移步官网:https://xiaonuo.vip |
| | | * 当然,有问题讨论的小伙伴还可以加入我们的QQ技术群:[732230670](https://wpa.qq.com/msgrd?v=3&uin=732230670&_blank),一起学习讨论。 |
| | | |
| | | ### 详细功能 |
| | | |
| | | 1. 主控面板、控制台页面,可进行工作台,分析页,统计等功能的展示。 |
| | | 2. 用户管理、对企业用户和系统管理员用户的维护,可绑定用户职务,机构,角色,数据权限等。 |
| | | 3. 应用管理、通过应用来控制不同维度的菜单展示。 |
| | | 4. 机构管理、公司组织架构维护,支持多层级结构的树形结构。 |
| | | 5. 职位管理、用户职务管理,职务可作为用户的一个标签,职务目前没有和权限等其他功能挂钩。 |
| | | 6. 菜单管理、菜单目录,菜单,和按钮的维护是权限控制的基本单位。 |
| | | 7. 角色管理、角色绑定菜单后,可限制相关角色的人员登录系统的功能范围。角色也可以绑定数据授权范围。 |
| | | 8. 字典管理、系统内各种枚举类型的维护。 |
| | | 9. 访问日志、用户的登录和退出日志的查看和管理。 |
| | | 10. 操作日志、用户的操作业务的日志的查看和管理。 |
| | | 11. 服务监控、服务器的运行状态,Java虚拟机信息,jvm等数据的查看。 |
| | | 12. 在线用户、当前系统在线用户的查看。 |
| | | 13. 数据监控、druid控制台功能,可查看sql的运行信息。 |
| | | 14. 公告管理、系统的公告的管理。 |
| | | 15. 文件管理、文件的上传下载查看等操作,文件可使用本地存储,阿里云oss,腾讯cos接入,支持拓展。 |
| | | 16. 定时任务、定时任务的维护,通过cron表达式控制任务的执行频率。 |
| | | 17. 系统配置、系统运行的参数的维护,参数的配置与系统运行机制息息相关。 |
| | | 18. 邮件发送、发送邮件功能。 |
| | | 19. 短信发送、短信发送功能,可使用阿里云sms,腾讯云sms,支持拓展。 |
| | | |
| | | ### 官方微信群 |
| | | |
| | | ##### 因群达到200人以上,需加微信拉群 |
| | | |
| | | <table> |
| | | <tr> |
| | | <td>微信群</td> |
| | | <td><img src="https://pan.xiaonuo.vip/?explorer/share/fileOut&shareID=7qwFVcdA&path=%7BshareItemLink%3A7qwFVcdA%7D%2F" width="120"/></td> |
| | | </tr> |
| | | </table> |
| | | |
| | | ### 参与贡献 |
| | | |
| | | - 欢迎各路英雄好汉参与Snowy全系版本代码贡献,期待您的加入! |
| | | - 1. Fork 本仓库 |
| | | - 2. 新建 Feat_xxx 分支 |
| | | - 3. 提交代码 |
| | | - 4. 新建 Pull Request |
| | | |
| | | ### 更新日志: |
| | | |
| | | 更新日志:https://doc.xiaonuo.vip/snowy_vue/#%E6%9B%B4%E6%96%B0%E6%97%A5%E5%BF%97 |
| | | |
| | | ### 版权说明 |
| | | |
| | | - Snowy生态技术框架全系版本采用 Apache License2.0协议 |
| | | - 代码可用于个人项目等接私活或企业项目脚手架使用,Snowy全系开源版完全免费 |
| | | - 二次开发如用于商业性质或开源竞品请先联系群主审核。 |
| | | - 请不要删除和修改Snowy源码头部的版权与作者声明及出处。 |
| | | |
| | | ### 小诺技术团队荣誉作品 |
| | | |
| | | | 成员组成 | 负责内容 | |
| | | | :---: | :---: | |
| | | | 俞宝山 | 全栈 | |
| | | | 徐玉祥 | 全栈 | |
| | | | 董夏雨 | 全栈 | |
New file |
| | |
| | | > 1% |
| | | last 2 versions |
| | | not ie <= 10 |
New file |
| | |
| | | [*] |
| | | charset=utf-8 |
| | | end_of_line=lf |
| | | insert_final_newline=false |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [*.svg] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [*.js.map] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [*.less] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [*.vue] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
| | | [{.analysis_options,*.yml,*.yaml}] |
| | | indent_style=space |
| | | indent_size=2 |
| | | |
New file |
| | |
| | | NODE_ENV=production |
| | | VUE_APP_PREVIEW=true |
| | | VUE_APP_API_BASE_URL=http://localhost:82 |
New file |
| | |
| | | NODE_ENV=development |
| | | VUE_APP_PREVIEW=true |
| | | VUE_APP_API_BASE_URL=http://localhost:82 |
New file |
| | |
| | | NODE_ENV=production |
| | | VUE_APP_PREVIEW=false |
| | | VUE_APP_API_BASE_URL=http://localhost:82 |
New file |
| | |
| | | module.exports = { |
| | | root: true, |
| | | env: { |
| | | node: true |
| | | }, |
| | | 'extends': [ |
| | | 'plugin:vue/strongly-recommended', |
| | | '@vue/standard' |
| | | ], |
| | | rules: { |
| | | 'no-console': 'off', |
| | | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', |
| | | 'generator-star-spacing': 'off', |
| | | 'no-mixed-operators': 0, |
| | | 'vue/max-attributes-per-line': [ |
| | | 2, |
| | | { |
| | | 'singleline': 5, |
| | | 'multiline': { |
| | | 'max': 1, |
| | | 'allowFirstLine': false |
| | | } |
| | | } |
| | | ], |
| | | 'vue/attribute-hyphenation': 0, |
| | | 'vue/html-self-closing': 0, |
| | | 'vue/component-name-in-template-casing': 0, |
| | | 'vue/html-closing-bracket-spacing': 0, |
| | | 'vue/singleline-html-element-content-newline': 0, |
| | | 'vue/no-unused-components': 0, |
| | | 'vue/multiline-html-element-content-newline': 0, |
| | | 'vue/no-use-v-if-with-v-for': 0, |
| | | 'vue/html-closing-bracket-newline': 0, |
| | | 'vue/no-parsing-error': 0, |
| | | 'no-tabs': 0, |
| | | 'quotes': [ |
| | | 2, |
| | | 'single', |
| | | { |
| | | 'avoidEscape': true, |
| | | 'allowTemplateLiterals': true |
| | | } |
| | | ], |
| | | 'semi': [ |
| | | 2, |
| | | 'never', |
| | | { |
| | | 'beforeStatementContinuationChars': 'never' |
| | | } |
| | | ], |
| | | 'no-delete-var': 2, |
| | | 'prefer-const': [ |
| | | 2, |
| | | { |
| | | 'ignoreReadBeforeAssign': false |
| | | } |
| | | ], |
| | | 'template-curly-spacing': 'off', |
| | | 'indent': 'off', |
| | | "space-before-function-paren": 0, |
| | | 'no-multi-spaces': 2, //不能用多余的空格 |
| | | }, |
| | | parserOptions: { |
| | | parser: 'babel-eslint' |
| | | }, |
| | | overrides: [ |
| | | { |
| | | files: [ |
| | | '**/__tests__/*.{j,t}s?(x)', |
| | | '**/tests/unit/**/*.spec.{j,t}s?(x)' |
| | | ], |
| | | env: { |
| | | jest: true |
| | | } |
| | | } |
| | | ] |
| | | } |
New file |
| | |
| | | node_modules/ |
| | | dist/ |
| | | .idea/ |
New file |
| | |
| | | { |
| | | "printWidth": 120, |
| | | "semi": false, |
| | | "singleQuote": true |
| | | } |
New file |
| | |
| | | language: node_js |
| | | node_js: |
| | | - 10.15.0 |
| | | cache: yarn |
| | | script: |
| | | - yarn |
| | | - yarn run lint --no-fix && yarn run build |
New file |
| | |
| | | MIT License |
| | | |
| | | Copyright (c) 2018 Anan Yang |
| | | |
| | | 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. |
New file |
| | |
| | | const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV) |
| | | |
| | | const plugins = [] |
| | | if (IS_PROD) { |
| | | plugins.push('transform-remove-console') |
| | | } |
| | | |
| | | // lazy load ant-design-vue |
| | | // if your use import on Demand, Use this code |
| | | plugins.push(['import', { |
| | | 'libraryName': 'ant-design-vue', |
| | | 'libraryDirectory': 'es', |
| | | 'style': true // `style: true` 会加载 less 文件 |
| | | }]) |
| | | |
| | | module.exports = { |
| | | presets: [ |
| | | '@vue/cli-plugin-babel/preset', |
| | | [ |
| | | '@babel/preset-env', |
| | | { |
| | | 'useBuiltIns': 'entry', |
| | | 'corejs': 3 |
| | | } |
| | | ] |
| | | ], |
| | | plugins |
| | | } |
New file |
| | |
| | | const ThemeColorReplacer = require('webpack-theme-color-replacer') |
| | | const generate = require('@ant-design/colors/lib/generate').default |
| | | |
| | | const getAntdSerials = (color) => { |
| | | // 淡化(即less的tint) |
| | | const lightens = new Array(9).fill().map((t, i) => { |
| | | return ThemeColorReplacer.varyColor.lighten(color, i / 10) |
| | | }) |
| | | const colorPalettes = generate(color) |
| | | const rgb = ThemeColorReplacer.varyColor.toNum3(color.replace('#', '')).join(',') |
| | | return lightens.concat(colorPalettes).concat(rgb) |
| | | } |
| | | |
| | | const themePluginOption = { |
| | | fileName: 'css/theme-colors-[contenthash:8].css', |
| | | matchColors: getAntdSerials('#1890ff'), // 主色系列 |
| | | // 改变样式选择器,解决样式覆盖问题 |
| | | changeSelector (selector) { |
| | | switch (selector) { |
| | | case '.ant-calendar-today .ant-calendar-date': |
| | | return ':not(.ant-calendar-selected-date):not(.ant-calendar-selected-day)' + selector |
| | | case '.ant-btn:focus,.ant-btn:hover': |
| | | return '.ant-btn:focus:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:hover:not(.ant-btn-primary):not(.ant-btn-danger)' |
| | | case '.ant-btn.active,.ant-btn:active': |
| | | return '.ant-btn.active:not(.ant-btn-primary):not(.ant-btn-danger),.ant-btn:active:not(.ant-btn-primary):not(.ant-btn-danger)' |
| | | case '.ant-steps-item-process .ant-steps-item-icon > .ant-steps-icon': |
| | | case '.ant-steps-item-process .ant-steps-item-icon>.ant-steps-icon': |
| | | return ':not(.ant-steps-item-process)' + selector |
| | | case '.ant-menu-horizontal>.ant-menu-item-active,.ant-menu-horizontal>.ant-menu-item-open,.ant-menu-horizontal>.ant-menu-item-selected,.ant-menu-horizontal>.ant-menu-item:hover,.ant-menu-horizontal>.ant-menu-submenu-active,.ant-menu-horizontal>.ant-menu-submenu-open,.ant-menu-horizontal>.ant-menu-submenu-selected,.ant-menu-horizontal>.ant-menu-submenu:hover': |
| | | case '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal > .ant-menu-submenu-selected,.ant-menu-horizontal > .ant-menu-submenu:hover': |
| | | return '.ant-menu-horizontal > .ant-menu-item-active,.ant-menu-horizontal > .ant-menu-item-open,.ant-menu-horizontal > .ant-menu-item-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-item:hover,.ant-menu-horizontal > .ant-menu-submenu-active,.ant-menu-horizontal > .ant-menu-submenu-open,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu-selected,.ant-menu-horizontal:not(.ant-menu-dark) > .ant-menu-submenu:hover' |
| | | case '.ant-menu-horizontal > .ant-menu-item-selected > a': |
| | | case '.ant-menu-horizontal>.ant-menu-item-selected>a': |
| | | return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item-selected > a' |
| | | case '.ant-menu-horizontal > .ant-menu-item > a:hover': |
| | | case '.ant-menu-horizontal>.ant-menu-item>a:hover': |
| | | return '.ant-menu-horizontal:not(ant-menu-light):not(.ant-menu-dark) > .ant-menu-item > a:hover' |
| | | default : |
| | | return selector |
| | | } |
| | | } |
| | | } |
| | | |
| | | const createThemeColorReplacerPlugin = () => new ThemeColorReplacer(themePluginOption) |
| | | |
| | | module.exports = createThemeColorReplacerPlugin |
New file |
| | |
| | | module.exports = { |
| | | moduleFileExtensions: [ |
| | | 'js', |
| | | 'jsx', |
| | | 'json', |
| | | 'vue' |
| | | ], |
| | | transform: { |
| | | '^.+\\.vue$': 'vue-jest', |
| | | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', |
| | | '^.+\\.jsx?$': 'babel-jest' |
| | | }, |
| | | moduleNameMapper: { |
| | | '^@/(.*)$': '<rootDir>/src/$1' |
| | | }, |
| | | snapshotSerializers: [ |
| | | 'jest-serializer-vue' |
| | | ], |
| | | testMatch: [ |
| | | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' |
| | | ], |
| | | testURL: 'http://localhost/' |
| | | } |
New file |
| | |
| | | { |
| | | "compilerOptions": { |
| | | "target": "es6", |
| | | "baseUrl": ".", |
| | | "paths": { |
| | | "@/*": ["src/*"] |
| | | } |
| | | }, |
| | | "exclude": ["node_modules", "dist"], |
| | | "include": ["src/**/*"] |
| | | } |
New file |
| | |
| | | { |
| | | "name": "vue-antd-pro", |
| | | "version": "2.1.0", |
| | | "private": true, |
| | | "scripts": { |
| | | "serve": "vue-cli-service serve", |
| | | "build": "vue-cli-service build", |
| | | "test:unit": "vue-cli-service test:unit", |
| | | "build:preview": "vue-cli-service build --mode preview", |
| | | "postinstall": "opencollective-postinstall" |
| | | }, |
| | | "dependencies": { |
| | | "@antv/data-set": "^0.10.2", |
| | | "ant-design-vue": "1.5.0-rc.6", |
| | | "axios": "^0.19.0", |
| | | "babel-polyfill": "^6.26.0", |
| | | "clipboard": "^2.0.6", |
| | | "compression-webpack-plugin": "5.0.1", |
| | | "core-js": "^3.1.2", |
| | | "crypto-js": "^4.0.0", |
| | | "default-passive-events": "^1.0.10", |
| | | "enquire.js": "^2.1.6", |
| | | "font-awesome": "^4.7.0", |
| | | "jquery": "^3.5.1", |
| | | "lodash.clonedeep": "^4.5.0", |
| | | "lodash.get": "^4.4.2", |
| | | "lodash.pick": "^4.4.0", |
| | | "md5": "^2.2.1", |
| | | "mockjs2": "1.0.8", |
| | | "moment": "^2.24.0", |
| | | "nprogress": "^0.2.0", |
| | | "print-js": "^1.0.63", |
| | | "raphael": "^2.3.0", |
| | | "screenfull": "^5.1.0", |
| | | "sm-crypto": "^0.3.6", |
| | | "viser-vue": "^2.4.6", |
| | | "vue": "2.6.10", |
| | | "vue-clipboard2": "^0.2.1", |
| | | "vue-codemirror-lite": "^1.0.4", |
| | | "vue-cropper": "0.4.9", |
| | | "vue-ls": "^3.2.1", |
| | | "vue-quill-editor": "^3.0.6", |
| | | "vue-router": "^3.1.2", |
| | | "vue-svg-component-runtime": "^1.0.1", |
| | | "vuedraggable": "^2.23.2", |
| | | "vuex": "^3.1.1", |
| | | "wangeditor": "^3.1.1" |
| | | }, |
| | | "devDependencies": { |
| | | "@ant-design/colors": "^3.2.1", |
| | | "@vue/cli-plugin-babel": "^4.0.4", |
| | | "@vue/cli-plugin-eslint": "^4.0.4", |
| | | "@vue/cli-plugin-router": "^4.0.4", |
| | | "@vue/cli-plugin-unit-jest": "^4.0.4", |
| | | "@vue/cli-plugin-vuex": "^4.0.4", |
| | | "@vue/cli-service": "^4.0.4", |
| | | "@vue/eslint-config-prettier": "^5.0.0", |
| | | "@vue/eslint-config-standard": "^4.0.0", |
| | | "@vue/test-utils": "^1.0.0-beta.29", |
| | | "babel-eslint": "^10.0.1", |
| | | "babel-plugin-import": "^1.13.0", |
| | | "babel-plugin-transform-remove-console": "^6.9.4", |
| | | "eslint": "^6.8.0", |
| | | "eslint-plugin-html": "^5.0.0", |
| | | "eslint-plugin-prettier": "^3.1.0", |
| | | "eslint-plugin-vue": "^5.2.3", |
| | | "less": "^3.0.4", |
| | | "less-loader": "^5.0.0", |
| | | "opencollective": "^1.0.3", |
| | | "opencollective-postinstall": "^2.0.2", |
| | | "prettier": "^1.18.2", |
| | | "vue-svg-icon-loader": "^2.1.1", |
| | | "vue-template-compiler": "2.6.10", |
| | | "webpack-theme-color-replacer": "1.3.18" |
| | | }, |
| | | "collective": { |
| | | "type": "opencollective", |
| | | "url": "https://opencollective.com/ant-design-pro-vue" |
| | | }, |
| | | "main": ".eslintrc.js", |
| | | "directories": { |
| | | "test": "tests" |
| | | }, |
| | | "keywords": [], |
| | | "author": "", |
| | | "license": "ISC", |
| | | "description": "" |
| | | } |
New file |
| | |
| | | module.exports = { |
| | | plugins: { |
| | | autoprefixer: {} |
| | | } |
| | | } |
New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-cmn-Hans"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge"> |
| | | <meta name="viewport" content="width=device-width,initial-scale=1.0"> |
| | | <link rel="icon" href="<%= BASE_URL %>logo.png"> |
| | | <title>Snowy快速开发平台</title> |
| | | |
| | | <style>.first-loading-wrp{display:flex;justify-content:center;align-items:center;flex-direction:column;min-height:420px;height:100%}.first-loading-wrp>h1{font-size:128px}.first-loading-wrp .loading-wrp{padding:98px;display:flex;justify-content:center;align-items:center}.dot{animation:antRotate 1.2s infinite linear;transform:rotate(45deg);position:relative;display:inline-block;font-size:32px;width:32px;height:32px;box-sizing:border-box}.dot i{width:14px;height:14px;position:absolute;display:block;background-color:#1890ff;border-radius:100%;transform:scale(.75);transform-origin:50% 50%;opacity:.3;animation:antSpinMove 1s infinite linear alternate}.dot i:nth-child(1){top:0;left:0}.dot i:nth-child(2){top:0;right:0;-webkit-animation-delay:.4s;animation-delay:.4s}.dot i:nth-child(3){right:0;bottom:0;-webkit-animation-delay:.8s;animation-delay:.8s}.dot i:nth-child(4){bottom:0;left:0;-webkit-animation-delay:1.2s;animation-delay:1.2s}@keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@-webkit-keyframes antRotate{to{-webkit-transform:rotate(405deg);transform:rotate(405deg)}}@keyframes antSpinMove{to{opacity:1}}@-webkit-keyframes antSpinMove{to{opacity:1}}</style> |
| | | <!-- <script type="text/javascript" src="https://onlyoffice.xiaonuo.vip/web-apps/apps/api/documents/api.js"></script> --> |
| | | <!-- require cdn assets css --> |
| | | <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %> |
| | | <link rel="stylesheet" href="<%= htmlWebpackPlugin.options.cdn.css[i] %>" /> |
| | | <% } %> |
| | | |
| | | </head> |
| | | <body> |
| | | <noscript> |
| | | <strong>We're sorry but vue-antd-pro doesn't work properly without JavaScript enabled. Please enable it to continue.</strong> |
| | | </noscript> |
| | | <div id="app"> |
| | | <div class="first-loading-wrp"> |
| | | <h1>Snowy</h1> |
| | | <div class="loading-wrp"> |
| | | <span class="dot dot-spin"><i></i><i></i><i></i><i></i></span> |
| | | </div> |
| | | <div style="display: flex; justify-content: center; align-items: center;">Snowy快速开发平台</div> |
| | | </div> |
| | | </div> |
| | | <!-- require cdn assets js --> |
| | | <% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %> |
| | | <script src="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script> |
| | | <% } %> |
| | | <!-- built files will be auto injected --> |
| | | </body> |
| | | </html> |
New file |
| | |
| | | #preloadingAnimation{position:fixed;left:0;top:0;height:100%;width:100%;background:#ffffff;user-select:none;z-index: 9999;overflow: hidden}.lds-roller{display:inline-block;position:relative;left:50%;top:50%;transform:translate(-50%,-50%);width:64px;height:64px;}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(0.5,0,0.5,1) infinite;transform-origin:32px 32px;}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#13c2c2;margin:-3px 0 0 -3px;}.lds-roller div:nth-child(1){animation-delay:-0.036s;}.lds-roller div:nth-child(1):after{top:50px;left:50px;}.lds-roller div:nth-child(2){animation-delay:-0.072s;}.lds-roller div:nth-child(2):after{top:54px;left:45px;}.lds-roller div:nth-child(3){animation-delay:-0.108s;}.lds-roller div:nth-child(3):after{top:57px;left:39px;}.lds-roller div:nth-child(4){animation-delay:-0.144s;}.lds-roller div:nth-child(4):after{top:58px;left:32px;}.lds-roller div:nth-child(5){animation-delay:-0.18s;}.lds-roller div:nth-child(5):after{top:57px;left:25px;}.lds-roller div:nth-child(6){animation-delay:-0.216s;}.lds-roller div:nth-child(6):after{top:54px;left:19px;}.lds-roller div:nth-child(7){animation-delay:-0.252s;}.lds-roller div:nth-child(7):after{top:50px;left:14px;}.lds-roller div:nth-child(8){animation-delay:-0.288s;}.lds-roller div:nth-child(8):after{top:45px;left:10px;}#preloadingAnimation .load-tips{color: #13c2c2;font-size:2rem;position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);margin-top:80px;text-align:center;width:400px;height:64px;} @keyframes lds-roller{0%{transform:rotate(0deg);} 100%{transform:rotate(360deg);}} |
New file |
| | |
| | | <div id="preloadingAnimation"><div class=lds-roller><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><div class=load-tips>Loading</div></div> |
New file |
| | |
| | | <div class="preloading-animate"> |
| | | <div class="preloading-wrapper"> |
| | | <svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg> |
| | | </div> |
| | | </div> |
New file |
| | |
| | | .preloading-animate{background:#ffffff;width:100%;height:100%;position:fixed;left:0;top:0;z-index:299;}.preloading-animate .preloading-wrapper{position:absolute;width:5rem;height:5rem;left:50%;top:50%;transform:translate(-50%,-50%);}.preloading-animate .preloading-wrapper .preloading-balls{font-size:5rem;} |
New file |
| | |
| | | <svg class="preloading-balls" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid"><circle cx="67.802" cy="59.907" r="6" fill="#51CACC"><animate attributeName="cx" values="75;57.72542485937369" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="50;73.77641290737884" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#51CACC;#9DF871" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="46.079" cy="69.992" r="6" fill="#9DF871"><animate attributeName="cx" values="57.72542485937369;29.774575140626318" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="73.77641290737884;64.69463130731182" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#9DF871;#E0FF77" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="29.775" cy="52.449" r="6" fill="#E0FF77"><animate attributeName="cx" values="29.774575140626318;29.774575140626315" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="64.69463130731182;35.30536869268818" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#E0FF77;#DE9DD6" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="41.421" cy="31.521" r="6" fill="#DE9DD6"><animate attributeName="cx" values="29.774575140626315;57.72542485937368" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="35.30536869268818;26.22358709262116" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#DE9DD6;#FF708E" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle><circle cx="64.923" cy="36.13" r="6" fill="#FF708E"><animate attributeName="cx" values="57.72542485937368;75" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="cy" values="26.22358709262116;49.99999999999999" keyTimes="0;1" dur="1s" repeatCount="indefinite"/><animate attributeName="fill" values="#FF708E;#51CACC" keyTimes="0;1" dur="1s" repeatCount="indefinite"/></circle></svg> |
New file |
| | |
| | | <template> |
| | | <a-config-provider :locale="locale"> |
| | | <div id="app" class="app app1"> |
| | | <router-view class="scrollbar"/> |
| | | </div> |
| | | </a-config-provider> |
| | | </template> |
| | | |
| | | <script> |
| | | import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN' |
| | | import { AppDeviceEnquire } from '@/utils/mixin' |
| | | |
| | | export default { |
| | | mixins: [AppDeviceEnquire], |
| | | data () { |
| | | return { |
| | | locale: zhCN |
| | | } |
| | | }, |
| | | mounted () { |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | <style> |
| | | .app { |
| | | overflow: auto; |
| | | border : none; |
| | | } |
| | | .scrollbar { |
| | | margin: 0 auto; |
| | | } |
| | | .app1::-webkit-scrollbar { |
| | | /*滚动条整体样式*/ |
| | | width : 8px; /*高宽分别对应横竖滚动条的尺寸*/ |
| | | } |
| | | .app1::-webkit-scrollbar-thumb { |
| | | /*滚动条里面小方块*/ |
| | | border-radius: 6px; |
| | | background : #aaa; |
| | | } |
| | | .app1::-webkit-scrollbar-track { |
| | | /*滚动条里面轨道*/ |
| | | border-radius: 8px; |
| | | background : #FFFFFF; |
| | | } |
| | | </style> |
New file |
| | |
| | | /** |
| | | * 代码生成 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGeneratePage (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 增加 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateAdd (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateEdit (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateDelete (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查询当前数据库用户下的所有表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateInformationList (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/InformationList', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 本地生成 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateRunLocal (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/runLocal', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 压缩包方式下载 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/12/23 15:00 |
| | | */ |
| | | export function codeGenerateRunDown (parameter) { |
| | | return axios({ |
| | | url: '/codeGenerate/runDown', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 代码生成详细配置列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021-02-06 20:19:49 |
| | | */ |
| | | export function sysCodeGenerateConfigList (parameter) { |
| | | return axios({ |
| | | url: '/sysCodeGenerateConfig/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑代码生成详细配置 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021-02-06 20:19:49 |
| | | */ |
| | | export function sysCodeGenerateConfigEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysCodeGenerateConfig/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | /** 您的业务接口文件全写在此文件夹下面,升级底座直接迁移代码即可 **/ |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询blog文章主体 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticlePage (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * blog文章主体列表 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticleList (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加blog文章主体 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticleAdd (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑blog文章主体 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticleEdit (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除blog文章主体 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticleDelete (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出blog文章主体 |
| | | * |
| | | * @author inleft |
| | | * @date 2022-01-22 16:53:06 |
| | | */ |
| | | export function blogArticleExport (parameter) { |
| | | return axios({ |
| | | url: '/blogArticle/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
New file |
| | |
| | | /** 此文件夹下代码尽量不要动,底座升级直接覆盖替换 **/ |
New file |
| | |
| | | /** |
| | | * 系统应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年4月23日12:10:57 |
| | | */ |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 系统应用列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年7月9日15:05:01 |
| | | */ |
| | | export function getAppPage (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 系统应用列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年7月9日15:05:01 |
| | | */ |
| | | export function getAppList (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 新增系统应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年7月9日15:05:01 |
| | | */ |
| | | export function sysAppAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @param parameter |
| | | * @returns {*} |
| | | */ |
| | | export function sysAppEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年7月9日15:05:01 |
| | | */ |
| | | export function sysAppDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 设为默认应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020年7月9日15:05:01 |
| | | */ |
| | | export function sysAppSetAsDefault (parameter) { |
| | | return axios({ |
| | | url: '/sysApp/setAsDefault', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 获取区域列表 |
| | | * |
| | | * @author xuyuxiang |
| | | * @param parameter |
| | | * @returns {*} |
| | | */ |
| | | export function getAreaList (parameter) { |
| | | return axios({ |
| | | url: '/sysArea/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 分页查询配置列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:57 |
| | | */ |
| | | export function sysConfigPage (parameter) { |
| | | return axios({ |
| | | url: '/sysConfig/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加系统参数配置 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:57 |
| | | */ |
| | | export function sysConfigAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysConfig/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统参数配置 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:57 |
| | | */ |
| | | export function sysConfigEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysConfig/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统参数配置 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:57 |
| | | */ |
| | | export function sysConfigDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysConfig/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取字典类型下所有字典,举例,返回格式为:[{code:"M",value:"男"},{code:"F",value:"女"}] |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 02:06 |
| | | */ |
| | | export function sysDictTypeDropDown (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/dropDown', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取系统的所有任务列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/8 20:46 |
| | | */ |
| | | export function sysTimersGetActionClasses (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/getActionClasses', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询系统字典值 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 02:24 |
| | | */ |
| | | export function sysDictDataPage (parameter) { |
| | | return axios({ |
| | | url: '/sysDictData/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加系统字典值 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 02:24 |
| | | */ |
| | | export function sysDictDataAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysDictData/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统字典值 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 02:25 |
| | | */ |
| | | export function sysDictDataEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysDictData/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统字典值 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 02:25 |
| | | */ |
| | | export function sysDictDataDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysDictData/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 分页查询系统字典类型 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 01:46 |
| | | */ |
| | | export function sysDictTypePage (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加系统字典类型 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 01:46 |
| | | */ |
| | | export function sysDictTypeAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统字典类型 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 01:50 |
| | | */ |
| | | export function sysDictTypeEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统字典类型 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/17 01:50 |
| | | */ |
| | | export function sysDictTypeDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取字典类型下所有字典,举例,返回格式为:[{code:"M",value:"男"},{code:"F",value:"女"}] |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/10 00:10 |
| | | */ |
| | | export function sysDictTypeDropDown (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/dropDown', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取所有字典,启动时加入缓存使用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/10 00:10 |
| | | */ |
| | | export function sysDictTypeTree (parameter) { |
| | | return axios({ |
| | | url: '/sysDictType/tree', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 发送邮件 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 23:22 |
| | | */ |
| | | export function emailSendEmail (parameter) { |
| | | return axios({ |
| | | url: '/email/sendEmail', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 发送html邮件 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 23:23 |
| | | */ |
| | | export function emailSendEmailHtml (parameter) { |
| | | return axios({ |
| | | url: '/email/sendEmailHtml', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 分页查询文件信息表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoPage (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取全部文件信息表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoList (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 上传文件 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoUpload (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/upload', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 下载文件 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoDownload (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/download', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查看图片 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoPreview (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/preview', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'arraybuffer' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查看详情文件信息表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoDetail (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/detail', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除文件信息表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取在线文档配置 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 00:20 |
| | | */ |
| | | export function sysFileInfoGetOnlineConfig (parameter) { |
| | | return axios({ |
| | | url: '/sysFileInfo/getOnlineFileConfig', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询访问日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/19 11:57 |
| | | */ |
| | | export function sysVisLogPage (parameter) { |
| | | return axios({ |
| | | url: '/sysVisLog/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查询操作日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/19 11:57 |
| | | */ |
| | | export function sysOpLogPage (parameter) { |
| | | return axios({ |
| | | url: '/sysOpLog/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 清空访问日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/23 23:09 |
| | | */ |
| | | export function sysVisLogDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysVisLog/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 清空登录日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/23 23:09 |
| | | */ |
| | | export function sysOpLogDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysOpLog/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出登录日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021/5/30 18:03 |
| | | */ |
| | | export function sysVisLogExport (parameter) { |
| | | return axios({ |
| | | url: '/sysVisLog/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出操作日志 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021/5/30 18:03 |
| | | */ |
| | | export function sysOpLogExport (parameter) { |
| | | return axios({ |
| | | url: '/sysOpLog/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
New file |
| | |
| | | /** |
| | | * 系统应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 19:06 |
| | | */ |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 登录 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 19:06 |
| | | */ |
| | | export function login (parameter) { |
| | | // 密码采用sm2加密传输密码 |
| | | const sm2 = require('sm-crypto').sm2 |
| | | const publicKey = '04298364ec840088475eae92a591e01284d1abefcda348b47eb324bb521bb03b0b2a5bc393f6b71dabb8f15c99a0050818b56b23f31743b93df9cf8948f15ddb54' |
| | | const encryptData = sm2.doEncrypt(parameter.password, publicKey, 1) |
| | | parameter.password = encryptData |
| | | |
| | | return axios({ |
| | | url: '/login', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 登出 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 19:07 |
| | | */ |
| | | export function logout (parameter) { |
| | | return axios({ |
| | | url: '/logout', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取登录用户信息 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 19:08 |
| | | */ |
| | | export function getLoginUser (parameter) { |
| | | return axios({ |
| | | url: '/getLoginUser', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取租户开关 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/9/5 1:24 |
| | | */ |
| | | export function getTenantOpen (parameter) { |
| | | return axios({ |
| | | url: '/getTenantOpen', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取短信验证码 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 19:29 |
| | | */ |
| | | export function getSmsCaptcha (parameter) { |
| | | return axios({ |
| | | url: '/getSmsCaptcha', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取验证码开关 |
| | | * |
| | | * @author Jax |
| | | * @date 2021/1/22 00:00 |
| | | */ |
| | | export function getCaptchaOpen (parameter) { |
| | | return axios({ |
| | | url: '/getCaptchaOpen', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取验证图片 以及token |
| | | * |
| | | * @author Jax |
| | | * @date 2021/1/22 00:00 |
| | | */ |
| | | export function reqGet(data) { |
| | | return axios({ |
| | | url: '/captcha/get', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 滑动或者点选验证 |
| | | * |
| | | * @author Jax |
| | | * @date 2021/1/22 00:00 |
| | | */ |
| | | export function reqCheck(data) { |
| | | return axios({ |
| | | url: '/captcha/check', |
| | | method: 'post', |
| | | data |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 系统属性监控 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/8 19:47 |
| | | */ |
| | | export function sysMachineQuery (parameter) { |
| | | return axios({ |
| | | url: '/sysMachine/query', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 获取菜单列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @param parameter |
| | | * @returns {*} |
| | | */ |
| | | export function getMenuList (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取系统菜单树,用于新增,编辑时选择上级节点 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/23 12:22 |
| | | */ |
| | | export function getMenuTree (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/tree', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 增加菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/24 23:23 |
| | | */ |
| | | export function sysMenuAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 增加菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/24 23:23 |
| | | */ |
| | | export function sysMenuDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查看菜单详情 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/25 01:11 |
| | | */ |
| | | export function sysMenuDetail (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/detail', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/25 01:11 |
| | | */ |
| | | export function sysMenuEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取系统菜单树,用于给角色授权时选择 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/2 17:30 |
| | | */ |
| | | export function SysMenuTreeForGrant (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/treeForGrant', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 根据系统切换菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/28 15:25 |
| | | */ |
| | | export function sysMenuChange (parameter) { |
| | | return axios({ |
| | | url: '/sysMenu/change', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询系统通知公告 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 01:56 |
| | | */ |
| | | export function sysNoticePage (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加系统通知公告 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 01:56 |
| | | */ |
| | | export function sysNoticeAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统通知公告 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 01:56 |
| | | */ |
| | | export function sysNoticeEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统通知公告 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 01:56 |
| | | */ |
| | | export function sysNoticeDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 通知公告详情 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/30 01:56 |
| | | */ |
| | | export function sysNoticeDetail (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/detail', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 修改状态 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/30 02:23 |
| | | */ |
| | | export function sysNoticeChangeStatus (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/changeStatus', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询我收到的系统通知公告 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:02 |
| | | */ |
| | | export function sysNoticeReceived (parameter) { |
| | | return axios({ |
| | | url: '/sysNotice/received', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 在线用户列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/8 11:11 |
| | | */ |
| | | export function sysOnlineUserList (parameter) { |
| | | return axios({ |
| | | url: '/sysOnlineUser/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 强制下线 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/8 11:11 |
| | | */ |
| | | export function sysOnlineUserForceExist (parameter) { |
| | | return axios({ |
| | | url: '/sysOnlineUser/forceExist', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 获取机构树 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/26 12:08 |
| | | */ |
| | | export function getOrgTree (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/tree', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取机构列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/11 12:59 |
| | | */ |
| | | export function getOrgList (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取机构列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/11 16:17 |
| | | */ |
| | | export function getOrgPage (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 新增机构 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/11 13:56 |
| | | */ |
| | | export function sysOrgAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑机构 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/11 13:56 |
| | | */ |
| | | export function sysOrgEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除机构 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/11 12:59 |
| | | */ |
| | | export function sysOrgDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出系统机构 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021/5/30 12:58 |
| | | */ |
| | | export function sysOrgExport (parameter) { |
| | | return axios({ |
| | | url: '/sysOrg/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 查询系统职位 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:31 |
| | | */ |
| | | export function sysPosPage (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 系统职位列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/21 23:50 |
| | | */ |
| | | export function sysPosList (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加系统职位 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:31 |
| | | */ |
| | | export function sysPosAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑系统职位 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:31 |
| | | */ |
| | | export function sysPosEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除系统职位 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/25 01:31 |
| | | */ |
| | | export function sysPosDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出系统职位 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021/5/29 16:19 |
| | | */ |
| | | export function sysPosExport (parameter) { |
| | | return axios({ |
| | | url: '/sysPos/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 获取角色列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/6 11:44 |
| | | */ |
| | | export function getRolePage (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 增加角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/6 11:44 |
| | | */ |
| | | export function sysRoleAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/6 11:44 |
| | | */ |
| | | export function sysRoleEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/6 17:51 |
| | | */ |
| | | export function sysRoleDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/7 11:28 |
| | | */ |
| | | export function sysRoleDeteil (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/detail', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取授权角色列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 23:59 |
| | | */ |
| | | export function sysRoleDropDown (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/dropDown', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 拥有菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/02 19:02 |
| | | */ |
| | | export function sysRoleOwnMenu (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/ownMenu', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 授权菜单 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/2 21:10 |
| | | */ |
| | | export function sysRoleGrantMenu (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/grantMenu', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 拥有数据 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/02 21:40 |
| | | */ |
| | | export function sysRoleOwnData (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/ownData', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 授权数据 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/2 21:50 |
| | | */ |
| | | export function sysRoleGrantData (parameter) { |
| | | return axios({ |
| | | url: '/sysRole/grantData', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 发送记录查询 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 22:11 |
| | | */ |
| | | export function smsPage (parameter) { |
| | | return axios({ |
| | | url: '/sms/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 验证短信验证码 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 22:12 |
| | | */ |
| | | export function sysSendLoginMessage (parameter) { |
| | | return axios({ |
| | | url: '/sms/sendLoginMessage', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 验证短信验证码 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 22:12 |
| | | */ |
| | | export function sysValidateMessage (parameter) { |
| | | return axios({ |
| | | url: '/sms/validateMessage', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 分页查询定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:13 |
| | | */ |
| | | export function sysTimersPage (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取全部定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersList (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/list', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 查看详情定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersDetail (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/detail', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 添加定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取系统的所有任务列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersGetActionClasses (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/getActionClasses', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 启动定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersStart (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/start', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 停止定时任务 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/7/3 03:23 |
| | | */ |
| | | export function sysTimersStop (parameter) { |
| | | return axios({ |
| | | url: '/sysTimers/stop', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | import { axios } from '@/utils/request' |
| | | |
| | | /** |
| | | * 获取用户列表 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/4/26 12:08 |
| | | */ |
| | | export function getUserPage (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/page', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 增加用户 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/5 02:08 |
| | | */ |
| | | export function sysUserAdd (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/add', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 编辑用户 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/5 02:08 |
| | | */ |
| | | export function sysUserEdit (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/edit', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 获取用户详情 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/5 19:55 |
| | | */ |
| | | export function sysUserDetail (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/detail', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 删除用户 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/7 19:31 |
| | | */ |
| | | export function sysUserDelete (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/delete', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 导出用户 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021/5/30 00:23 |
| | | */ |
| | | export function sysUserExport (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/export', |
| | | method: 'get', |
| | | params: parameter, |
| | | responseType: 'blob' |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 拥有角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/3 11:58 |
| | | */ |
| | | export function sysUserOwnRole (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/ownRole', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 授权角色 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/5/26 23:59 |
| | | */ |
| | | export function sysUserGrantRole (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/grantRole', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 拥有数据 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/2 23:14 |
| | | */ |
| | | export function sysUserOwnData (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/ownData', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 授权数据 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/2 23:15 |
| | | */ |
| | | export function sysUserGrantData (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/grantData', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 修改状态 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/23 21:36 |
| | | */ |
| | | export function sysUserChangeStatus (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/changeStatus', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 重置密码 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/23 22:04 |
| | | */ |
| | | export function sysUserResetPwd (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/resetPwd', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 修改密码 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/25 00:25 |
| | | */ |
| | | export function sysUserUpdatePwd (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/updatePwd', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 用户选择器 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/6/25 00:25 |
| | | */ |
| | | export function sysUserSelector (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/selector', |
| | | method: 'get', |
| | | params: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 修改头像 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/9/20 2:21 |
| | | */ |
| | | export function sysUserUpdateAvatar (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/updateAvatar', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 更新基本信息 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/9/20 03:12 |
| | | */ |
| | | export function sysUserUpdateInfo (parameter) { |
| | | return axios({ |
| | | url: '/sysUser/updateInfo', |
| | | method: 'post', |
| | | data: parameter |
| | | }) |
| | | } |
New file |
| | |
| | | <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1551058675966" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7872" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M85.333333 512h85.333334a340.736 340.736 0 0 1 99.712-241.621333 337.493333 337.493333 0 0 1 108.458666-72.96 346.453333 346.453333 0 0 1 261.546667-1.749334A106.154667 106.154667 0 0 0 746.666667 298.666667C805.802667 298.666667 853.333333 251.136 853.333333 192S805.802667 85.333333 746.666667 85.333333c-29.397333 0-55.978667 11.776-75.221334 30.933334-103.722667-41.514667-222.848-40.874667-325.76 2.517333a423.594667 423.594667 0 0 0-135.68 91.264 423.253333 423.253333 0 0 0-91.306666 135.637333A426.88 426.88 0 0 0 85.333333 512z m741.248 133.205333c-17.109333 40.618667-41.685333 77.141333-72.96 108.416s-67.797333 55.850667-108.458666 72.96a346.453333 346.453333 0 0 1-261.546667 1.749334A106.154667 106.154667 0 0 0 277.333333 725.333333C218.197333 725.333333 170.666667 772.864 170.666667 832S218.197333 938.666667 277.333333 938.666667c29.397333 0 55.978667-11.776 75.221334-30.933334A425.173333 425.173333 0 0 0 512 938.666667a425.941333 425.941333 0 0 0 393.258667-260.352A426.325333 426.325333 0 0 0 938.666667 512h-85.333334a341.034667 341.034667 0 0 1-26.752 133.205333z" p-id="7873"></path><path d="M512 318.378667c-106.752 0-193.621333 86.869333-193.621333 193.621333S405.248 705.621333 512 705.621333s193.621333-86.869333 193.621333-193.621333S618.752 318.378667 512 318.378667z m0 301.909333c-59.690667 0-108.288-48.597333-108.288-108.288S452.309333 403.712 512 403.712s108.288 48.597333 108.288 108.288-48.597333 108.288-108.288 108.288z" p-id="7874"></path></svg> |
New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8" standalone="no"?> |
| | | <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> |
| | | <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="500px" height="500px" viewBox="0 0 500 500" enable-background="new 0 0 500 500" xml:space="preserve"> <image id="image0" width="500" height="500" x="0" y="0" |
| | | href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAfQAAAH0CAMAAAD8CC+4AAAABGdBTUEAALGPC/xhBQAAACBjSFJN |
| | | AAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAADAFBMVEX////X7v+w3//L6f/U |
| | | 7v8ao/+64//i8/8lqP+v3//Z8P8HnP/S7f/v+f87sf8Amf8Onv8tq//n9f/5/f9Vu//R7f8Lnf9G |
| | | tf/z+v9txf/Q7P8Mnv9dvv/8/v+Dzf+75P/M6/8Jnf94yf+a1/+85P/I6f+T1P+04f8Bmf/F6P8E |
| | | m/8Tof8Cmv+95f/C5/8Qn//G6P/X7/8ip//A5v8Dmv8hpv/a8P/l9f8yrf83r//r9/+24v9Mt//2 |
| | | +/9ev/9kwf/9/v9yx/++5f+BzP+Iz/+d2P+i2v+r3f+54//B5v+k2/8Xov/K6v8mqP/f8v/c8f8k |
| | | p/89sf/w+f/s9/+e2P9Tuv/7/f/4/P9NuP8Fm/+Z1v9pw/9iwP+Fzv97yv8Gm//L6v+W1f+Q0/+s |
| | | 3v8InP/O6/+M0f8Yo/8SoP/P7P+Hz//h8//W7/8epf8Knf8+sv/x+f/m9f8vrP9XvP9Bs/9/zP9x |
| | | xv/6/f+K0P9vxf+p3f91yP+c1//T7f+y4P/J6f9lwf92yP9gv/9avf9Wu/8RoP/E5//Y7/+g2f9Q |
| | | uf80rv9OuP/b8f9jwf/e8v8oqf9Cs/99y/8Uof/d8f/3/P/1+/8jp//0+/8pqf86sP/t+P/y+v9r |
| | | xP8Vof/g8//o9v/u+P/H6f/j9P/k9P/p9v81rv8bpP9Jtv/q9/90x/+P0v9sxP9DtP9Zvf8sqv9Y |
| | | vP9mwv9qw//q9v84pv8Akv9Itv8Ck//g8v8qqv8Bkv+J0P8wrP+S0/+m2/9Puf95yf+95P+o3P+t |
| | | 3v/V7v85sP+n3P+i2f9hwP9kwP98y/9PuP9Es/8/sf87sP+/5v8Alf/v+P9EtP/H6P8Mnf8Im/8E |
| | | mv+x4P8Dmf8cpP8AmP+r3v+S1P9vxv9bvv9Ouf////88sv8zqf8tpf8ppP8kov/0+v9txP8gof8b |
| | | nv8Wm/8Sm/8Omf8Kl/8Glf/d8v9ZvP8Ak/9jwP8jpP8VnP/l9P88r/84q/82qv8Bk//R7P8wp/8q |
| | | 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 |
| | | 18BLBkx0Sxg90UcYdWa72WxbmycHG0xe4+nbew+DJl0rOWctONf138G60Fb0G0csaGEl2hOTs911 |
| | | 9TpNG1mgaSKDP9QShGuaC3SLaBOajSNKikJfK1PLWr1v9h20Ba3GEelLjmq7xPhH61DN4/Tf6zwi |
| | | yibq+8PEg+kaso2k/1wjmW3UBn1vmDgxOfI1vjv9x+9G/W3xdX1PmPixNmqy76L/Nqo24dwW0zKv |
| | | 2TG+2FZ0dTLb7s8LQl8aQ6JejVfc3pW1CUu49F9iM7nYSvT36L9afog/1BKdtO42ot9H/tGQQfpj |
| | | M8EYT73GK57pZHWTRXP0B2YCQn2zX6b/5Cv5X+TfCH0tjDbXpAt0Heg/kJe02cQfas2Js7KOjXPp |
| | | 38tqHvRr4W0SmyF7mz7Z99G/HtO05sFm/lBrfhxo8mR/lf7xm4WNfrt+LKeiNkuuNfpmH07/tHFe |
| | | 01Wu0d5cWdvwyX6R/uVDGfV/mNtldmjXGXvGv1NPy0707/bH6n+ofRXabcaJA5e1RJ9RrzPbprWh |
| | | nWZcub31phA9LbX2R4cfC+0wAyDtw+hn+oXarIlOnIqaJOwtjhJ9dk1e0/KeR0L7yqA4UD3Zr9A/ |
| | | OL6x6gdcMyi5qJrsimf60SFCZHPNoGRjxi4xi/6/R4aKad+GdpHB8w8v0U1ut/7+3LdC+8f4oC3d |
| | | 8nTrm5yjxjAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAM |
| | | wzAMwzAMwzAMwzAMwzAMwzAMwzAMwzAMwzDJyf8H/RgaDtLOxBkAAAAldEVYdGRhdGU6Y3JlYXRl |
| | | ADIwMjEtMDMtMDJUMTg6MDA6MDArMDg6MDA9+rsMAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDIxLTAz |
| | | LTAyVDE4OjAwOjAwKzA4OjAwTKcDsAAAACB0RVh0c29mdHdhcmUAaHR0cHM6Ly9pbWFnZW1hZ2lj |
| | | ay5vcme8zx2dAAAAGHRFWHRUaHVtYjo6RG9jdW1lbnQ6OlBhZ2VzADGn/7svAAAAGHRFWHRUaHVt |
| | | Yjo6SW1hZ2U6OkhlaWdodAA1MDB4mAPsAAAAF3RFWHRUaHVtYjo6SW1hZ2U6OldpZHRoADUwMOtp |
| | | U7EAAAAZdEVYdFRodW1iOjpNaW1ldHlwZQBpbWFnZS9wbmc/slZOAAAAF3RFWHRUaHVtYjo6TVRp |
| | | bWUAMTYxNDY3OTIwMCAf/wcAAAATdEVYdFRodW1iOjpTaXplADExMjczQkI7FGLOAAAARnRFWHRU |
| | | aHVtYjo6VVJJAGZpbGU6Ly8vYXBwL3RtcC9pbWFnZWxjL2ltZ3ZpZXcyXzlfMTYwOTkwMzUxMTcy |
| | | MzMzODZfOTJfWzBdRG9xdgAAAABJRU5ErkJggg==" ></image> |
| | | </svg> |
New file |
| | |
| | | <template> |
| | | <div class="antd-pro-components-article-list-content-index-listContent"> |
| | | <div class="description"> |
| | | <slot> |
| | | {{ description }} |
| | | </slot> |
| | | </div> |
| | | <div class="extra"> |
| | | <a-avatar :src="avatar" size="small" /> |
| | | <a :href="href">{{ owner }}</a> 发布在 <a :href="href">{{ href }}</a> |
| | | <em>{{ updateAt | moment }}</em> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'ArticleListContent', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'antd-pro-components-article-list-content-index-listContent' |
| | | }, |
| | | description: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | owner: { |
| | | type: String, |
| | | required: true |
| | | }, |
| | | avatar: { |
| | | type: String, |
| | | required: true |
| | | }, |
| | | href: { |
| | | type: String, |
| | | required: true |
| | | }, |
| | | updateAt: { |
| | | type: String, |
| | | required: true |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import '../index.less'; |
| | | |
| | | .antd-pro-components-article-list-content-index-listContent { |
| | | .description { |
| | | max-width: 720px; |
| | | line-height: 22px; |
| | | } |
| | | .extra { |
| | | margin-top: 16px; |
| | | color: @text-color-secondary; |
| | | line-height: 22px; |
| | | |
| | | & /deep/ .ant-avatar { |
| | | position: relative; |
| | | top: 1px; |
| | | width: 20px; |
| | | height: 20px; |
| | | margin-right: 8px; |
| | | vertical-align: top; |
| | | } |
| | | |
| | | & > em { |
| | | margin-left: 16px; |
| | | color: @disabled-color; |
| | | font-style: normal; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: @screen-xs) { |
| | | .antd-pro-components-article-list-content-index-listContent { |
| | | .extra { |
| | | & > em { |
| | | display: block; |
| | | margin-top: 8px; |
| | | margin-left: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import ArticleListContent from './ArticleListContent' |
| | | |
| | | export default ArticleListContent |
New file |
| | |
| | | <template> |
| | | <tooltip v-if="tips !== ''"> |
| | | <template slot="title">{{ tips }}</template> |
| | | <avatar :size="avatarSize" :src="src" /> |
| | | </tooltip> |
| | | <avatar v-else :size="avatarSize" :src="src" /> |
| | | </template> |
| | | |
| | | <script> |
| | | import Avatar from 'ant-design-vue/es/avatar' |
| | | import Tooltip from 'ant-design-vue/es/tooltip' |
| | | |
| | | export default { |
| | | name: 'AvatarItem', |
| | | components: { |
| | | Avatar, |
| | | Tooltip |
| | | }, |
| | | props: { |
| | | tips: { |
| | | type: String, |
| | | default: '', |
| | | required: false |
| | | }, |
| | | src: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | size: this.$parent.size |
| | | } |
| | | }, |
| | | computed: { |
| | | avatarSize () { |
| | | return this.size !== 'mini' && this.size || 20 |
| | | } |
| | | }, |
| | | watch: { |
| | | '$parent.size' (val) { |
| | | this.size = val |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <!-- |
| | | <template> |
| | | <div :class="[prefixCls]"> |
| | | <ul> |
| | | <slot></slot> |
| | | <template v-for="item in filterEmpty($slots.default).slice(0, 3)"></template> |
| | | |
| | | <template v-if="maxLength > 0 && filterEmpty($slots.default).length > maxLength"> |
| | | <avatar-item :size="size"> |
| | | <avatar :size="size !== 'mini' && size || 20" :style="excessItemsStyle">{{ `+${maxLength}` }}</avatar> |
| | | </avatar-item> |
| | | </template> |
| | | </ul> |
| | | </div> |
| | | </template> |
| | | --> |
| | | |
| | | <script> |
| | | import Avatar from 'ant-design-vue/es/avatar' |
| | | import AvatarItem from './Item' |
| | | import { filterEmpty } from '@/components/_util/util' |
| | | |
| | | export default { |
| | | AvatarItem, |
| | | name: 'AvatarList', |
| | | components: { |
| | | Avatar, |
| | | AvatarItem |
| | | }, |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-avatar-list' |
| | | }, |
| | | /** |
| | | * 头像大小 类型: large、small 、mini, default |
| | | * 默认值: default |
| | | */ |
| | | size: { |
| | | type: [String, Number], |
| | | default: 'default' |
| | | }, |
| | | /** |
| | | * 要显示的最大项目 |
| | | */ |
| | | maxLength: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | /** |
| | | * 多余的项目风格 |
| | | */ |
| | | excessItemsStyle: { |
| | | type: Object, |
| | | default: () => { |
| | | return { |
| | | color: '#f56a00', |
| | | backgroundColor: '#fde3cf' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data () { |
| | | return {} |
| | | }, |
| | | methods: { |
| | | getItems (items) { |
| | | const classString = { |
| | | [`${this.prefixCls}-item`]: true, |
| | | [`${this.size}`]: true |
| | | } |
| | | |
| | | if (this.maxLength > 0) { |
| | | items = items.slice(0, this.maxLength) |
| | | items.push((<Avatar size={ this.size } style={ this.excessItemsStyle }>{`+${this.maxLength}`}</Avatar>)) |
| | | } |
| | | const itemList = items.map((item) => ( |
| | | <li class={ classString }>{ item }</li> |
| | | )) |
| | | return itemList |
| | | } |
| | | }, |
| | | render () { |
| | | const { prefixCls, size } = this.$props |
| | | const classString = { |
| | | [`${prefixCls}`]: true, |
| | | [`${size}`]: true |
| | | } |
| | | const items = filterEmpty(this.$slots.default) |
| | | const itemsDom = items && items.length ? <ul class={`${prefixCls}-items`}>{ this.getItems(items) }</ul> : null |
| | | |
| | | return ( |
| | | <div class={ classString }> |
| | | { itemsDom } |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import AvatarList from './List' |
| | | import './index.less' |
| | | |
| | | export default AvatarList |
New file |
| | |
| | | @import "../index"; |
| | | |
| | | @avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list"; |
| | | @avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item"; |
| | | |
| | | .@{avatar-list-prefix-cls} { |
| | | display: inline-block; |
| | | |
| | | ul { |
| | | list-style: none; |
| | | display: inline-block; |
| | | padding: 0; |
| | | margin: 0 0 0 8px; |
| | | font-size: 0; |
| | | } |
| | | } |
| | | |
| | | .@{avatar-list-item-prefix-cls} { |
| | | display: inline-block; |
| | | font-size: @font-size-base; |
| | | margin-left: -8px; |
| | | width: @avatar-size-base; |
| | | height: @avatar-size-base; |
| | | |
| | | :global { |
| | | .ant-avatar { |
| | | border: 1px solid #fff; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | &.large { |
| | | width: @avatar-size-lg; |
| | | height: @avatar-size-lg; |
| | | } |
| | | |
| | | &.small { |
| | | width: @avatar-size-sm; |
| | | height: @avatar-size-sm; |
| | | } |
| | | |
| | | &.mini { |
| | | width: 20px; |
| | | height: 20px; |
| | | |
| | | :global { |
| | | .ant-avatar { |
| | | width: 20px; |
| | | height: 20px; |
| | | line-height: 20px; |
| | | |
| | | .ant-avatar-string { |
| | | font-size: 12px; |
| | | line-height: 18px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | # AvatarList 用户头像列表 |
| | | |
| | | |
| | | 一组用户头像,常用在项目/团队成员列表。可通过设置 `size` 属性来指定头像大小。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import AvatarList from '@/components/AvatarList' |
| | | const AvatarListItem = AvatarList.AvatarItem |
| | | |
| | | export default { |
| | | components: { |
| | | AvatarList, |
| | | AvatarListItem |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 [demo](https://pro.loacg.com/test/home) |
| | | |
| | | ```html |
| | | <avatar-list size="mini"> |
| | | <avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" /> |
| | | <avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | </avatar-list> |
| | | ``` |
| | | 或 |
| | | ```html |
| | | <avatar-list :max-length="3"> |
| | | <avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" /> |
| | | <avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | <avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" /> |
| | | </avatar-list> |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## API |
| | | |
| | | ### AvatarList |
| | | |
| | | | 参数 | 说明 | 类型 | 默认值 | |
| | | | ---------------- | -------- | ---------------------------------- | --------- | |
| | | | size | 头像大小 | `large`、`small` 、`mini`, `default` | `default` | |
| | | | maxLength | 要显示的最大项目 | number | - | |
| | | | excessItemsStyle | 多余的项目风格 | CSSProperties | - | |
| | | |
| | | ### AvatarList.Item |
| | | |
| | | | 参数 | 说明 | 类型 | 默认值 | |
| | | | ---- | ------ | --------- | --- | |
| | | | tips | 头像展示文案 | string | - | |
| | | | src | 头像图片连接 | string | - | |
| | | |
New file |
| | |
| | | <template> |
| | | <div :style="{ padding: '0 0 32px 32px' }"> |
| | | <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4> |
| | | <v-chart |
| | | height="254" |
| | | :data="data" |
| | | :forceFit="true" |
| | | :padding="['auto', 'auto', '40', '50']"> |
| | | <v-tooltip /> |
| | | <v-axis /> |
| | | <v-bar position="x*y"/> |
| | | </v-chart> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'Bar', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | data: { |
| | | type: Array, |
| | | default: () => { |
| | | return [] |
| | | } |
| | | }, |
| | | scale: { |
| | | type: Array, |
| | | default: () => { |
| | | return [{ |
| | | dataKey: 'x', |
| | | min: 2 |
| | | }, { |
| | | dataKey: 'y', |
| | | title: '时间', |
| | | min: 1, |
| | | max: 22 |
| | | }] |
| | | } |
| | | }, |
| | | tooltip: { |
| | | type: Array, |
| | | default: () => { |
| | | return [ |
| | | 'x*y', |
| | | (x, y) => ({ |
| | | name: x, |
| | | value: y |
| | | }) |
| | | ] |
| | | } |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-card :loading="loading" :body-style="{ padding: '20px 24px 8px' }" :bordered="false"> |
| | | <div class="chart-card-header"> |
| | | <div class="meta"> |
| | | <span class="chart-card-title"> |
| | | <slot name="title"> |
| | | {{ title }} |
| | | </slot> |
| | | </span> |
| | | <span class="chart-card-action"> |
| | | <slot name="action"></slot> |
| | | </span> |
| | | </div> |
| | | <div class="total"> |
| | | <slot name="total"> |
| | | <span>{{ typeof total === 'function' && total() || total }}</span> |
| | | </slot> |
| | | </div> |
| | | </div> |
| | | <div class="chart-card-content"> |
| | | <div class="content-fix"> |
| | | <slot></slot> |
| | | </div> |
| | | </div> |
| | | <div class="chart-card-footer"> |
| | | <div class="field"> |
| | | <slot name="footer"></slot> |
| | | </div> |
| | | </div> |
| | | </a-card> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'ChartCard', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | total: { |
| | | type: [Function, Number, String], |
| | | required: false, |
| | | default: null |
| | | }, |
| | | loading: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .chart-card-header { |
| | | position: relative; |
| | | overflow: hidden; |
| | | width: 100%; |
| | | |
| | | .meta { |
| | | position: relative; |
| | | overflow: hidden; |
| | | width: 100%; |
| | | color: rgba(0, 0, 0, .45); |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | } |
| | | } |
| | | |
| | | .chart-card-action { |
| | | cursor: pointer; |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | } |
| | | |
| | | .chart-card-footer { |
| | | border-top: 1px solid #e8e8e8; |
| | | padding-top: 9px; |
| | | margin-top: 8px; |
| | | |
| | | > * { |
| | | position: relative; |
| | | } |
| | | |
| | | .field { |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | margin: 0; |
| | | } |
| | | } |
| | | |
| | | .chart-card-content { |
| | | margin-bottom: 12px; |
| | | position: relative; |
| | | height: 46px; |
| | | width: 100%; |
| | | |
| | | .content-fix { |
| | | position: absolute; |
| | | left: 0; |
| | | bottom: 0; |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .total { |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | white-space: nowrap; |
| | | color: #000; |
| | | margin-top: 4px; |
| | | margin-bottom: 0; |
| | | font-size: 30px; |
| | | line-height: 38px; |
| | | height: 38px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <v-chart |
| | | :forceFit="true" |
| | | :height="height" |
| | | :width="width" |
| | | :data="data" |
| | | :scale="scale" |
| | | :padding="0"> |
| | | <v-tooltip /> |
| | | <v-interval |
| | | :shape="['liquid-fill-gauge']" |
| | | position="transfer*value" |
| | | color="" |
| | | :v-style="{ |
| | | lineWidth: 10, |
| | | opacity: 0.75 |
| | | }" |
| | | :tooltip="[ |
| | | 'transfer*value', |
| | | (transfer, value) => { |
| | | return { |
| | | name: transfer, |
| | | value, |
| | | }; |
| | | }, |
| | | ]" |
| | | ></v-interval> |
| | | <v-guide |
| | | v-for="(row, index) in data" |
| | | :key="index" |
| | | type="text" |
| | | :top="true" |
| | | :position="{ |
| | | gender: row.transfer, |
| | | value: 45 |
| | | }" |
| | | :content="row.value + '%'" |
| | | :v-style="{ |
| | | fontSize: 100, |
| | | textAlign: 'center', |
| | | opacity: 0.75, |
| | | }" |
| | | /> |
| | | </v-chart> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'Liquid', |
| | | props: { |
| | | height: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | width: { |
| | | type: Number, |
| | | default: 0 |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="antv-chart-mini"> |
| | | <div class="chart-wrapper" :style="{ height: 46 }"> |
| | | <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 0, 18, 0]"> |
| | | <v-tooltip /> |
| | | <v-smooth-area position="x*y" /> |
| | | </v-chart> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import moment from 'moment' |
| | | const data = [] |
| | | const beginDay = new Date().getTime() |
| | | |
| | | for (let i = 0; i < 10; i++) { |
| | | data.push({ |
| | | x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'), |
| | | y: Math.round(Math.random() * 10) |
| | | }) |
| | | } |
| | | |
| | | const tooltip = [ |
| | | 'x*y', |
| | | (x, y) => ({ |
| | | name: x, |
| | | value: y |
| | | }) |
| | | ] |
| | | const scale = [{ |
| | | dataKey: 'x', |
| | | min: 2 |
| | | }, { |
| | | dataKey: 'y', |
| | | title: '时间', |
| | | min: 1, |
| | | max: 22 |
| | | }] |
| | | |
| | | export default { |
| | | name: 'MiniArea', |
| | | data () { |
| | | return { |
| | | data, |
| | | tooltip, |
| | | scale, |
| | | height: 100 |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "chart"; |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="antv-chart-mini"> |
| | | <div class="chart-wrapper" :style="{ height: 46 }"> |
| | | <v-chart :force-fit="true" :height="height" :data="data" :padding="[36, 5, 18, 5]"> |
| | | <v-tooltip /> |
| | | <v-bar position="x*y" /> |
| | | </v-chart> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import moment from 'moment' |
| | | const data = [] |
| | | const beginDay = new Date().getTime() |
| | | |
| | | for (let i = 0; i < 10; i++) { |
| | | data.push({ |
| | | x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'), |
| | | y: Math.round(Math.random() * 10) |
| | | }) |
| | | } |
| | | |
| | | const tooltip = [ |
| | | 'x*y', |
| | | (x, y) => ({ |
| | | name: x, |
| | | value: y |
| | | }) |
| | | ] |
| | | |
| | | const scale = [{ |
| | | dataKey: 'x', |
| | | min: 2 |
| | | }, { |
| | | dataKey: 'y', |
| | | title: '时间', |
| | | min: 1, |
| | | max: 30 |
| | | }] |
| | | |
| | | export default { |
| | | name: 'MiniBar', |
| | | data () { |
| | | return { |
| | | data, |
| | | tooltip, |
| | | scale, |
| | | height: 100 |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "chart"; |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="chart-mini-progress"> |
| | | <div class="target" :style="{ left: target + '%'}"> |
| | | <span :style="{ backgroundColor: color }" /> |
| | | <span :style="{ backgroundColor: color }"/> |
| | | </div> |
| | | <div class="progress-wrapper"> |
| | | <div class="progress" :style="{ backgroundColor: color, width: percentage + '%', height: height }"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'MiniProgress', |
| | | props: { |
| | | target: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | height: { |
| | | type: String, |
| | | default: '10px' |
| | | }, |
| | | color: { |
| | | type: String, |
| | | default: '#13C2C2' |
| | | }, |
| | | percentage: { |
| | | type: Number, |
| | | default: 0 |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .chart-mini-progress { |
| | | padding: 5px 0; |
| | | position: relative; |
| | | width: 100%; |
| | | |
| | | .target { |
| | | position: absolute; |
| | | top: 0; |
| | | bottom: 0; |
| | | |
| | | span { |
| | | border-radius: 100px; |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | height: 4px; |
| | | width: 2px; |
| | | |
| | | &:last-child { |
| | | top: auto; |
| | | bottom: 0; |
| | | } |
| | | } |
| | | } |
| | | .progress-wrapper { |
| | | background-color: #f5f5f5; |
| | | position: relative; |
| | | |
| | | .progress { |
| | | transition: all .4s cubic-bezier(.08,.82,.17,1) 0s; |
| | | border-radius: 1px 0 0 1px; |
| | | background-color: #1890ff; |
| | | width: 0; |
| | | height: 100%; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div :class="prefixCls"> |
| | | <div class="chart-wrapper" :style="{ height: 46 }"> |
| | | <v-chart :force-fit="true" :height="100" :data="dataSource" :scale="scale" :padding="[36, 0, 18, 0]"> |
| | | <v-tooltip /> |
| | | <v-smooth-line position="x*y" :size="2" /> |
| | | <v-smooth-area position="x*y" /> |
| | | </v-chart> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'MiniSmoothArea', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-smooth-area' |
| | | }, |
| | | scale: { |
| | | type: [Object, Array], |
| | | required: true |
| | | }, |
| | | dataSource: { |
| | | type: Array, |
| | | required: true |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | height: 100 |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "smooth.area.less"; |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <v-chart :forceFit="true" height="400" :data="data" :padding="[20, 20, 95, 20]" :scale="scale"> |
| | | <v-tooltip></v-tooltip> |
| | | <v-axis :dataKey="axis1Opts.dataKey" :line="axis1Opts.line" :tickLine="axis1Opts.tickLine" :grid="axis1Opts.grid" /> |
| | | <v-axis :dataKey="axis2Opts.dataKey" :line="axis2Opts.line" :tickLine="axis2Opts.tickLine" :grid="axis2Opts.grid" /> |
| | | <v-legend dataKey="user" marker="circle" :offset="30" /> |
| | | <v-coord type="polar" radius="0.8" /> |
| | | <v-line position="item*score" color="user" :size="2" /> |
| | | <v-point position="item*score" color="user" :size="4" shape="circle" /> |
| | | </v-chart> |
| | | </template> |
| | | |
| | | <script> |
| | | const axis1Opts = { |
| | | dataKey: 'item', |
| | | line: null, |
| | | tickLine: null, |
| | | grid: { |
| | | lineStyle: { |
| | | lineDash: null |
| | | }, |
| | | hideFirstLine: false |
| | | } |
| | | } |
| | | const axis2Opts = { |
| | | dataKey: 'score', |
| | | line: null, |
| | | tickLine: null, |
| | | grid: { |
| | | type: 'polygon', |
| | | lineStyle: { |
| | | lineDash: null |
| | | } |
| | | } |
| | | } |
| | | |
| | | const scale = [ |
| | | { |
| | | dataKey: 'score', |
| | | min: 0, |
| | | max: 80 |
| | | }, { |
| | | dataKey: 'user', |
| | | alias: '类型' |
| | | } |
| | | ] |
| | | |
| | | export default { |
| | | name: 'Radar', |
| | | props: { |
| | | data: { |
| | | type: Array, |
| | | default: null |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | axis1Opts, |
| | | axis2Opts, |
| | | scale |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="rank"> |
| | | <h4 class="title">{{ title }}</h4> |
| | | <ul class="list"> |
| | | <li :key="index" v-for="(item, index) in list"> |
| | | <span :class="index < 3 ? 'active' : null">{{ index + 1 }}</span> |
| | | <span>{{ item.name }}</span> |
| | | <span>{{ item.total }}</span> |
| | | </li> |
| | | </ul> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'RankList', |
| | | // ['title', 'list'] |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | list: { |
| | | type: Array, |
| | | default: null |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .rank { |
| | | padding: 0 32px 32px 72px; |
| | | |
| | | .list { |
| | | margin: 25px 0 0; |
| | | padding: 0; |
| | | list-style: none; |
| | | |
| | | li { |
| | | margin-top: 16px; |
| | | |
| | | span { |
| | | color: rgba(0, 0, 0, .65); |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | |
| | | &:first-child { |
| | | background-color: #f5f5f5; |
| | | border-radius: 20px; |
| | | display: inline-block; |
| | | font-size: 12px; |
| | | font-weight: 600; |
| | | margin-right: 24px; |
| | | height: 20px; |
| | | line-height: 20px; |
| | | width: 20px; |
| | | text-align: center; |
| | | } |
| | | &.active { |
| | | background-color: #314659; |
| | | color: #fff; |
| | | } |
| | | &:last-child { |
| | | float: right; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .mobile .rank { |
| | | padding: 0 32px 32px 32px; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <v-chart :width="width" :height="height" :padding="[0]" :data="data" :scale="scale"> |
| | | <v-tooltip :show-title="false" /> |
| | | <v-coord type="rect" direction="TL" /> |
| | | <v-point position="x*y" color="category" shape="cloud" tooltip="value*category" /> |
| | | </v-chart> |
| | | </template> |
| | | |
| | | <script> |
| | | import { registerShape } from 'viser-vue' |
| | | const DataSet = require('@antv/data-set') |
| | | |
| | | const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png' |
| | | |
| | | const scale = [ |
| | | { dataKey: 'x', nice: false }, |
| | | { dataKey: 'y', nice: false } |
| | | ] |
| | | |
| | | registerShape('point', 'cloud', { |
| | | draw (cfg, container) { |
| | | return container.addShape('text', { |
| | | attrs: { |
| | | fillOpacity: cfg.opacity, |
| | | fontSize: cfg.origin._origin.size, |
| | | rotate: cfg.origin._origin.rotate, |
| | | text: cfg.origin._origin.text, |
| | | textAlign: 'center', |
| | | fontFamily: cfg.origin._origin.font, |
| | | fill: cfg.color, |
| | | textBaseline: 'Alphabetic', |
| | | ...cfg.style, |
| | | x: cfg.x, |
| | | y: cfg.y |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | |
| | | export default { |
| | | name: 'TagCloud', |
| | | props: { |
| | | tagList: { |
| | | type: Array, |
| | | required: true |
| | | }, |
| | | height: { |
| | | type: Number, |
| | | default: 400 |
| | | }, |
| | | width: { |
| | | type: Number, |
| | | default: 640 |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | data: [], |
| | | scale |
| | | } |
| | | }, |
| | | watch: { |
| | | tagList: function (val) { |
| | | if (val.length > 0) { |
| | | this.initTagCloud(val) |
| | | } |
| | | } |
| | | }, |
| | | mounted () { |
| | | if (this.tagList.length > 0) { |
| | | this.initTagCloud(this.tagList) |
| | | } |
| | | }, |
| | | methods: { |
| | | initTagCloud (dataSource) { |
| | | const { height, width } = this |
| | | |
| | | const dv = new DataSet.View().source(dataSource) |
| | | const range = dv.range('value') |
| | | const min = range[0] |
| | | const max = range[1] |
| | | const imageMask = new Image() |
| | | imageMask.crossOrigin = '' |
| | | imageMask.src = imgUrl |
| | | imageMask.onload = () => { |
| | | dv.transform({ |
| | | type: 'tag-cloud', |
| | | fields: ['name', 'value'], |
| | | size: [width, height], |
| | | imageMask, |
| | | font: 'Verdana', |
| | | padding: 0, |
| | | timeInterval: 5000, // max execute time |
| | | rotate () { |
| | | let random = ~~(Math.random() * 4) % 4 |
| | | if (random === 2) { |
| | | random = 0 |
| | | } |
| | | return random * 90 // 0, 90, 270 |
| | | }, |
| | | fontSize (d) { |
| | | if (d.value) { |
| | | return ((d.value - min) / (max - min)) * (32 - 8) + 8 |
| | | } |
| | | return 0 |
| | | } |
| | | }) |
| | | this.data = dv.rows |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div :style="{ padding: '0 0 32px 32px' }"> |
| | | <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4> |
| | | <v-chart |
| | | height="254" |
| | | :data="data" |
| | | :scale="scale" |
| | | :forceFit="true" |
| | | :padding="['auto', 'auto', '40', '50']"> |
| | | <v-tooltip /> |
| | | <v-axis /> |
| | | <v-bar position="x*y"/> |
| | | </v-chart> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | const tooltip = [ |
| | | 'x*y', |
| | | (x, y) => ({ |
| | | name: x, |
| | | value: y |
| | | }) |
| | | ] |
| | | const scale = [{ |
| | | dataKey: 'x', |
| | | title: '日期(天)', |
| | | alias: '日期(天)', |
| | | min: 2 |
| | | }, { |
| | | dataKey: 'y', |
| | | title: '流量(Gb)', |
| | | alias: '流量(Gb)', |
| | | min: 1 |
| | | }] |
| | | |
| | | export default { |
| | | name: 'Bar', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | data: [], |
| | | scale, |
| | | tooltip |
| | | } |
| | | }, |
| | | created () { |
| | | this.getMonthBar() |
| | | }, |
| | | methods: { |
| | | getMonthBar () { |
| | | this.$http.get('/analysis/month-bar') |
| | | .then(res => { |
| | | this.data = res.result |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div class="chart-trend"> |
| | | {{ term }} |
| | | <span>{{ rate }}%</span> |
| | | <span :class="['trend-icon', trend]"><a-icon :type="'caret-' + trend"/></span> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'Trend', |
| | | props: { |
| | | term: { |
| | | type: String, |
| | | default: '', |
| | | required: true |
| | | }, |
| | | percentage: { |
| | | type: Number, |
| | | default: null |
| | | }, |
| | | type: { |
| | | type: Boolean, |
| | | default: null |
| | | }, |
| | | target: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | value: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | fixed: { |
| | | type: Number, |
| | | default: 2 |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | trend: this.type && 'up' || 'down', |
| | | rate: this.percentage |
| | | } |
| | | }, |
| | | created () { |
| | | const type = this.type === null ? this.value >= this.target : this.type |
| | | this.trend = type ? 'up' : 'down' |
| | | this.rate = (this.percentage === null ? Math.abs(this.value - this.target) * 100 / this.target : this.percentage).toFixed(this.fixed) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .chart-trend { |
| | | display: inline-block; |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | |
| | | .trend-icon { |
| | | font-size: 12px; |
| | | |
| | | &.up, &.down { |
| | | margin-left: 4px; |
| | | position: relative; |
| | | top: 1px; |
| | | |
| | | i { |
| | | font-size: 12px; |
| | | transform: scale(.83); |
| | | } |
| | | } |
| | | |
| | | &.up { |
| | | color: #f5222d; |
| | | } |
| | | &.down { |
| | | color: #52c41a; |
| | | top: -1px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | .antv-chart-mini { |
| | | position: relative; |
| | | width: 100%; |
| | | |
| | | .chart-wrapper { |
| | | position: absolute; |
| | | bottom: -28px; |
| | | width: 100%; |
| | | |
| | | /* margin: 0 -5px; |
| | | overflow: hidden;*/ |
| | | } |
| | | } |
New file |
| | |
| | | @import "../index"; |
| | | |
| | | @smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area"; |
| | | |
| | | .@{smoothArea-prefix-cls} { |
| | | position: relative; |
| | | width: 100%; |
| | | |
| | | .chart-wrapper { |
| | | position: absolute; |
| | | bottom: -28px; |
| | | width: 100%; |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <span> |
| | | {{ lastTime | format }} |
| | | </span> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | function fixedZero (val) { |
| | | return val * 1 < 10 ? `0${val}` : val |
| | | } |
| | | |
| | | export default { |
| | | name: 'CountDown', |
| | | props: { |
| | | format: { |
| | | type: Function, |
| | | default: undefined |
| | | }, |
| | | target: { |
| | | type: [Date, Number], |
| | | required: true |
| | | }, |
| | | onEnd: { |
| | | type: Function, |
| | | default: () => ({}) |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | dateTime: '0', |
| | | originTargetTime: 0, |
| | | lastTime: 0, |
| | | timer: 0, |
| | | interval: 1000 |
| | | } |
| | | }, |
| | | filters: { |
| | | format (time) { |
| | | const hours = 60 * 60 * 1000 |
| | | const minutes = 60 * 1000 |
| | | |
| | | const h = Math.floor(time / hours) |
| | | const m = Math.floor((time - h * hours) / minutes) |
| | | const s = Math.floor((time - h * hours - m * minutes) / 1000) |
| | | return `${fixedZero(h)}:${fixedZero(m)}:${fixedZero(s)}` |
| | | } |
| | | }, |
| | | created () { |
| | | this.initTime() |
| | | this.tick() |
| | | }, |
| | | methods: { |
| | | initTime () { |
| | | let lastTime = 0 |
| | | let targetTime = 0 |
| | | this.originTargetTime = this.target |
| | | try { |
| | | if (Object.prototype.toString.call(this.target) === '[object Date]') { |
| | | targetTime = this.target |
| | | } else { |
| | | targetTime = new Date(this.target).getTime() |
| | | } |
| | | } catch (e) { |
| | | throw new Error('invalid target prop') |
| | | } |
| | | |
| | | lastTime = targetTime - new Date().getTime() |
| | | |
| | | this.lastTime = lastTime < 0 ? 0 : lastTime |
| | | }, |
| | | tick () { |
| | | const { onEnd } = this |
| | | |
| | | this.timer = setTimeout(() => { |
| | | if (this.lastTime < this.interval) { |
| | | clearTimeout(this.timer) |
| | | this.lastTime = 0 |
| | | if (typeof onEnd === 'function') { |
| | | onEnd() |
| | | } |
| | | } else { |
| | | this.lastTime -= this.interval |
| | | this.tick() |
| | | } |
| | | }, this.interval) |
| | | } |
| | | }, |
| | | beforeUpdate () { |
| | | if (this.originTargetTime !== this.target) { |
| | | this.initTime() |
| | | } |
| | | }, |
| | | beforeDestroy () { |
| | | clearTimeout(this.timer) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | import CountDown from './CountDown' |
| | | |
| | | export default CountDown |
New file |
| | |
| | | # CountDown 倒计时 |
| | | |
| | | 倒计时组件。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import CountDown from '@/components/CountDown/CountDown' |
| | | |
| | | export default { |
| | | components: { |
| | | CountDown |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 [demo](https://pro.loacg.com/test/home) |
| | | |
| | | ```html |
| | | <count-down :target="new Date().getTime() + 3000000" :on-end="onEndHandle" /> |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## API |
| | | |
| | | | 参数 | 说明 | 类型 | 默认值 | |
| | | |----------|------------------------------------------|-------------|-------| |
| | | | target | 目标时间 | Date | - | |
| | | | onEnd | 倒计时结束回调 | funtion | -| |
New file |
| | |
| | | <template> |
| | | <a-tree-select |
| | | :dropdownStyle="{ maxHeight: '300px', overflow: 'auto' }" |
| | | allowClear |
| | | :treeData="orgTree" |
| | | :placeholder="placeholder" |
| | | treeDefaultExpandAll |
| | | @change="onchange" |
| | | > |
| | | <span slot="title" slot-scope="{ id }">{{ id }}</span> |
| | | </a-tree-select> |
| | | </template> |
| | | <script> |
| | | import { getOrgTree } from '@/api/modular/system/orgManage' |
| | | |
| | | export default { |
| | | name: 'DepartSelect', |
| | | props: { |
| | | placeholder: { |
| | | type: String |
| | | }, |
| | | value: { |
| | | type: String |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | orgTree: [] |
| | | } |
| | | }, |
| | | created() { |
| | | this.getOrgData() |
| | | }, |
| | | methods: { |
| | | getOrgData() { |
| | | getOrgTree().then((res) => { |
| | | this.orgTree = res |
| | | }) |
| | | }, |
| | | /** |
| | | * 选择树机构,初始化机构名称于表单中 |
| | | */ |
| | | onchange (value) { |
| | | this.$emit('change', value) |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import DepartmentSelect from './DepartmentSelect' |
| | | |
| | | export default DepartmentSelect |
New file |
| | |
| | | <template> |
| | | <div :class="['description-list', size, layout === 'vertical' ? 'vertical': 'horizontal']"> |
| | | <div v-if="title" class="title">{{ title }}</div> |
| | | <a-row> |
| | | <slot></slot> |
| | | </a-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { Col } from 'ant-design-vue/es/grid/' |
| | | |
| | | const Item = { |
| | | name: 'DetailListItem', |
| | | props: { |
| | | term: { |
| | | type: String, |
| | | default: '', |
| | | required: false |
| | | } |
| | | }, |
| | | inject: { |
| | | col: { |
| | | type: Number |
| | | } |
| | | }, |
| | | render () { |
| | | return ( |
| | | <Col {...{ props: responsive[this.col] }}> |
| | | <div class="term">{this.$props.term}</div> |
| | | <div class="content">{this.$slots.default}</div> |
| | | </Col> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | const responsive = { |
| | | 1: { xs: 24 }, |
| | | 2: { xs: 24, sm: 12 }, |
| | | 3: { xs: 24, sm: 12, md: 8 }, |
| | | 4: { xs: 24, sm: 12, md: 6 } |
| | | } |
| | | |
| | | export default { |
| | | name: 'DetailList', |
| | | Item: Item, |
| | | components: { |
| | | Col |
| | | }, |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '', |
| | | required: false |
| | | }, |
| | | col: { |
| | | type: Number, |
| | | required: false, |
| | | default: 3 |
| | | }, |
| | | size: { |
| | | type: String, |
| | | required: false, |
| | | default: 'large' |
| | | }, |
| | | layout: { |
| | | type: String, |
| | | required: false, |
| | | default: 'horizontal' |
| | | } |
| | | }, |
| | | provide () { |
| | | return { |
| | | col: this.col > 4 ? 4 : this.col |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .description-list { |
| | | |
| | | .title { |
| | | color: rgba(0,0,0,.85); |
| | | font-size: 14px; |
| | | font-weight: 500; |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | /deep/ .term { |
| | | color: rgba(0,0,0,.85); |
| | | display: table-cell; |
| | | line-height: 20px; |
| | | margin-right: 8px; |
| | | padding-bottom: 16px; |
| | | white-space: nowrap; |
| | | |
| | | &:not(:empty):after { |
| | | content: ":"; |
| | | margin: 0 8px 0 2px; |
| | | position: relative; |
| | | top: -.5px; |
| | | } |
| | | } |
| | | |
| | | /deep/ .content { |
| | | color: rgba(0,0,0,.65); |
| | | display: table-cell; |
| | | min-height: 22px; |
| | | line-height: 22px; |
| | | padding-bottom: 16px; |
| | | width: 100%; |
| | | &:empty { |
| | | content: ' '; |
| | | height: 38px; |
| | | padding-bottom: 16px; |
| | | } |
| | | } |
| | | |
| | | &.small { |
| | | |
| | | .title { |
| | | font-size: 14px; |
| | | color: rgba(0, 0, 0, .65); |
| | | font-weight: normal; |
| | | margin-bottom: 12px; |
| | | } |
| | | /deep/ .term, .content { |
| | | padding-bottom: 8px; |
| | | } |
| | | } |
| | | |
| | | &.large { |
| | | /deep/ .term, .content { |
| | | padding-bottom: 16px; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 16px; |
| | | } |
| | | } |
| | | |
| | | &.vertical { |
| | | .term { |
| | | padding-bottom: 8px; |
| | | } |
| | | /deep/ .term, .content { |
| | | display: block; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import DescriptionList from './DescriptionList' |
| | | export default DescriptionList |
New file |
| | |
| | | import Modal from 'ant-design-vue/es/modal' |
| | | export default (Vue) => { |
| | | function dialog (component, componentProps, modalProps) { |
| | | const _vm = this |
| | | modalProps = modalProps || {} |
| | | if (!_vm || !_vm._isVue) { |
| | | return |
| | | } |
| | | let dialogDiv = document.querySelector('body>div[type=dialog]') |
| | | if (!dialogDiv) { |
| | | dialogDiv = document.createElement('div') |
| | | dialogDiv.setAttribute('type', 'dialog') |
| | | document.body.appendChild(dialogDiv) |
| | | } |
| | | |
| | | const handle = function (checkFunction, afterHandel) { |
| | | if (checkFunction instanceof Function) { |
| | | const res = checkFunction() |
| | | if (res instanceof Promise) { |
| | | res.then(c => { |
| | | c && afterHandel() |
| | | }) |
| | | } else { |
| | | res && afterHandel() |
| | | } |
| | | } else { |
| | | // checkFunction && afterHandel() |
| | | checkFunction || afterHandel() |
| | | } |
| | | } |
| | | |
| | | const dialogInstance = new Vue({ |
| | | data () { |
| | | return { |
| | | visible: true |
| | | } |
| | | }, |
| | | router: _vm.$router, |
| | | store: _vm.$store, |
| | | mounted () { |
| | | this.$on('close', (v) => { |
| | | this.handleClose() |
| | | }) |
| | | }, |
| | | methods: { |
| | | handleClose () { |
| | | handle(this.$refs._component.onCancel, () => { |
| | | this.visible = false |
| | | this.$refs._component.$emit('close') |
| | | this.$refs._component.$emit('cancel') |
| | | dialogInstance.$destroy() |
| | | }) |
| | | }, |
| | | handleOk () { |
| | | handle(this.$refs._component.onOK || this.$refs._component.onOk, () => { |
| | | this.visible = false |
| | | this.$refs._component.$emit('close') |
| | | this.$refs._component.$emit('ok') |
| | | dialogInstance.$destroy() |
| | | }) |
| | | } |
| | | }, |
| | | render: function (h) { |
| | | const that = this |
| | | const modalModel = modalProps && modalProps.model |
| | | if (modalModel) { |
| | | delete modalProps.model |
| | | } |
| | | const ModalProps = Object.assign({}, modalModel && { model: modalModel } || {}, { |
| | | attrs: Object.assign({}, { |
| | | ...(modalProps.attrs || modalProps) |
| | | }, { |
| | | visible: this.visible |
| | | }), |
| | | on: Object.assign({}, { |
| | | ...(modalProps.on || modalProps) |
| | | }, { |
| | | ok: () => { |
| | | that.handleOk() |
| | | }, |
| | | cancel: () => { |
| | | that.handleClose() |
| | | } |
| | | }) |
| | | }) |
| | | |
| | | const componentModel = componentProps && componentProps.model |
| | | if (componentModel) { |
| | | delete componentProps.model |
| | | } |
| | | const ComponentProps = Object.assign({}, componentModel && { model: componentModel } || {}, { |
| | | ref: '_component', |
| | | attrs: Object.assign({}, { |
| | | ...((componentProps && componentProps.attrs) || componentProps) |
| | | }), |
| | | on: Object.assign({}, { |
| | | ...((componentProps && componentProps.on) || componentProps) |
| | | }) |
| | | }) |
| | | |
| | | return h(Modal, ModalProps, [h(component, ComponentProps)]) |
| | | } |
| | | }).$mount(dialogDiv) |
| | | } |
| | | |
| | | Object.defineProperty(Vue.prototype, '$dialog', { |
| | | get: () => { |
| | | return function () { |
| | | dialog.apply(this, arguments) |
| | | } |
| | | } |
| | | }) |
| | | } |
New file |
| | |
| | | <template> |
| | | <div :class="prefixCls"> |
| | | <quill-editor |
| | | v-model="content" |
| | | ref="myQuillEditor" |
| | | :options="editorOption" |
| | | @blur="onEditorBlur($event)" |
| | | @focus="onEditorFocus($event)" |
| | | @ready="onEditorReady($event)" |
| | | @change="onEditorChange($event)"> |
| | | </quill-editor> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import 'quill/dist/quill.core.css' |
| | | import 'quill/dist/quill.snow.css' |
| | | import 'quill/dist/quill.bubble.css' |
| | | |
| | | import { quillEditor } from 'vue-quill-editor' |
| | | |
| | | export default { |
| | | name: 'QuillEditor', |
| | | components: { |
| | | quillEditor |
| | | }, |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-editor-quill' |
| | | }, |
| | | // 表单校验用字段 |
| | | // eslint-disable-next-line |
| | | value: { |
| | | type: String |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | content: null, |
| | | editorOption: { |
| | | // some quill options |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | onEditorBlur (quill) { |
| | | console.log('editor blur!', quill) |
| | | }, |
| | | onEditorFocus (quill) { |
| | | console.log('editor focus!', quill) |
| | | }, |
| | | onEditorReady (quill) { |
| | | console.log('editor ready!', quill) |
| | | }, |
| | | onEditorChange ({ quill, html, text }) { |
| | | console.log('editor change!', quill, html, text) |
| | | this.$emit('change', html) |
| | | } |
| | | }, |
| | | watch: { |
| | | value (val) { |
| | | this.content = val |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import url('../index.less'); |
| | | |
| | | /* 覆盖 quill 默认边框圆角为 ant 默认圆角,用于统一 ant 组件风格 */ |
| | | .ant-editor-quill { |
| | | /deep/ .ql-toolbar.ql-snow { |
| | | border-radius: @border-radius-base @border-radius-base 0 0; |
| | | } |
| | | /deep/ .ql-container.ql-snow { |
| | | border-radius: 0 0 @border-radius-base @border-radius-base; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <div id="editor" ref="myEditor"></div> |
| | | <slot></slot> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import WangEditor from 'wangeditor' |
| | | export default { |
| | | name: 'ComponentWangeditor', |
| | | data () { |
| | | return { |
| | | edit: '' |
| | | } |
| | | }, |
| | | props: { |
| | | value: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | config: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | }, |
| | | uploadConfig: { |
| | | type: Object, |
| | | default: () => { |
| | | return { |
| | | method: 'http', // 支持custom(objurl)和http(服务器)和base64 |
| | | url: '/' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | customConfig () { |
| | | return { |
| | | pasteFilterStyle: false, // 关闭掉粘贴样式的过滤 |
| | | pasteIgnoreImg: false, // 粘贴时不忽略图片 |
| | | ...this.config |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | |
| | | }, |
| | | components: { |
| | | |
| | | }, |
| | | methods: { |
| | | readBlobAsDataURL (blob, callback) { |
| | | var a = new FileReader() |
| | | a.onload = function (e) { callback(e.target.result) } |
| | | a.readAsDataURL(blob) |
| | | }, |
| | | initEditor () { |
| | | var self = this |
| | | this.editor = new WangEditor(this.$refs.myEditor) |
| | | // 配置 onchange 事件 |
| | | this.editor.customConfig = this.customConfig |
| | | this.editor.customConfig.uploadImgMaxLength = 5 |
| | | this.editor.change = function () { // 编辑区域内容变化时 |
| | | self.$emit('input', this.txt.html()) |
| | | self.$emit('onchange', this.txt.html(), this.txt) |
| | | // editor.txt.html('.....') //设置编辑器内容 |
| | | // editor.txt.clear() //清空编辑器内容 |
| | | // editor.txt.append('<p>追加的内容</p>')//继续追加内容。 |
| | | // editor.txt.text() // 读取 text |
| | | // editor.txt.getJSON() // 获取 JSON 格式的内容 |
| | | } |
| | | this.editor.customConfig.customUploadImg = function (files, insert) { |
| | | if (self.uploadConfig.method === 'custom') { |
| | | files.forEach(file => { |
| | | var fileUrl = URL.createObjectURL(file) |
| | | insert(fileUrl) |
| | | }) |
| | | } |
| | | if (self.uploadConfig.method === 'base64') { |
| | | files.forEach(file => { |
| | | self.readBlobAsDataURL(file, function (dataurl) { |
| | | insert(dataurl) |
| | | }) |
| | | }) |
| | | } |
| | | if (self.uploadConfig.method === 'http') { |
| | | if (self.uploadConfig.callback) { |
| | | self.uploadConfig.callback(files, insert) |
| | | } else { |
| | | var formData = new FormData() |
| | | files.forEach(file => { |
| | | formData.append('file', file) |
| | | }) |
| | | self.$axios.post(self.uploadConfig.url, formData).then(({ data }) => { |
| | | if (data.status === 'success') { |
| | | insert(data.url) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | this.editor.create() // 生成编辑器 |
| | | this.editor.txt.text(this.value) // 生成编辑器 |
| | | this.$emit('oninit', this.editor) |
| | | } |
| | | }, |
| | | beforeCreate () { |
| | | }, |
| | | created () { |
| | | }, |
| | | beforeMount () { |
| | | }, |
| | | mounted () { |
| | | this.initEditor() |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style > |
| | | .w-e-toolbar{ |
| | | flex-wrap:wrap; |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <script> |
| | | import Tooltip from 'ant-design-vue/es/tooltip' |
| | | import { cutStrByFullLength, getStrFullLength } from '@/components/_util/util' |
| | | /* |
| | | const isSupportLineClamp = document.body.style.webkitLineClamp !== undefined; |
| | | |
| | | const TooltipOverlayStyle = { |
| | | overflowWrap: 'break-word', |
| | | wordWrap: 'break-word', |
| | | }; |
| | | */ |
| | | |
| | | export default { |
| | | name: 'Ellipsis', |
| | | components: { |
| | | Tooltip |
| | | }, |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-ellipsis' |
| | | }, |
| | | tooltip: { |
| | | type: Boolean |
| | | }, |
| | | length: { |
| | | type: Number, |
| | | required: true |
| | | }, |
| | | lines: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | fullWidthRecognition: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | methods: { |
| | | getStrDom (str, fullLength) { |
| | | return ( |
| | | <span>{ cutStrByFullLength(str, this.length) + (fullLength > this.length ? '...' : '') }</span> |
| | | ) |
| | | }, |
| | | getTooltip (fullStr, fullLength) { |
| | | return ( |
| | | <Tooltip> |
| | | <template slot="title">{ fullStr }</template> |
| | | { this.getStrDom(fullStr, fullLength) } |
| | | </Tooltip> |
| | | ) |
| | | } |
| | | }, |
| | | render () { |
| | | const { tooltip, length } = this.$props |
| | | const str = this.$slots.default.map(vNode => vNode.text).join('') |
| | | const fullLength = getStrFullLength(str) |
| | | const strDom = tooltip && fullLength > length ? this.getTooltip(str, fullLength) : this.getStrDom(str, fullLength) |
| | | return ( |
| | | strDom |
| | | ) |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import Ellipsis from './Ellipsis' |
| | | |
| | | export default Ellipsis |
New file |
| | |
| | | # Ellipsis 文本自动省略号 |
| | | |
| | | 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import Ellipsis from '@/components/Ellipsis' |
| | | |
| | | export default { |
| | | components: { |
| | | Ellipsis |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 [demo](https://pro.loacg.com/test/home) |
| | | |
| | | ```html |
| | | <ellipsis :length="100" tooltip> |
| | | There were injuries alleged in three cases in 2015, and a |
| | | fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall. |
| | | </ellipsis> |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## API |
| | | |
| | | |
| | | 参数 | 说明 | 类型 | 默认值 |
| | | ----|------|-----|------ |
| | | tooltip | 移动到文本展示完整内容的提示 | boolean | - |
| | | length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - |
New file |
| | |
| | | <template> |
| | | <div class="exception"> |
| | | <div class="imgBlock"> |
| | | <div class="imgEle" :style="{backgroundImage: `url(${config[type].img})`}"> |
| | | </div> |
| | | </div> |
| | | <div class="content"> |
| | | <h1>{{ config[type].title }}</h1> |
| | | <div class="desc">{{ config[type].desc }}</div> |
| | | <div class="actions"> |
| | | <a-button type="primary" @click="handleToHome">返回首页</a-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import types from './type' |
| | | |
| | | export default { |
| | | name: 'Exception', |
| | | props: { |
| | | type: { |
| | | type: String, |
| | | default: '404' |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | config: types |
| | | } |
| | | }, |
| | | methods: { |
| | | handleToHome () { |
| | | this.$router.push({ name: 'Console' }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | @import "~ant-design-vue/lib/style/index"; |
| | | |
| | | .exception { |
| | | display: flex; |
| | | align-items: center; |
| | | height: 80%; |
| | | min-height: 500px; |
| | | |
| | | .imgBlock { |
| | | flex: 0 0 62.5%; |
| | | width: 62.5%; |
| | | padding-right: 152px; |
| | | zoom: 1; |
| | | &::before, |
| | | &::after { |
| | | content: ' '; |
| | | display: table; |
| | | } |
| | | &::after { |
| | | clear: both; |
| | | height: 0; |
| | | font-size: 0; |
| | | visibility: hidden; |
| | | } |
| | | } |
| | | |
| | | .imgEle { |
| | | float: right; |
| | | width: 100%; |
| | | max-width: 430px; |
| | | height: 360px; |
| | | background-repeat: no-repeat; |
| | | background-position: 50% 50%; |
| | | background-size: contain; |
| | | } |
| | | |
| | | .content { |
| | | flex: auto; |
| | | |
| | | h1 { |
| | | margin-bottom: 24px; |
| | | color: #434e59; |
| | | font-weight: 600; |
| | | font-size: 72px; |
| | | line-height: 72px; |
| | | } |
| | | |
| | | .desc { |
| | | margin-bottom: 16px; |
| | | color: @text-color-secondary; |
| | | font-size: 20px; |
| | | line-height: 28px; |
| | | } |
| | | |
| | | .actions { |
| | | button:not(:last-child) { |
| | | margin-right: 8px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: @screen-xl) { |
| | | .exception { |
| | | .imgBlock { |
| | | padding-right: 88px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: @screen-sm) { |
| | | .exception { |
| | | display: block; |
| | | text-align: center; |
| | | .imgBlock { |
| | | margin: 0 auto 24px; |
| | | padding-right: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: @screen-xs) { |
| | | .exception { |
| | | .imgBlock { |
| | | margin-bottom: -24px; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import ExceptionPage from './ExceptionPage.vue' |
| | | export default ExceptionPage |
New file |
| | |
| | | const types = { |
| | | 403: { |
| | | img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg', |
| | | title: '403', |
| | | desc: '抱歉,你无权访问该页面' |
| | | }, |
| | | 404: { |
| | | img: 'https://gw.alipayobjects.com/zos/rmsportal/KpnpchXsobRgLElEozzI.svg', |
| | | title: '404', |
| | | desc: '抱歉,你访问的页面不存在或仍在开发中' |
| | | }, |
| | | 500: { |
| | | img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg', |
| | | title: '500', |
| | | desc: '抱歉,服务器出错了' |
| | | } |
| | | } |
| | | |
| | | export default types |
New file |
| | |
| | | <template> |
| | | <div :class="prefixCls"> |
| | | <div style="float: left"> |
| | | <slot name="extra">{{ extra }}</slot> |
| | | </div> |
| | | <div style="float: right"> |
| | | <slot></slot> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'FooterToolBar', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-footer-toolbar' |
| | | }, |
| | | extra: { |
| | | type: [String, Object], |
| | | default: '' |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | import FooterToolBar from './FooterToolBar' |
| | | import './index.less' |
| | | |
| | | export default FooterToolBar |
New file |
| | |
| | | @import "../index"; |
| | | |
| | | @footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar"; |
| | | |
| | | .@{footer-toolbar-prefix-cls} { |
| | | position: fixed; |
| | | width: 100%; |
| | | bottom: 0; |
| | | right: 0; |
| | | height: 56px; |
| | | line-height: 56px; |
| | | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03); |
| | | background: #fff; |
| | | border-top: 1px solid #e8e8e8; |
| | | padding: 0 24px; |
| | | z-index: 9; |
| | | |
| | | &:after { |
| | | content: ""; |
| | | display: block; |
| | | clear: both; |
| | | } |
| | | } |
New file |
| | |
| | | # FooterToolbar 底部工具栏 |
| | | |
| | | 固定在底部的工具栏。 |
| | | |
| | | |
| | | |
| | | ## 何时使用 |
| | | |
| | | 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import FooterToolBar from '@/components/FooterToolbar' |
| | | |
| | | export default { |
| | | components: { |
| | | FooterToolBar |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 |
| | | |
| | | ```html |
| | | <footer-tool-bar> |
| | | <a-button type="primary" @click="validate" :loading="loading">提交</a-button> |
| | | </footer-tool-bar> |
| | | ``` |
| | | 或 |
| | | ```html |
| | | <footer-tool-bar extra="扩展信息提示"> |
| | | <a-button type="primary" @click="validate" :loading="loading">提交</a-button> |
| | | </footer-tool-bar> |
| | | ``` |
| | | |
| | | |
| | | ## API |
| | | |
| | | 参数 | 说明 | 类型 | 默认值 |
| | | ----|------|-----|------ |
| | | children (slot) | 工具栏内容,向右对齐 | - | - |
| | | extra | 额外信息,向左对齐 | String, Object | - |
| | | |
New file |
| | |
| | | <template> |
| | | <div class="footer"> |
| | | <div class="links"> |
| | | </div> |
| | | <div class="copyright"> |
| | | Copyright © 2020 <a target="_blank" href="https://www.xiaonuo.vip/">小诺开源技术</a> All rights reserved. Snowy 1.8 |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'GlobalFooter', |
| | | data () { |
| | | return {} |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .footer { |
| | | padding: 0 16px; |
| | | margin: 48px 0 24px; |
| | | text-align: center; |
| | | |
| | | .links { |
| | | margin-bottom: 8px; |
| | | |
| | | a { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | |
| | | &:hover { |
| | | color: rgba(0, 0, 0, 0.65); |
| | | } |
| | | |
| | | &:not(:last-child) { |
| | | margin-right: 40px; |
| | | } |
| | | } |
| | | } |
| | | .copyright { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import GlobalFooter from './GlobalFooter' |
| | | export default GlobalFooter |
New file |
| | |
| | | <template> |
| | | <transition name="showHeader"> |
| | | <div v-if="visible" class="header-animat"> |
| | | <a-layout-header |
| | | v-if="visible" |
| | | :class="[fixedHeader && 'ant-header-fixedHeader', sidebarOpened ? 'ant-header-side-opened' : 'ant-header-side-closed', ]" |
| | | :style="{ padding: '0', height: '55px' }"> |
| | | <div v-if="mode === 'sidemenu'" class="header"> |
| | | |
| | | <a-menu |
| | | style="height: 55px; border-bottom: 0px;" |
| | | mode="horizontal" |
| | | :default-selected-keys="this.defApp" |
| | | > |
| | | <a-icon v-if="device==='mobile'" class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle"/> |
| | | <a-icon v-else class="trigger" :type="collapsed ? 'menu-unfold' : 'menu-fold'" @click="toggle" style="padding-left: 20px; padding-right: 20px;"/> |
| | | |
| | | <a-menu-item v-for="(item) in userInfo.apps" :key="item.code" style="top:0px; line-height: 55px; padding-left: 10px; padding-right: 10px" @click="switchApp(item.code)"> |
| | | {{ item.name }} |
| | | </a-menu-item> |
| | | <user-menu></user-menu> |
| | | </a-menu> |
| | | |
| | | </div> |
| | | <div v-else :class="['top-nav-header-index', theme]"> |
| | | |
| | | <div class="header-index-wide"> |
| | | <div class="header-index-left"> |
| | | <logo class="top-nav-header" :show-title="device !== 'mobile'"/> |
| | | <s-menu v-if="device !== 'mobile'" mode="horizontal" :menu="menus" :theme="theme" /> |
| | | <a-icon v-else class="trigger" :type="collapsed ? 'menu-fold' : 'menu-unfold'" @click="toggle" /> |
| | | </div> |
| | | <user-menu class="header-index-right"></user-menu> |
| | | </div> |
| | | </div> |
| | | </a-layout-header> |
| | | </div> |
| | | </transition> |
| | | </template> |
| | | |
| | | <script> |
| | | import UserMenu from '../tools/UserMenu' |
| | | import SMenu from '../Menu/' |
| | | import Logo from '../tools/Logo' |
| | | import { mixin } from '@/utils/mixin' |
| | | import { mapActions, mapGetters } from 'vuex' |
| | | import { ALL_APPS_MENU } from '@/store/mutation-types' |
| | | import Vue from 'vue' |
| | | import { message } from 'ant-design-vue/es' |
| | | |
| | | export default { |
| | | name: 'GlobalHeader', |
| | | components: { |
| | | UserMenu, |
| | | SMenu, |
| | | Logo |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['userInfo']) |
| | | }, |
| | | created () { |
| | | this.defApp.push(Vue.ls.get(ALL_APPS_MENU)[0].code) |
| | | }, |
| | | mixins: [mixin], |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | // sidemenu, topmenu |
| | | default: 'sidemenu' |
| | | }, |
| | | menus: { |
| | | type: Array, |
| | | required: true |
| | | }, |
| | | theme: { |
| | | type: String, |
| | | required: false, |
| | | default: 'dark' |
| | | }, |
| | | collapsed: { |
| | | type: Boolean, |
| | | required: false, |
| | | default: false |
| | | }, |
| | | device: { |
| | | type: String, |
| | | required: false, |
| | | default: 'desktop' |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | visible: true, |
| | | oldScrollTop: 0, |
| | | defApp: [] |
| | | } |
| | | }, |
| | | mounted () { |
| | | document.addEventListener('scroll', this.handleScroll, { passive: true }) |
| | | }, |
| | | methods: { |
| | | ...mapActions(['MenuChange']), |
| | | |
| | | /** |
| | | * 应用切换 |
| | | */ |
| | | switchApp (appCode) { |
| | | this.defApp = [] |
| | | const applicationData = this.userInfo.apps.filter(item => item.code === appCode) |
| | | const hideMessage = message.loading('正在切换应用!', 0) |
| | | this.MenuChange(applicationData[0]).then((res) => { |
| | | hideMessage() |
| | | // eslint-disable-next-line handle-callback-err |
| | | }).catch((err) => { |
| | | message.error('应用切换异常') |
| | | }) |
| | | }, |
| | | handleScroll () { |
| | | if (!this.autoHideHeader) { |
| | | return |
| | | } |
| | | |
| | | const scrollTop = document.body.scrollTop + document.documentElement.scrollTop |
| | | if (!this.ticking) { |
| | | this.ticking = true |
| | | requestAnimationFrame(() => { |
| | | if (this.oldScrollTop > scrollTop) { |
| | | this.visible = true |
| | | } else if (scrollTop > 300 && this.visible) { |
| | | this.visible = false |
| | | } else if (scrollTop < 300 && !this.visible) { |
| | | this.visible = true |
| | | } |
| | | this.oldScrollTop = scrollTop |
| | | this.ticking = false |
| | | }) |
| | | } |
| | | }, |
| | | toggle () { |
| | | this.$emit('toggle') |
| | | } |
| | | }, |
| | | beforeDestroy () { |
| | | document.body.removeEventListener('scroll', this.handleScroll, true) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less"> |
| | | @import '../index.less'; |
| | | |
| | | .header-animat{ |
| | | position: relative; |
| | | z-index: @ant-global-header-zindex; |
| | | } |
| | | .showHeader-enter-active { |
| | | transition: all 0.25s ease; |
| | | } |
| | | .showHeader-leave-active { |
| | | transition: all 0.5s ease; |
| | | } |
| | | .showHeader-enter, .showHeader-leave-to { |
| | | opacity: 0; |
| | | } |
| | | </style> |
New file |
| | |
| | | import GlobalHeader from './GlobalHeader' |
| | | export default GlobalHeader |
New file |
| | |
| | | <template> |
| | | <div :class="prefixCls"> |
| | | <a-tabs v-model="currentTab" @change="handleTabChange"> |
| | | <a-tab-pane v-for="v in icons" :tab="v.title" :key="v.key"> |
| | | <ul> |
| | | <li v-for="(icon, key) in v.icons" :key="`${v.key}-${key}`" :class="{ 'active': selectedIcon==icon }" @click="handleSelectedIcon(icon)" > |
| | | <a-icon :type="icon" :style="{ fontSize: '36px' }" /> |
| | | </li> |
| | | </ul> |
| | | </a-tab-pane> |
| | | </a-tabs> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import icons from './icons' |
| | | |
| | | export default { |
| | | name: 'IconSelect', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-icon-selector' |
| | | }, |
| | | // eslint-disable-next-line |
| | | value: { |
| | | type: String |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | selectedIcon: this.value || '', |
| | | currentTab: 'directional', |
| | | icons |
| | | } |
| | | }, |
| | | watch: { |
| | | value (val) { |
| | | this.selectedIcon = val |
| | | this.autoSwitchTab() |
| | | } |
| | | }, |
| | | created () { |
| | | if (this.value) { |
| | | this.autoSwitchTab() |
| | | } |
| | | }, |
| | | methods: { |
| | | handleSelectedIcon (icon) { |
| | | this.selectedIcon = icon |
| | | this.$emit('change', icon) |
| | | }, |
| | | handleTabChange (activeKey) { |
| | | this.currentTab = activeKey |
| | | }, |
| | | autoSwitchTab () { |
| | | icons.some(item => item.icons.some(icon => icon === this.value) && (this.currentTab = item.key)) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "../index.less"; |
| | | |
| | | ul{ |
| | | list-style: none; |
| | | padding: 0; |
| | | overflow-y: scroll; |
| | | height: 250px; |
| | | |
| | | li{ |
| | | display: inline-block; |
| | | padding: @padding-sm; |
| | | margin: 3px 0; |
| | | border-radius: @border-radius-base; |
| | | |
| | | &:hover, &.active{ |
| | | // box-shadow: 0px 0px 5px 2px @primary-color; |
| | | cursor: pointer; |
| | | color: @white; |
| | | background-color: @primary-color; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | IconSelector |
| | | ==== |
| | | |
| | | > 图标选择组件,常用于为某一个数据设定一个图标时使用 |
| | | > eg: 设定菜单列表时,为每个菜单设定一个图标 |
| | | |
| | | 该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装 |
| | | |
| | | |
| | | |
| | | ### 使用方式 |
| | | |
| | | ```vue |
| | | <template> |
| | | <div> |
| | | <icon-selector @change="handleIconChange"/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import IconSelector from '@/components/IconSelector' |
| | | |
| | | export default { |
| | | name: 'YourView', |
| | | components: { |
| | | IconSelector |
| | | }, |
| | | data () { |
| | | return { |
| | | } |
| | | }, |
| | | methods: { |
| | | handleIconChange (icon) { |
| | | console.log('change Icon', icon) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | |
| | | |
| | | ### 事件 |
| | | |
| | | |
| | | | 名称 | 说明 | 类型 | 默认值 | |
| | | | ------ | -------------------------- | ------ | ------ | |
| | | | change | 当改变了 `icon` 选中项触发 | String | - | |
New file |
| | |
| | | /** |
| | | * 增加新的图标时,请遵循以下数据结构 |
| | | * Adding new icon please follow the data structure below |
| | | */ |
| | | export default [ |
| | | { |
| | | key: 'directional', |
| | | title: '方向性图标', |
| | | icons: ['step-backward', 'step-forward', 'fast-backward', 'fast-forward', 'shrink', 'arrows-alt', 'down', 'up', 'left', 'right', 'caret-up', 'caret-down', 'caret-left', 'caret-right', 'up-circle', 'down-circle', 'left-circle', 'right-circle', 'double-right', 'double-left', 'vertical-left', 'vertical-right', 'forward', 'backward', 'rollback', 'enter', 'retweet', 'swap', 'swap-left', 'swap-right', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right', 'play-circle', 'up-square', 'down-square', 'left-square', 'right-square', 'login', 'logout', 'menu-fold', 'menu-unfold', 'border-bottom', 'border-horizontal', 'border-inner', 'border-left', 'border-right', 'border-top', 'border-verticle', 'pic-center', 'pic-left', 'pic-right', 'radius-bottomleft', 'radius-bottomright', 'radius-upleft', 'fullscreen', 'fullscreen-exit'] |
| | | }, |
| | | { |
| | | key: 'suggested', |
| | | title: '提示建议性图标', |
| | | icons: ['question', 'question-circle', 'plus', 'plus-circle', 'pause', 'pause-circle', 'minus', 'minus-circle', 'plus-square', 'minus-square', 'info', 'info-circle', 'exclamation', 'exclamation-circle', 'close', 'close-circle', 'close-square', 'check', 'check-circle', 'check-square', 'clock-circle', 'warning', 'issues-close', 'stop'] |
| | | }, |
| | | { |
| | | key: 'editor', |
| | | title: '编辑类图标', |
| | | icons: ['edit', 'form.vue', 'copy', 'scissor', 'delete', 'snippets', 'diff', 'highlight', 'align-center', 'align-left', 'align-right', 'bg-colors', 'bold', 'italic', 'underline', 'strikethrough', 'redo', 'undo', 'zoom-in', 'zoom-out', 'font-colors', 'font-size', 'line-height', 'column-height', 'dash', 'small-dash', 'sort-ascending', 'sort-descending', 'drag', 'ordered-list', 'radius-setting'] |
| | | }, |
| | | { |
| | | key: 'data', |
| | | title: '数据类图标', |
| | | icons: ['area-chart', 'pie-chart', 'bar-chart', 'dot-chart', 'line-chart', 'radar-chart', 'heat-map', 'fall', 'rise', 'stock', 'box-plot', 'fund', 'sliders'] |
| | | }, |
| | | { |
| | | key: 'brand_logo', |
| | | title: '网站通用图标', |
| | | icons: ['lock', 'unlock', 'bars', 'book', 'calendar', 'cloud', 'cloud-download', 'code', 'copy', 'credit-card', 'delete', 'desktop', 'download', 'ellipsis', 'file', 'file-text', 'file-unknown', 'file-pdf', 'file-word', 'file-excel', 'file-jpg', 'file-ppt', 'file-markdown', 'file-add', 'folder', 'folder-open', 'folder-add', 'hdd', 'frown', 'meh', 'smile', 'inbox', 'laptop', 'appstore', 'link', 'mail', 'mobile', 'notification', 'paper-clip', 'picture', 'poweroff', 'reload', 'search', 'setting', 'share-alt', 'shopping-cart', 'tablet', 'tag', 'tags', 'to-top', 'upload', 'user', 'video-camera', 'home', 'loading', 'loading-3-quarters', 'cloud-upload', 'star', 'heart', 'environment', 'eye', 'camera', 'save', 'team', 'solution', 'phone', 'filter', 'exception', 'export', 'customer-service', 'qrcode', 'scan', 'like', 'dislike', 'message', 'pay-circle', 'calculator', 'pushpin', 'bulb', 'select', 'switcher', 'rocket', 'bell', 'disconnect', 'database', 'compass', 'barcode', 'hourglass', 'key', 'flag', 'layout', 'printer', 'sound', 'usb', 'skin', 'tool', 'sync', 'wifi', 'car', 'schedule', 'user-add', 'user-delete', 'usergroup-add', 'usergroup-delete', 'man', 'woman', 'shop', 'gift', 'idcard', 'medicine-box', 'red-envelope', 'coffee', 'copyright', 'trademark', 'safety', 'wallet', 'bank', 'trophy', 'contacts', 'global', 'shake', 'api', 'fork', 'dashboard', 'table', 'profile', 'alert', 'audit', 'branches', 'build', 'border', 'crown', 'experiment', 'fire', 'money-collect', 'property-safety', 'read', 'reconciliation', 'rest', 'security-scan', 'insurance', 'interaction', 'safety-certificate', 'project', 'thunderbolt', 'block', 'cluster', 'deployment-unit', 'dollar', 'euro', 'pound', 'file-done', 'file-exclamation', 'file-protect', 'file-search', 'file-sync', 'gateway', 'gold', 'robot', 'shopping'] |
| | | }, |
| | | { |
| | | key: 'application', |
| | | title: '品牌和标识', |
| | | icons: ['android', 'apple', 'windows', 'ie', 'chrome', 'github', 'aliwangwang', 'dingding', 'weibo-square', 'weibo-circle', 'taobao-circle', 'html5', 'weibo', 'twitter', 'wechat', 'youtube', 'alipay-circle', 'taobao', 'skype', 'qq', 'medium-workmark', 'gitlab', 'medium', 'linkedin', 'google-plus', 'dropbox', 'facebook', 'codepen', 'code-sandbox', 'amazon', 'google', 'codepen-circle', 'alipay', 'ant-design', 'aliyun', 'zhihu', 'slack', 'slack-square', 'behance', 'behance-square', 'dribbble', 'dribbble-square', 'instagram', 'yuque', 'alibaba', 'yahoo'] |
| | | } |
| | | ] |
New file |
| | |
| | | import IconSelector from './IconSelector' |
| | | export default IconSelector |
New file |
| | |
| | | <template> |
| | | <a-layout-sider |
| | | :class="['sider', isDesktop() ? null : 'shadow', theme, fixSiderbar ? 'ant-fixed-sidemenu' : null ]" |
| | | width="230px" |
| | | :collapsible="collapsible" |
| | | v-model="collapsed" |
| | | :trigger="null"> |
| | | <logo /> |
| | | <s-menu |
| | | :collapsed="collapsed" |
| | | :menu="menus" |
| | | :theme="theme" |
| | | :mode="mode" |
| | | @select="onSelect" |
| | | ></s-menu> |
| | | </a-layout-sider> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | import Logo from '@/components/tools/Logo' |
| | | import SMenu from './index' |
| | | import { mixin, mixinDevice } from '@/utils/mixin' |
| | | |
| | | export default { |
| | | name: 'SideMenu', |
| | | components: { Logo, SMenu }, |
| | | mixins: [mixin, mixinDevice], |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | required: false, |
| | | default: 'inline' |
| | | }, |
| | | theme: { |
| | | type: String, |
| | | required: false, |
| | | default: 'dark' |
| | | }, |
| | | collapsible: { |
| | | type: Boolean, |
| | | required: false, |
| | | default: false |
| | | }, |
| | | collapsed: { |
| | | type: Boolean, |
| | | required: false, |
| | | default: false |
| | | }, |
| | | menus: { |
| | | type: Array, |
| | | required: true |
| | | } |
| | | }, |
| | | methods: { |
| | | onSelect (obj) { |
| | | this.$emit('menuSelect', obj) |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import SMenu from './menu' |
| | | export default SMenu |
New file |
| | |
| | | import Menu from 'ant-design-vue/es/menu' |
| | | import Icon from 'ant-design-vue/es/icon' |
| | | |
| | | export default { |
| | | name: 'SMenu', |
| | | props: { |
| | | menu: { |
| | | type: Array, |
| | | required: true |
| | | }, |
| | | theme: { |
| | | type: String, |
| | | required: false, |
| | | default: 'dark' |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | required: false, |
| | | default: 'inline' |
| | | }, |
| | | collapsed: { |
| | | type: Boolean, |
| | | required: false, |
| | | default: false |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | openKeys: [], |
| | | selectedKeys: [], |
| | | cachedOpenKeys: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | rootSubmenuKeys: vm => { |
| | | const keys = [] |
| | | vm.menu.forEach(item => keys.push(item.path)) |
| | | return keys |
| | | } |
| | | }, |
| | | mounted () { |
| | | this.updateMenu() |
| | | }, |
| | | watch: { |
| | | collapsed (val) { |
| | | if (val) { |
| | | this.cachedOpenKeys = this.openKeys.concat() |
| | | this.openKeys = [] |
| | | } else { |
| | | this.openKeys = this.cachedOpenKeys |
| | | } |
| | | }, |
| | | $route: function () { |
| | | this.updateMenu() |
| | | } |
| | | }, |
| | | methods: { |
| | | // select menu item |
| | | onOpenChange (openKeys) { |
| | | // 在水平模式下时执行,并且不再执行后续 |
| | | if (this.mode === 'horizontal') { |
| | | this.openKeys = openKeys |
| | | return |
| | | } |
| | | // 非水平模式时 |
| | | const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key)) |
| | | if (!this.rootSubmenuKeys.includes(latestOpenKey)) { |
| | | this.openKeys = openKeys |
| | | } else { |
| | | this.openKeys = latestOpenKey ? [latestOpenKey] : [] |
| | | } |
| | | }, |
| | | onSelect ({ item, key, selectedKeys }) { |
| | | this.selectedKeys = selectedKeys |
| | | this.$emit('select', { item, key, selectedKeys }) |
| | | }, |
| | | updateMenu () { |
| | | const routes = this.$route.matched.concat() |
| | | const { hidden } = this.$route.meta |
| | | if (routes.length >= 3 && hidden) { |
| | | routes.pop() |
| | | this.selectedKeys = [routes[routes.length - 1].path] |
| | | } else { |
| | | this.selectedKeys = [routes.pop().path] |
| | | } |
| | | const openKeys = [] |
| | | if (this.mode === 'inline') { |
| | | routes.forEach(item => { |
| | | openKeys.push(item.path) |
| | | }) |
| | | } |
| | | |
| | | this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys) |
| | | }, |
| | | |
| | | // render |
| | | renderItem (menu) { |
| | | if (!menu.hidden) { |
| | | return menu.children && !menu.hideChildrenInMenu ? this.renderSubMenu(menu) : this.renderMenuItem(menu) |
| | | } |
| | | return null |
| | | }, |
| | | renderMenuItem (menu) { |
| | | const target = menu.meta.target || null |
| | | const CustomTag = target && 'a' || 'router-link' |
| | | const props = { to: { name: menu.name } } |
| | | const attrs = { href: menu.path, target: menu.meta.target } |
| | | |
| | | if (menu.children && menu.hideChildrenInMenu) { |
| | | // 把有子菜单的 并且 父菜单是要隐藏子菜单的 |
| | | // 都给子菜单增加一个 hidden 属性 |
| | | // 用来给刷新页面时, selectedKeys 做控制用 |
| | | menu.children.forEach(item => { |
| | | item.meta = Object.assign(item.meta, { hidden: true }) |
| | | }) |
| | | } |
| | | |
| | | return ( |
| | | <Menu.Item {...{ key: menu.path }}> |
| | | <CustomTag {...{ props, attrs }}> |
| | | {this.renderIcon(menu.meta.icon)} |
| | | <span>{menu.meta.title}</span> |
| | | </CustomTag> |
| | | </Menu.Item> |
| | | ) |
| | | }, |
| | | renderSubMenu (menu) { |
| | | const itemArr = [] |
| | | if (!menu.hideChildrenInMenu) { |
| | | menu.children.forEach(item => itemArr.push(this.renderItem(item))) |
| | | } |
| | | return ( |
| | | <Menu.SubMenu {...{ key: menu.path }}> |
| | | <span slot="title"> |
| | | {this.renderIcon(menu.meta.icon)} |
| | | <span>{menu.meta.title}</span> |
| | | </span> |
| | | {itemArr} |
| | | </Menu.SubMenu> |
| | | ) |
| | | }, |
| | | renderIcon (icon) { |
| | | if (icon === 'none' || icon === undefined) { |
| | | return null |
| | | } |
| | | const props = {} |
| | | typeof (icon) === 'object' ? props.component = icon : props.type = icon |
| | | return ( |
| | | <Icon {... { props } }/> |
| | | ) |
| | | } |
| | | }, |
| | | |
| | | render () { |
| | | const dynamicProps = { |
| | | props: { |
| | | mode: this.mode, |
| | | theme: this.theme, |
| | | openKeys: this.openKeys, |
| | | selectedKeys: this.selectedKeys |
| | | }, |
| | | on: { |
| | | openChange: this.onOpenChange, |
| | | select: this.onSelect |
| | | } |
| | | } |
| | | |
| | | const menuTree = this.menu.map(item => { |
| | | if (item.hidden) { |
| | | return null |
| | | } |
| | | return this.renderItem(item) |
| | | }) |
| | | |
| | | return (<Menu {...dynamicProps}>{menuTree}</Menu>) |
| | | } |
| | | } |
New file |
| | |
| | | import Menu from 'ant-design-vue/es/menu' |
| | | import Icon from 'ant-design-vue/es/icon' |
| | | |
| | | const { Item, SubMenu } = Menu |
| | | |
| | | export default { |
| | | name: 'SMenu', |
| | | props: { |
| | | menu: { |
| | | type: Array, |
| | | required: true |
| | | }, |
| | | theme: { |
| | | type: String, |
| | | required: false, |
| | | default: 'dark' |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | required: false, |
| | | default: 'inline' |
| | | }, |
| | | collapsed: { |
| | | type: Boolean, |
| | | required: false, |
| | | default: false |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | openKeys: [], |
| | | selectedKeys: [], |
| | | cachedOpenKeys: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | rootSubmenuKeys: vm => { |
| | | const keys = [] |
| | | vm.menu.forEach(item => keys.push(item.path)) |
| | | return keys |
| | | } |
| | | }, |
| | | created () { |
| | | this.updateMenu() |
| | | }, |
| | | watch: { |
| | | collapsed (val) { |
| | | if (val) { |
| | | this.cachedOpenKeys = this.openKeys.concat() |
| | | this.openKeys = [] |
| | | } else { |
| | | this.openKeys = this.cachedOpenKeys |
| | | } |
| | | }, |
| | | $route: function () { |
| | | this.updateMenu() |
| | | } |
| | | }, |
| | | methods: { |
| | | renderIcon: function (h, icon) { |
| | | if (icon === 'none' || icon === undefined) { |
| | | return null |
| | | } |
| | | const props = {} |
| | | typeof (icon) === 'object' ? props.component = icon : props.type = icon |
| | | return h(Icon, { props: { ...props } }) |
| | | }, |
| | | renderMenuItem: function (h, menu, pIndex, index) { |
| | | const target = menu.meta.target || null |
| | | return h(Item, { key: menu.path ? menu.path : 'item_' + pIndex + '_' + index }, [ |
| | | h('router-link', { attrs: { to: { name: menu.name }, target: target } }, [ |
| | | this.renderIcon(h, menu.meta.icon), |
| | | h('span', [menu.meta.title]) |
| | | ]) |
| | | ]) |
| | | }, |
| | | renderSubMenu: function (h, menu, pIndex, index) { |
| | | const this2_ = this |
| | | const subItem = [h('span', { slot: 'title' }, [this.renderIcon(h, menu.meta.icon), h('span', [menu.meta.title])])] |
| | | const itemArr = [] |
| | | const pIndex_ = pIndex + '_' + index |
| | | console.log('menu', menu) |
| | | if (!menu.hideChildrenInMenu) { |
| | | menu.children.forEach(function (item, i) { |
| | | itemArr.push(this2_.renderItem(h, item, pIndex_, i)) |
| | | }) |
| | | } |
| | | return h(SubMenu, { key: menu.path ? menu.path : 'submenu_' + pIndex + '_' + index }, subItem.concat(itemArr)) |
| | | }, |
| | | renderItem: function (h, menu, pIndex, index) { |
| | | if (!menu.hidden) { |
| | | return menu.children && !menu.hideChildrenInMenu |
| | | ? this.renderSubMenu(h, menu, pIndex, index) |
| | | : this.renderMenuItem(h, menu, pIndex, index) |
| | | } |
| | | }, |
| | | renderMenu: function (h, menuTree) { |
| | | const this2_ = this |
| | | const menuArr = [] |
| | | menuTree.forEach(function (menu, i) { |
| | | if (!menu.hidden) { |
| | | menuArr.push(this2_.renderItem(h, menu, '0', i)) |
| | | } |
| | | }) |
| | | return menuArr |
| | | }, |
| | | onOpenChange (openKeys) { |
| | | const latestOpenKey = openKeys.find(key => !this.openKeys.includes(key)) |
| | | if (!this.rootSubmenuKeys.includes(latestOpenKey)) { |
| | | this.openKeys = openKeys |
| | | } else { |
| | | this.openKeys = latestOpenKey ? [latestOpenKey] : [] |
| | | } |
| | | }, |
| | | updateMenu () { |
| | | const routes = this.$route.matched.concat() |
| | | |
| | | if (routes.length >= 4 && this.$route.meta.hidden) { |
| | | routes.pop() |
| | | this.selectedKeys = [routes[2].path] |
| | | } else { |
| | | this.selectedKeys = [routes.pop().path] |
| | | } |
| | | |
| | | const openKeys = [] |
| | | if (this.mode === 'inline') { |
| | | routes.forEach(item => { |
| | | openKeys.push(item.path) |
| | | }) |
| | | } |
| | | |
| | | this.collapsed ? (this.cachedOpenKeys = openKeys) : (this.openKeys = openKeys) |
| | | } |
| | | }, |
| | | render (h) { |
| | | return h( |
| | | Menu, |
| | | { |
| | | props: { |
| | | theme: this.$props.theme, |
| | | mode: this.$props.mode, |
| | | openKeys: this.openKeys, |
| | | selectedKeys: this.selectedKeys |
| | | }, |
| | | on: { |
| | | openChange: this.onOpenChange, |
| | | select: obj => { |
| | | this.selectedKeys = obj.selectedKeys |
| | | this.$emit('select', obj) |
| | | } |
| | | } |
| | | }, |
| | | this.renderMenu(h, this.menu) |
| | | ) |
| | | } |
| | | } |
New file |
| | |
| | | <script> |
| | | import events from './events' |
| | | |
| | | export default { |
| | | name: 'MultiTab', |
| | | data () { |
| | | return { |
| | | fullPathList: [], |
| | | pages: [], |
| | | activeKey: '', |
| | | newTabIndex: 0 |
| | | } |
| | | }, |
| | | created () { |
| | | // bind event |
| | | events.$on('open', val => { |
| | | if (!val) { |
| | | throw new Error(`multi-tab: open tab ${val} err`) |
| | | } |
| | | this.activeKey = val |
| | | }).$on('close', val => { |
| | | if (!val) { |
| | | this.closeThat(this.activeKey) |
| | | return |
| | | } |
| | | this.closeThat(val) |
| | | }).$on('rename', ({ key, name }) => { |
| | | console.log('rename', key, name) |
| | | try { |
| | | const item = this.pages.find(item => item.path === key) |
| | | item.meta.customTitle = name |
| | | this.$forceUpdate() |
| | | } catch (e) { |
| | | } |
| | | }) |
| | | |
| | | this.pages.push(this.$route) |
| | | this.fullPathList.push(this.$route.fullPath) |
| | | this.selectedLastPath() |
| | | }, |
| | | methods: { |
| | | onEdit (targetKey, action) { |
| | | this[action](targetKey) |
| | | }, |
| | | remove (targetKey) { |
| | | this.pages = this.pages.filter(page => page.fullPath !== targetKey) |
| | | this.fullPathList = this.fullPathList.filter(path => path !== targetKey) |
| | | // 判断当前标签是否关闭,若关闭则跳转到最后一个还存在的标签页 |
| | | if (!this.fullPathList.includes(this.activeKey)) { |
| | | this.selectedLastPath() |
| | | } |
| | | }, |
| | | selectedLastPath () { |
| | | this.activeKey = this.fullPathList[this.fullPathList.length - 1] |
| | | }, |
| | | |
| | | // content menu |
| | | closeThat (e) { |
| | | // 判断是否为最后一个标签页,如果是最后一个,则无法被关闭 |
| | | if (this.fullPathList.length > 1) { |
| | | this.remove(e) |
| | | } else { |
| | | this.$message.info('这是最后一个标签了, 无法被关闭') |
| | | } |
| | | }, |
| | | closeLeft (e) { |
| | | const currentIndex = this.fullPathList.indexOf(e) |
| | | if (currentIndex > 0) { |
| | | this.fullPathList.forEach((item, index) => { |
| | | if (index < currentIndex) { |
| | | this.remove(item) |
| | | } |
| | | }) |
| | | } else { |
| | | this.$message.info('左侧没有标签') |
| | | } |
| | | }, |
| | | closeRight (e) { |
| | | const currentIndex = this.fullPathList.indexOf(e) |
| | | if (currentIndex < (this.fullPathList.length - 1)) { |
| | | this.fullPathList.forEach((item, index) => { |
| | | if (index > currentIndex) { |
| | | this.remove(item) |
| | | } |
| | | }) |
| | | } else { |
| | | this.$message.info('右侧没有标签') |
| | | } |
| | | }, |
| | | closeAll (e) { |
| | | const currentIndex = this.fullPathList.indexOf(e) |
| | | this.fullPathList.forEach((item, index) => { |
| | | if (index !== currentIndex) { |
| | | this.remove(item) |
| | | } |
| | | }) |
| | | }, |
| | | closeMenuClick (key, route) { |
| | | this[key](route) |
| | | }, |
| | | renderTabPaneMenu (e) { |
| | | return ( |
| | | <a-menu {...{ on: { click: ({ key, item, domEvent }) => { this.closeMenuClick(key, e) } } }}> |
| | | <a-menu-item key="closeThat">关闭当前标签</a-menu-item> |
| | | <a-menu-item key="closeRight">关闭右侧</a-menu-item> |
| | | <a-menu-item key="closeLeft">关闭左侧</a-menu-item> |
| | | <a-menu-item key="closeAll">关闭全部</a-menu-item> |
| | | </a-menu> |
| | | ) |
| | | }, |
| | | // render |
| | | renderTabPane (title, keyPath) { |
| | | const menu = this.renderTabPaneMenu(keyPath) |
| | | |
| | | return ( |
| | | <a-dropdown overlay={menu} trigger={['contextmenu']}> |
| | | <span style={{ userSelect: 'none' }}>{ title }</span> |
| | | </a-dropdown> |
| | | ) |
| | | } |
| | | }, |
| | | watch: { |
| | | '$route': function (newVal) { |
| | | this.activeKey = newVal.fullPath |
| | | if (this.fullPathList.indexOf(newVal.fullPath) < 0) { |
| | | this.fullPathList.push(newVal.fullPath) |
| | | this.pages.push(newVal) |
| | | } |
| | | }, |
| | | activeKey: function (newPathKey) { |
| | | this.$router.push({ path: newPathKey }) |
| | | } |
| | | }, |
| | | render () { |
| | | const { onEdit, $data: { pages } } = this |
| | | const panes = pages.map(page => { |
| | | return ( |
| | | <a-tab-pane |
| | | style={{ height: 0, background: '#8999ee', color: '#899ee' }} |
| | | tab={this.renderTabPane(page.meta.customTitle || page.meta.title, page.fullPath)} |
| | | key={page.fullPath} closable={pages.length > 1} |
| | | > |
| | | </a-tab-pane>) |
| | | }) |
| | | |
| | | return ( |
| | | <div class="ant-pro-multi-tab"> |
| | | <div class="ant-pro-multi-tab-wrapper"> |
| | | <a-tabs |
| | | hideAdd |
| | | tabBarGutter={-1} |
| | | type={'editable-card'} |
| | | v-model={this.activeKey} |
| | | tabBarStyle={{ background: '#FFF', margin: 0, paddingLeft: '0px', paddingTop: '0px' }} |
| | | {...{ on: { edit: onEdit } }}> |
| | | {panes} |
| | | </a-tabs> |
| | | </div> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import Vue from 'vue' |
| | | export default new Vue() |
New file |
| | |
| | | import events from './events' |
| | | import MultiTab from './MultiTab' |
| | | import './index.less' |
| | | |
| | | const api = { |
| | | /** |
| | | * open new tab on route fullPath |
| | | * @param config |
| | | */ |
| | | open: function (config) { |
| | | events.$emit('open', config) |
| | | }, |
| | | rename: function (key, name) { |
| | | events.$emit('rename', { key: key, name: name }) |
| | | }, |
| | | /** |
| | | * close current page |
| | | */ |
| | | closeCurrentPage: function () { |
| | | this.close() |
| | | }, |
| | | /** |
| | | * close route fullPath tab |
| | | * @param config |
| | | */ |
| | | close: function (config) { |
| | | events.$emit('close', config) |
| | | } |
| | | } |
| | | |
| | | MultiTab.install = function (Vue) { |
| | | if (Vue.prototype.$multiTab) { |
| | | return |
| | | } |
| | | api.instance = events |
| | | Vue.prototype.$multiTab = api |
| | | Vue.component('multi-tab', MultiTab) |
| | | } |
| | | |
| | | export default MultiTab |
New file |
| | |
| | | @import '../index'; |
| | | |
| | | @multi-tab-prefix-cls: ~"@{ant-pro-prefix}-multi-tab"; |
| | | @multi-tab-wrapper-prefix-cls: ~"@{ant-pro-prefix}-multi-tab-wrapper"; |
| | | |
| | | /* |
| | | .topmenu .@{multi-tab-prefix-cls} { |
| | | max-width: 1200px; |
| | | margin: -23px auto 24px auto; |
| | | } |
| | | */ |
| | | .@{multi-tab-prefix-cls} { |
| | | margin: -23px -24px 24px -24px; |
| | | background: #fff; |
| | | } |
| | | |
| | | .topmenu .@{multi-tab-wrapper-prefix-cls} { |
| | | max-width: 1200px; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | .topmenu.content-width-Fluid .@{multi-tab-wrapper-prefix-cls} { |
| | | max-width: 100%; |
| | | margin: 0 auto; |
| | | } |
New file |
| | |
| | | @import url('../index.less'); |
| | | |
| | | /* Make clicks pass-through */ |
| | | #nprogress { |
| | | pointer-events: none; |
| | | } |
| | | |
| | | #nprogress .bar { |
| | | background: @primary-color; |
| | | |
| | | position: fixed; |
| | | z-index: 1031; |
| | | top: 0; |
| | | left: 0; |
| | | |
| | | width: 100%; |
| | | height: 2px; |
| | | } |
| | | |
| | | /* Fancy blur effect */ |
| | | #nprogress .peg { |
| | | display: block; |
| | | position: absolute; |
| | | right: 0px; |
| | | width: 100px; |
| | | height: 100%; |
| | | box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; |
| | | opacity: 1.0; |
| | | |
| | | -webkit-transform: rotate(3deg) translate(0px, -4px); |
| | | -ms-transform: rotate(3deg) translate(0px, -4px); |
| | | transform: rotate(3deg) translate(0px, -4px); |
| | | } |
| | | |
| | | /* Remove these to get rid of the spinner */ |
| | | #nprogress .spinner { |
| | | display: block; |
| | | position: fixed; |
| | | z-index: 1031; |
| | | top: 15px; |
| | | right: 15px; |
| | | } |
| | | |
| | | #nprogress .spinner-icon { |
| | | width: 18px; |
| | | height: 18px; |
| | | box-sizing: border-box; |
| | | |
| | | border: solid 2px transparent; |
| | | border-top-color: @primary-color; |
| | | border-left-color: @primary-color; |
| | | border-radius: 50%; |
| | | |
| | | -webkit-animation: nprogress-spinner 400ms linear infinite; |
| | | animation: nprogress-spinner 400ms linear infinite; |
| | | } |
| | | |
| | | .nprogress-custom-parent { |
| | | overflow: hidden; |
| | | position: relative; |
| | | } |
| | | |
| | | .nprogress-custom-parent #nprogress .spinner, |
| | | .nprogress-custom-parent #nprogress .bar { |
| | | position: absolute; |
| | | } |
| | | |
| | | @-webkit-keyframes nprogress-spinner { |
| | | 0% { -webkit-transform: rotate(0deg); } |
| | | 100% { -webkit-transform: rotate(360deg); } |
| | | } |
| | | @keyframes nprogress-spinner { |
| | | 0% { transform: rotate(0deg); } |
| | | 100% { transform: rotate(360deg); } |
| | | } |
| | | |
New file |
| | |
| | | <template> |
| | | <a-popover |
| | | v-model="visible" |
| | | trigger="click" |
| | | placement="bottomRight" |
| | | overlayClassName="header-notice-wrapper" |
| | | :getPopupContainer="() => $refs.noticeRef.parentElement" |
| | | :autoAdjustOverflow="true" |
| | | :arrowPointAtCenter="true" |
| | | :overlayStyle="{ width: '300px', top: '50px' }" |
| | | > |
| | | <template slot="content"> |
| | | <a-spin :spinning="loading"> |
| | | <a-tabs> |
| | | <a-tab-pane tab="通知" key="1"> |
| | | <a-list> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你收到了 14 份新周报" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </a-tab-pane> |
| | | <a-tab-pane tab="消息" key="2"> |
| | | <a-list> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你收到了 14 份新周报" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </a-tab-pane> |
| | | <a-tab-pane tab="待办" key="3"> |
| | | <a-list> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你收到了 14 份新周报" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/ThXAXghbEsBCCSDihZxY.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="你推荐的 曲妮妮 已通过第三轮面试" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/OKJXDXrmkNshAMvwtvhu.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-list-item-meta title="这种模板可以区分多种通知类型" description="一年前"> |
| | | <a-avatar style="background-color: white" slot="avatar" src="https://gw.alipayobjects.com/zos/rmsportal/kISTdvpyTAhtGxpovNWd.png"/> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </a-tab-pane> |
| | | </a-tabs> |
| | | </a-spin> |
| | | </template> |
| | | <span @click="fetchNotice" class="header-notice" ref="noticeRef" style="padding: 0 18px"> |
| | | <a-badge count="12"> |
| | | <a-icon style="font-size: 16px; padding: 4px" type="bell" /> |
| | | </a-badge> |
| | | </span> |
| | | </a-popover> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'HeaderNotice', |
| | | data () { |
| | | return { |
| | | loading: false, |
| | | visible: false |
| | | } |
| | | }, |
| | | methods: { |
| | | fetchNotice () { |
| | | if (!this.visible) { |
| | | this.loading = true |
| | | setTimeout(() => { |
| | | this.loading = false |
| | | }, 100) |
| | | } else { |
| | | this.loading = false |
| | | } |
| | | this.visible = !this.visible |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="css"> |
| | | .header-notice-wrapper { |
| | | top: 50px !important; |
| | | } |
| | | </style> |
| | | <style lang="less" scoped> |
| | | .header-notice{ |
| | | display: inline-block; |
| | | transition: all 0.3s; |
| | | |
| | | span { |
| | | vertical-align: initial; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import NoticeIcon from './NoticeIcon' |
| | | export default NoticeIcon |
New file |
| | |
| | | <template> |
| | | <div :class="[prefixCls]"> |
| | | <slot name="subtitle"> |
| | | <div :class="[`${prefixCls}-subtitle`]">{{ typeof subTitle === 'string' ? subTitle : subTitle() }}</div> |
| | | </slot> |
| | | <div class="number-info-value"> |
| | | <span>{{ total }}</span> |
| | | <span class="sub-total"> |
| | | {{ subTotal }} |
| | | <icon :type="`caret-${status}`" /> |
| | | </span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import Icon from 'ant-design-vue/es/icon' |
| | | |
| | | export default { |
| | | name: 'NumberInfo', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-number-info' |
| | | }, |
| | | total: { |
| | | type: Number, |
| | | required: true |
| | | }, |
| | | subTotal: { |
| | | type: Number, |
| | | required: true |
| | | }, |
| | | subTitle: { |
| | | type: [String, Function], |
| | | default: '' |
| | | }, |
| | | status: { |
| | | type: String, |
| | | default: 'up' |
| | | } |
| | | }, |
| | | components: { |
| | | Icon |
| | | }, |
| | | data () { |
| | | return {} |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "index"; |
| | | </style> |
New file |
| | |
| | | import NumberInfo from './NumberInfo' |
| | | |
| | | export default NumberInfo |
New file |
| | |
| | | @import "../index"; |
| | | |
| | | @numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info"; |
| | | |
| | | .@{numberInfo-prefix-cls} { |
| | | |
| | | .ant-pro-number-info-subtitle { |
| | | color: @text-color-secondary; |
| | | font-size: @font-size-base; |
| | | height: 22px; |
| | | line-height: 22px; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .number-info-value { |
| | | margin-top: 4px; |
| | | font-size: 0; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | white-space: nowrap; |
| | | |
| | | & > span { |
| | | color: @heading-color; |
| | | display: inline-block; |
| | | line-height: 32px; |
| | | height: 32px; |
| | | font-size: 24px; |
| | | margin-right: 32px; |
| | | } |
| | | |
| | | .sub-total { |
| | | color: @text-color-secondary; |
| | | font-size: @font-size-lg; |
| | | vertical-align: top; |
| | | margin-right: 0; |
| | | i { |
| | | font-size: 12px; |
| | | transform: scale(0.82); |
| | | margin-left: 4px; |
| | | } |
| | | :global { |
| | | .anticon-caret-up { |
| | | color: @red-6; |
| | | } |
| | | .anticon-caret-down { |
| | | color: @green-6; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | # NumberInfo 数据文本 |
| | | |
| | | 常用在数据卡片中,用于突出展示某个业务数据。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import NumberInfo from '@/components/NumberInfo' |
| | | |
| | | export default { |
| | | components: { |
| | | NumberInfo |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 [demo](https://pro.loacg.com/test/home) |
| | | |
| | | ```html |
| | | <number-info |
| | | :sub-title="() => { return 'Visits this week' }" |
| | | :total="12321" |
| | | status="up" |
| | | :sub-total="17.1"></number-info> |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## API |
| | | |
| | | 参数 | 说明 | 类型 | 默认值 |
| | | ----|------|-----|------ |
| | | title | 标题 | ReactNode\|string | - |
| | | subTitle | 子标题 | ReactNode\|string | - |
| | | total | 总量 | ReactNode\|string | - |
| | | subTotal | 子总量 | ReactNode\|string | - |
| | | status | 增加状态 | 'up \| down' | - |
| | | theme | 状态样式 | string | 'light' |
| | | gap | 设置数字和描述之间的间距(像素)| number | 8 |
New file |
| | |
| | | <template> |
| | | <div class="page-header"> |
| | | <div class="page-header-index-wide"> |
| | | <s-breadcrumb /> |
| | | <div class="detail"> |
| | | <div class="main" v-if="!$route.meta.hiddenHeaderContent"> |
| | | <div class="row"> |
| | | <img v-if="logo" :src="logo" class="logo"/> |
| | | <h1 v-if="title" class="title">{{ title }}</h1> |
| | | <div class="action"> |
| | | <slot name="action"></slot> |
| | | </div> |
| | | </div> |
| | | <div class="row"> |
| | | <div v-if="avatar" class="avatar"> |
| | | <a-avatar :src="avatar" /> |
| | | </div> |
| | | <div v-if="this.$slots.content" class="headerContent"> |
| | | <slot name="content"></slot> |
| | | </div> |
| | | <div v-if="this.$slots.extra" class="extra"> |
| | | <slot name="extra"></slot> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | <slot name="pageMenu"></slot> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import Breadcrumb from '@/components/tools/Breadcrumb' |
| | | |
| | | export default { |
| | | name: 'PageHeader', |
| | | components: { |
| | | 's-breadcrumb': Breadcrumb |
| | | }, |
| | | props: { |
| | | title: { |
| | | type: [String, Boolean], |
| | | default: true, |
| | | required: false |
| | | }, |
| | | logo: { |
| | | type: String, |
| | | default: '', |
| | | required: false |
| | | }, |
| | | avatar: { |
| | | type: String, |
| | | default: '', |
| | | required: false |
| | | } |
| | | }, |
| | | data () { |
| | | return {} |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .page-header { |
| | | background: #fff; |
| | | padding: 10px 32px 0; |
| | | border-bottom: 0px solid #e8e8e8; |
| | | |
| | | .breadcrumb { |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .detail { |
| | | display: flex; |
| | | /*margin-bottom: 16px;*/ |
| | | |
| | | .avatar { |
| | | flex: 0 1 72px; |
| | | margin: 0 24px 8px 0; |
| | | |
| | | & > span { |
| | | border-radius: 72px; |
| | | display: block; |
| | | width: 72px; |
| | | height: 72px; |
| | | } |
| | | } |
| | | |
| | | .main { |
| | | width: 100%; |
| | | flex: 0 1 auto; |
| | | |
| | | .row { |
| | | display: flex; |
| | | width: 100%; |
| | | |
| | | .avatar { |
| | | margin-bottom: 10px; |
| | | } |
| | | } |
| | | |
| | | .title { |
| | | font-size: 20px; |
| | | font-weight: 500; |
| | | |
| | | font-size: 20px; |
| | | line-height: 28px; |
| | | font-weight: 500; |
| | | color: rgba(0, 0, 0, 0.85); |
| | | margin-bottom: 16px; |
| | | flex: auto; |
| | | } |
| | | .logo { |
| | | width: 28px; |
| | | height: 28px; |
| | | border-radius: 4px; |
| | | margin-right: 16px; |
| | | } |
| | | .content, |
| | | .headerContent { |
| | | flex: auto; |
| | | color: rgba(0, 0, 0, 0.45); |
| | | line-height: 22px; |
| | | |
| | | .link { |
| | | margin-top: 16px; |
| | | line-height: 24px; |
| | | |
| | | a { |
| | | font-size: 14px; |
| | | margin-right: 32px; |
| | | } |
| | | } |
| | | } |
| | | .extra { |
| | | flex: 0 1 auto; |
| | | margin-left: 88px; |
| | | min-width: 242px; |
| | | text-align: right; |
| | | } |
| | | .action { |
| | | margin-left: 56px; |
| | | min-width: 266px; |
| | | flex: 0 1 auto; |
| | | text-align: right; |
| | | &:empty { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .mobile .page-header { |
| | | .main { |
| | | .row { |
| | | flex-wrap: wrap; |
| | | |
| | | .avatar { |
| | | flex: 0 1 25%; |
| | | margin: 0 2% 8px 0; |
| | | } |
| | | |
| | | .content, |
| | | .headerContent { |
| | | flex: 0 1 70%; |
| | | |
| | | .link { |
| | | margin-top: 16px; |
| | | line-height: 24px; |
| | | |
| | | a { |
| | | font-size: 14px; |
| | | margin-right: 10px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .extra { |
| | | flex: 1 1 auto; |
| | | margin-left: 0; |
| | | min-width: 0; |
| | | text-align: right; |
| | | } |
| | | |
| | | .action { |
| | | margin-left: unset; |
| | | min-width: 266px; |
| | | flex: 0 1 auto; |
| | | text-align: left; |
| | | margin-bottom: 12px; |
| | | |
| | | &:empty { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import PageHeader from './PageHeader' |
| | | export default PageHeader |
New file |
| | |
| | | import { Spin } from 'ant-design-vue' |
| | | |
| | | export const PageLoading = { |
| | | name: 'PageLoading', |
| | | props: { |
| | | tip: { |
| | | type: String, |
| | | default: 'Loading..' |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: 'large' |
| | | } |
| | | }, |
| | | render () { |
| | | const style = { |
| | | textAlign: 'center', |
| | | background: 'rgba(0,0,0,0.6)', |
| | | position: 'fixed', |
| | | top: 0, |
| | | bottom: 0, |
| | | left: 0, |
| | | right: 0, |
| | | zIndex: 1100 |
| | | } |
| | | const spinStyle = { |
| | | position: 'absolute', |
| | | left: '50%', |
| | | top: '40%', |
| | | transform: 'translate(-50%, -50%)' |
| | | } |
| | | return (<div style={style}> |
| | | <Spin size={this.size} style={spinStyle} tip={this.tip} /> |
| | | </div>) |
| | | } |
| | | } |
| | | |
| | | const version = '0.0.1' |
| | | const loading = {} |
| | | |
| | | loading.newInstance = (Vue, options) => { |
| | | let loadingElement = document.querySelector('body>div[type=loading]') |
| | | if (!loadingElement) { |
| | | loadingElement = document.createElement('div') |
| | | loadingElement.setAttribute('type', 'loading') |
| | | loadingElement.setAttribute('class', 'ant-loading-wrapper') |
| | | document.body.appendChild(loadingElement) |
| | | } |
| | | |
| | | const cdProps = Object.assign({ visible: false, size: 'large', tip: 'Loading...' }, options) |
| | | |
| | | const instance = new Vue({ |
| | | data () { |
| | | return { |
| | | ...cdProps |
| | | } |
| | | }, |
| | | render () { |
| | | const { tip } = this |
| | | const props = {} |
| | | this.tip && (props.tip = tip) |
| | | if (this.visible) { |
| | | return <PageLoading { ...{ props } } /> |
| | | } |
| | | return null |
| | | } |
| | | }).$mount(loadingElement) |
| | | |
| | | function update (config) { |
| | | const { visible, size, tip } = { ...cdProps, ...config } |
| | | instance.$set(instance, 'visible', visible) |
| | | if (tip) { |
| | | instance.$set(instance, 'tip', tip) |
| | | } |
| | | if (size) { |
| | | instance.$set(instance, 'size', size) |
| | | } |
| | | } |
| | | |
| | | return { |
| | | instance, |
| | | update |
| | | } |
| | | } |
| | | |
| | | const api = { |
| | | show: function (options) { |
| | | this.instance.update({ ...options, visible: true }) |
| | | }, |
| | | hide: function () { |
| | | this.instance.update({ visible: false }) |
| | | } |
| | | } |
| | | |
| | | const install = function (Vue, options) { |
| | | if (Vue.prototype.$loading) { |
| | | return |
| | | } |
| | | api.instance = loading.newInstance(Vue, options) |
| | | Vue.prototype.$loading = api |
| | | } |
| | | |
| | | export default { |
| | | version, |
| | | install |
| | | } |
New file |
| | |
| | | <template> |
| | | <div class="result"> |
| | | <div> |
| | | <a-icon :class="{ 'icon': true, [`${type}`]: true }" :type="localIsSuccess ? 'check-circle' : 'close-circle'"/> |
| | | </div> |
| | | <div class="title"> |
| | | <slot name="title"> |
| | | {{ title }} |
| | | </slot> |
| | | </div> |
| | | <div class="description"> |
| | | <slot name="description"> |
| | | {{ description }} |
| | | </slot> |
| | | </div> |
| | | <div class="extra" v-if="$slots.default"> |
| | | <slot></slot> |
| | | </div> |
| | | <div class="action" v-if="$slots.action"> |
| | | <slot name="action"></slot> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | const resultEnum = ['success', 'error'] |
| | | |
| | | export default { |
| | | name: 'Result', |
| | | props: { |
| | | /** @Deprecated */ |
| | | isSuccess: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | type: { |
| | | type: String, |
| | | default: resultEnum[0], |
| | | validator (val) { |
| | | return (val) => resultEnum.includes(val) |
| | | } |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | description: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | localIsSuccess: function () { |
| | | return this.type === resultEnum[0] |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .result { |
| | | text-align: center; |
| | | width: 72%; |
| | | margin: 0 auto; |
| | | padding: 24px 0 8px; |
| | | |
| | | .icon { |
| | | font-size: 72px; |
| | | line-height: 72px; |
| | | margin-bottom: 24px; |
| | | } |
| | | .success { |
| | | color: #52c41a; |
| | | } |
| | | .error { |
| | | color: red; |
| | | } |
| | | .title { |
| | | font-size: 24px; |
| | | color: rgba(0, 0, 0, .85); |
| | | font-weight: 500; |
| | | line-height: 32px; |
| | | margin-bottom: 16px; |
| | | } |
| | | .description { |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | color: rgba(0, 0, 0, 0.45); |
| | | margin-bottom: 24px; |
| | | } |
| | | .extra { |
| | | background: #fafafa; |
| | | padding: 24px 40px; |
| | | border-radius: 2px; |
| | | text-align: left; |
| | | } |
| | | .action { |
| | | margin-top: 32px; |
| | | } |
| | | } |
| | | |
| | | .mobile { |
| | | .result { |
| | | width: 100%; |
| | | margin: 0 auto; |
| | | padding: unset; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import Result from './Result.vue' |
| | | export default Result |
New file |
| | |
| | | import { Select } from 'ant-design-vue' |
| | | import './index.less' |
| | | |
| | | const GlobalSearch = { |
| | | name: 'GlobalSearch', |
| | | data () { |
| | | return { |
| | | visible: false |
| | | } |
| | | }, |
| | | mounted () { |
| | | const keyboardHandle = (e) => { |
| | | e.preventDefault() |
| | | e.stopPropagation() |
| | | const { ctrlKey, shiftKey, altKey, keyCode } = e |
| | | console.log('keyCode:', e.keyCode, e) |
| | | // key is `K` and hold ctrl |
| | | if (keyCode === 75 && ctrlKey && !shiftKey && !altKey) { |
| | | this.visible = !this.visible |
| | | } |
| | | } |
| | | document.addEventListener('keydown', keyboardHandle) |
| | | }, |
| | | render () { |
| | | const { visible } = this |
| | | const handleSearch = (e) => { |
| | | this.$emit('search', e) |
| | | } |
| | | |
| | | const handleChange = (e) => { |
| | | this.$emit('change', e) |
| | | } |
| | | if (!visible) { |
| | | return null |
| | | } |
| | | return ( |
| | | <div class={'global-search global-search-wrapper'}> |
| | | <div class={'global-search-box'}> |
| | | <Select |
| | | size={'large'} |
| | | showSearch |
| | | placeholder="Input search text.." |
| | | style={{ width: '100%' }} |
| | | defaultActiveFirstOption={false} |
| | | showArrow={false} |
| | | filterOption={false} |
| | | onSearch={handleSearch} |
| | | onChange={handleChange} |
| | | notFoundContent={null} |
| | | > |
| | | </Select> |
| | | <div class={'global-search-tips'}>Open with Ctrl/⌘ + K</div> |
| | | </div> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
| | | |
| | | GlobalSearch.install = function (Vue) { |
| | | Vue.component(GlobalSearch.name, GlobalSearch) |
| | | } |
| | | |
| | | export default GlobalSearch |
New file |
| | |
| | | @import "~ant-design-vue/es/style/themes/default"; |
| | | |
| | | .global-search-wrapper { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | z-index: @zindex-modal-mask; |
| | | background: @modal-mask-bg; |
| | | |
| | | .global-search-box { |
| | | position: absolute; |
| | | top: 20%; |
| | | left: 50%; |
| | | width: 450px; |
| | | transform: translate(-50%, -50%); |
| | | |
| | | .global-search-tips { |
| | | color: @white; |
| | | font-size: @font-size-lg; |
| | | text-align: right; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <div class="setting-drawer"> |
| | | <a-drawer |
| | | width="300" |
| | | placement="right" |
| | | @close="onClose" |
| | | :closable="false" |
| | | :visible="visible" |
| | | :drawer-style="{ position: 'absolute' }" |
| | | style="position: absolute" |
| | | > |
| | | <div class="setting-drawer-index-content"> |
| | | |
| | | <div :style="{ marginBottom: '24px' }"> |
| | | <h3 class="setting-drawer-index-title">整体风格设置</h3> |
| | | |
| | | <div class="setting-drawer-index-blockChecbox"> |
| | | <a-tooltip> |
| | | <template slot="title"> |
| | | 暗色菜单风格 |
| | | </template> |
| | | <div class="setting-drawer-index-item" @click="handleMenuTheme('dark')"> |
| | | <img src="https://gw.alipayobjects.com/zos/rmsportal/LCkqqYNmvBEbokSDscrm.svg" alt="dark"> |
| | | <div class="setting-drawer-index-selectIcon" v-if="navTheme === 'dark'"> |
| | | <a-icon type="check"/> |
| | | </div> |
| | | </div> |
| | | </a-tooltip> |
| | | |
| | | <a-tooltip> |
| | | <template slot="title"> |
| | | 亮色菜单风格 |
| | | </template> |
| | | <div class="setting-drawer-index-item" @click="handleMenuTheme('light')"> |
| | | <img src="https://gw.alipayobjects.com/zos/rmsportal/jpRkZQMyYRryryPNtyIC.svg" alt="light"> |
| | | <div class="setting-drawer-index-selectIcon" v-if="navTheme !== 'dark'"> |
| | | <a-icon type="check"/> |
| | | </div> |
| | | </div> |
| | | </a-tooltip> |
| | | </div> |
| | | </div> |
| | | |
| | | <div :style="{ marginBottom: '24px' }"> |
| | | <h3 class="setting-drawer-index-title">主题色</h3> |
| | | |
| | | <div style="height: 20px"> |
| | | <a-tooltip class="setting-drawer-theme-color-colorBlock" v-for="(item, index) in colorList" :key="index"> |
| | | <template slot="title"> |
| | | {{ item.key }} |
| | | </template> |
| | | <a-tag :color="item.color" @click="changeColor(item.color)"> |
| | | <a-icon type="check" v-if="item.color === primaryColor"></a-icon> |
| | | </a-tag> |
| | | </a-tooltip> |
| | | |
| | | </div> |
| | | </div> |
| | | <a-divider /> |
| | | |
| | | <div :style="{ marginBottom: '24px' }"> |
| | | <h3 class="setting-drawer-index-title">导航模式</h3> |
| | | |
| | | <div class="setting-drawer-index-blockChecbox"> |
| | | <a-tooltip> |
| | | <template slot="title"> |
| | | 侧边栏导航 |
| | | </template> |
| | | <div class="setting-drawer-index-item" @click="handleLayout('sidemenu')"> |
| | | <img src="https://gw.alipayobjects.com/zos/rmsportal/JopDzEhOqwOjeNTXkoje.svg" alt="sidemenu"> |
| | | <div class="setting-drawer-index-selectIcon" v-if="layoutMode === 'sidemenu'"> |
| | | <a-icon type="check"/> |
| | | </div> |
| | | </div> |
| | | </a-tooltip> |
| | | |
| | | <a-tooltip> |
| | | <template slot="title"> |
| | | 顶部栏导航 |
| | | </template> |
| | | <div class="setting-drawer-index-item" @click="handleLayout('topmenu')"> |
| | | <img src="https://gw.alipayobjects.com/zos/rmsportal/KDNDBbriJhLwuqMoxcAr.svg" alt="topmenu"> |
| | | <div class="setting-drawer-index-selectIcon" v-if="layoutMode !== 'sidemenu'"> |
| | | <a-icon type="check"/> |
| | | </div> |
| | | </div> |
| | | </a-tooltip> |
| | | </div> |
| | | <div :style="{ marginTop: '24px' }"> |
| | | <a-list :split="false"> |
| | | <a-list-item> |
| | | <a-tooltip slot="actions"> |
| | | <template slot="title"> |
| | | 该设定仅 [顶部栏导航] 时有效 |
| | | </template> |
| | | <a-select size="small" style="width: 80px;" :defaultValue="contentWidth" @change="handleContentWidthChange"> |
| | | <a-select-option value="Fixed">固定</a-select-option> |
| | | <a-select-option value="Fluid" v-if="layoutMode !== 'sidemenu'">流式</a-select-option> |
| | | </a-select> |
| | | </a-tooltip> |
| | | <a-list-item-meta> |
| | | <div slot="title">内容区域宽度</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-switch slot="actions" size="small" :defaultChecked="fixedHeader" @change="handleFixedHeader" /> |
| | | <a-list-item-meta> |
| | | <div slot="title">固定 Header</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-switch slot="actions" size="small" :disabled="!fixedHeader" :defaultChecked="autoHideHeader" @change="handleFixedHeaderHidden" /> |
| | | <a-list-item-meta> |
| | | <a-tooltip slot="title" placement="left"> |
| | | <template slot="title">固定 Header 时可配置</template> |
| | | <div :style="{ opacity: !fixedHeader ? '0.5' : '1' }">下滑时隐藏 Header</div> |
| | | </a-tooltip> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item > |
| | | <a-switch slot="actions" size="small" :disabled="(layoutMode === 'topmenu')" :defaultChecked="fixSiderbar" @change="handleFixSiderbar" /> |
| | | <a-list-item-meta> |
| | | <div slot="title" :style="{ textDecoration: layoutMode === 'topmenu' ? 'line-through' : 'unset' }">固定侧边菜单</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </div> |
| | | </div> |
| | | <a-divider /> |
| | | |
| | | <div :style="{ marginBottom: '24px' }"> |
| | | <h3 class="setting-drawer-index-title">其他设置</h3> |
| | | <div> |
| | | <a-list :split="false"> |
| | | <a-list-item> |
| | | <a-switch slot="actions" size="small" :defaultChecked="colorWeak" @change="onColorWeak" /> |
| | | <a-list-item-meta> |
| | | <div slot="title">色弱模式</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | <a-list-item> |
| | | <a-switch slot="actions" size="small" :defaultChecked="multiTab" @change="onMultiTab" /> |
| | | <a-list-item-meta> |
| | | <div slot="title">多页签模式</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </div> |
| | | </div> |
| | | <a-divider /> |
| | | <div :style="{ marginBottom: '24px' }"> |
| | | <a-button |
| | | @click="doCopy" |
| | | icon="copy" |
| | | block |
| | | >拷贝设置</a-button> |
| | | <a-alert type="warning" :style="{ marginTop: '24px' }"> |
| | | <span slot="message"> |
| | | 配置栏只在开发环境用于预览,生产环境不会展现,请手动修改配置文件。修改配置文件后,需要清空本地缓存和LocalStorage |
| | | <a href="https://github.com/sendya/ant-design-pro-vue/blob/master/src/config/defaultSettings.js" target="_blank">src/config/defaultSettings.js</a> |
| | | </span> |
| | | </a-alert> |
| | | </div> |
| | | </div> |
| | | <div class="setting-drawer-index-handle" @click="toggle" slot="handle"> |
| | | <a-icon type="setting" v-if="!visible"/> |
| | | <a-icon type="close" v-else/> |
| | | </div> |
| | | </a-drawer> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | /* import { DetailList } from '@/components' |
| | | import SettingItem from './SettingItem' */ |
| | | import config from '@/config/defaultSettings' |
| | | import { updateTheme, updateColorWeak, colorList } from './settingConfig' |
| | | import { mixin, mixinDevice } from '@/utils/mixin' |
| | | |
| | | export default { |
| | | components: { |
| | | // DetailList, |
| | | // SettingItem |
| | | }, |
| | | mixins: [mixin, mixinDevice], |
| | | data () { |
| | | return { |
| | | visible: false, |
| | | colorList |
| | | } |
| | | }, |
| | | watch: { |
| | | |
| | | }, |
| | | mounted () { |
| | | updateTheme(this.primaryColor) |
| | | if (this.colorWeak !== config.colorWeak) { |
| | | updateColorWeak(this.colorWeak) |
| | | } |
| | | }, |
| | | methods: { |
| | | showDrawer () { |
| | | this.visible = true |
| | | }, |
| | | onClose () { |
| | | this.visible = false |
| | | }, |
| | | toggle () { |
| | | this.visible = !this.visible |
| | | }, |
| | | onColorWeak (checked) { |
| | | this.$store.dispatch('ToggleWeak', checked) |
| | | updateColorWeak(checked) |
| | | }, |
| | | onMultiTab (checked) { |
| | | this.$store.dispatch('ToggleMultiTab', checked) |
| | | }, |
| | | handleMenuTheme (theme) { |
| | | this.$store.dispatch('ToggleTheme', theme) |
| | | }, |
| | | doCopy () { |
| | | // get current settings from mixin or this.$store.state.app, pay attention to the property name |
| | | const text = `export default { |
| | | primaryColor: '${this.primaryColor}', // primary color of ant design |
| | | navTheme: '${this.navTheme}', // theme for nav menu |
| | | layout: '${this.layoutMode}', // nav menu position: sidemenu or topmenu |
| | | contentWidth: '${this.contentWidth}', // layout of content: Fluid or Fixed, only works when layout is topmenu |
| | | fixedHeader: ${this.fixedHeader}, // sticky header |
| | | fixSiderbar: ${this.fixSiderbar}, // sticky siderbar |
| | | autoHideHeader: ${this.autoHideHeader}, // auto hide header |
| | | colorWeak: ${this.colorWeak}, |
| | | multiTab: ${this.multiTab}, |
| | | production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true', |
| | | // vue-ls options |
| | | storageOptions: { |
| | | namespace: 'pro__', |
| | | name: 'ls', |
| | | storage: 'local', |
| | | } |
| | | }` |
| | | this.$copyText(text).then(message => { |
| | | console.log('copy', message) |
| | | this.$message.success('复制完毕') |
| | | }).catch(err => { |
| | | console.log('copy.err', err) |
| | | this.$message.error('复制失败') |
| | | }) |
| | | }, |
| | | handleLayout (mode) { |
| | | this.$store.dispatch('ToggleLayoutMode', mode) |
| | | // 因为顶部菜单不能固定左侧菜单栏,所以强制关闭 |
| | | this.handleFixSiderbar(false) |
| | | }, |
| | | handleContentWidthChange (type) { |
| | | this.$store.dispatch('ToggleContentWidth', type) |
| | | }, |
| | | changeColor (color) { |
| | | if (this.primaryColor !== color) { |
| | | this.$store.dispatch('ToggleColor', color) |
| | | updateTheme(color) |
| | | } |
| | | }, |
| | | handleFixedHeader (fixed) { |
| | | this.$store.dispatch('ToggleFixedHeader', fixed) |
| | | }, |
| | | handleFixedHeaderHidden (autoHidden) { |
| | | this.$store.dispatch('ToggleFixedHeaderHidden', autoHidden) |
| | | }, |
| | | handleFixSiderbar (fixed) { |
| | | if (this.layoutMode === 'topmenu') { |
| | | this.$store.dispatch('ToggleFixSiderbar', false) |
| | | return |
| | | } |
| | | this.$store.dispatch('ToggleFixSiderbar', fixed) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .setting-drawer-index-content { |
| | | |
| | | .setting-drawer-index-blockChecbox { |
| | | display: flex; |
| | | |
| | | .setting-drawer-index-item { |
| | | margin-right: 16px; |
| | | position: relative; |
| | | border-radius: 4px; |
| | | cursor: pointer; |
| | | |
| | | img { |
| | | width: 48px; |
| | | } |
| | | |
| | | .setting-drawer-index-selectIcon { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | width: 100%; |
| | | padding-top: 15px; |
| | | padding-left: 24px; |
| | | height: 100%; |
| | | color: #1890ff; |
| | | font-size: 14px; |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | } |
| | | .setting-drawer-theme-color-colorBlock { |
| | | width: 20px; |
| | | height: 20px; |
| | | border-radius: 2px; |
| | | float: left; |
| | | cursor: pointer; |
| | | margin-right: 8px; |
| | | padding-left: 0px; |
| | | padding-right: 0px; |
| | | text-align: center; |
| | | color: #fff; |
| | | font-weight: 700; |
| | | |
| | | i { |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .setting-drawer-index-handle { |
| | | position: absolute; |
| | | top: 240px; |
| | | background: #1890ff; |
| | | width: 48px; |
| | | height: 48px; |
| | | right: 300px; |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | cursor: pointer; |
| | | pointer-events: auto; |
| | | z-index: 1001; |
| | | text-align: center; |
| | | font-size: 16px; |
| | | border-radius: 4px 0 0 4px; |
| | | |
| | | i { |
| | | color: rgb(255, 255, 255); |
| | | font-size: 20px; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="setting-drawer-index-item"> |
| | | <h3 class="setting-drawer-index-title">{{ title }}</h3> |
| | | <slot></slot> |
| | | <a-divider v-if="divider"/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'SettingItem', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | divider: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .setting-drawer-index-item { |
| | | margin-bottom: 24px; |
| | | |
| | | .setting-drawer-index-title { |
| | | font-size: 14px; |
| | | color: rgba(0, 0, 0, .85); |
| | | line-height: 22px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | } |
| | | </style> |
New file |
| | |
| | | import SettingDrawer from './SettingDrawer' |
| | | export default SettingDrawer |
New file |
| | |
| | | import { message } from 'ant-design-vue/es' |
| | | import themeColor from './themeColor.js' |
| | | |
| | | const colorList = [ |
| | | { |
| | | key: '薄暮', color: '#F5222D' |
| | | }, |
| | | { |
| | | key: '火山', color: '#FA541C' |
| | | }, |
| | | { |
| | | key: '日暮', color: '#FAAD14' |
| | | }, |
| | | { |
| | | key: '明青', color: '#13C2C2' |
| | | }, |
| | | { |
| | | key: '极光绿', color: '#52C41A' |
| | | }, |
| | | { |
| | | key: '拂晓蓝(默认)', color: '#1890FF' |
| | | }, |
| | | { |
| | | key: '极客蓝', color: '#2F54EB' |
| | | }, |
| | | { |
| | | key: '酱紫', color: '#722ED1' |
| | | } |
| | | ] |
| | | |
| | | const updateTheme = newPrimaryColor => { |
| | | const hideMessage = message.loading('正在切换主题!', 0) |
| | | themeColor.changeColor(newPrimaryColor).finally(t => { |
| | | setTimeout(() => { |
| | | hideMessage() |
| | | }, 10) |
| | | }) |
| | | } |
| | | |
| | | const updateColorWeak = colorWeak => { |
| | | // document.body.className = colorWeak ? 'colorWeak' : ''; |
| | | const app = document.body.querySelector('#app') |
| | | colorWeak ? app.classList.add('colorWeak') : app.classList.remove('colorWeak') |
| | | } |
| | | |
| | | export { updateTheme, colorList, updateColorWeak } |
New file |
| | |
| | | import client from 'webpack-theme-color-replacer/client' |
| | | import generate from '@ant-design/colors/lib/generate' |
| | | |
| | | export default { |
| | | getAntdSerials (color) { |
| | | // 淡化(即less的tint) |
| | | const lightens = new Array(9).fill().map((t, i) => { |
| | | return client.varyColor.lighten(color, i / 10) |
| | | }) |
| | | // colorPalette变换得到颜色值 |
| | | const colorPalettes = generate(color) |
| | | const rgb = client.varyColor.toNum3(color.replace('#', '')).join(',') |
| | | return lightens.concat(colorPalettes).concat(rgb) |
| | | }, |
| | | changeColor (newColor) { |
| | | var options = { |
| | | newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors` |
| | | changeUrl (cssUrl) { |
| | | return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path |
| | | } |
| | | } |
| | | return client.changer.changeColor(options, Promise) |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <div :class="[prefixCls, lastCls, blockCls, gridCls]"> |
| | | <div v-if="title" class="antd-pro-components-standard-form-row-index-label"> |
| | | <span>{{ title }}</span> |
| | | </div> |
| | | <div class="antd-pro-components-standard-form-row-index-content"> |
| | | <slot></slot> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | const classes = [ |
| | | 'antd-pro-components-standard-form-row-index-standardFormRowBlock', |
| | | 'antd-pro-components-standard-form-row-index-standardFormRowGrid', |
| | | 'antd-pro-components-standard-form-row-index-standardFormRowLast' |
| | | ] |
| | | export default { |
| | | name: 'StandardFormRow', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'antd-pro-components-standard-form-row-index-standardFormRow' |
| | | }, |
| | | title: { |
| | | type: String, |
| | | default: undefined |
| | | }, |
| | | last: { |
| | | type: Boolean |
| | | }, |
| | | block: { |
| | | type: Boolean |
| | | }, |
| | | grid: { |
| | | type: Boolean |
| | | } |
| | | }, |
| | | computed: { |
| | | lastCls () { |
| | | return this.last ? classes[2] : null |
| | | }, |
| | | blockCls () { |
| | | return this.block ? classes[0] : null |
| | | }, |
| | | gridCls () { |
| | | return this.grid ? classes[1] : null |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import '../index.less'; |
| | | |
| | | .antd-pro-components-standard-form-row-index-standardFormRow { |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | padding-bottom: 16px; |
| | | border-bottom: 1px dashed @border-color-split; |
| | | |
| | | /deep/ .ant-form-item { |
| | | margin-right: 24px; |
| | | } |
| | | /deep/ .ant-form-item-label label { |
| | | margin-right: 0; |
| | | color: @text-color; |
| | | } |
| | | /deep/ .ant-form-item-label, |
| | | .ant-form-item-control { |
| | | padding: 0; |
| | | line-height: 32px; |
| | | } |
| | | |
| | | .antd-pro-components-standard-form-row-index-label { |
| | | flex: 0 0 auto; |
| | | margin-right: 24px; |
| | | color: @heading-color; |
| | | font-size: @font-size-base; |
| | | text-align: right; |
| | | & > span { |
| | | display: inline-block; |
| | | height: 32px; |
| | | line-height: 32px; |
| | | &::after { |
| | | content: ':'; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .antd-pro-components-standard-form-row-index-content { |
| | | flex: 1 1 0; |
| | | /deep/ .ant-form-item:last-child { |
| | | margin-right: 0; |
| | | } |
| | | } |
| | | |
| | | &.antd-pro-components-standard-form-row-index-standardFormRowLast { |
| | | margin-bottom: 0; |
| | | padding-bottom: 0; |
| | | border: none; |
| | | } |
| | | |
| | | &.antd-pro-components-standard-form-row-index-standardFormRowBlock { |
| | | /deep/ .ant-form-item, |
| | | div.ant-form-item-control-wrapper { |
| | | display: block; |
| | | } |
| | | } |
| | | |
| | | &.antd-pro-components-standard-form-row-index-standardFormRowGrid { |
| | | /deep/ .ant-form-item, |
| | | div.ant-form-item-control-wrapper { |
| | | display: block; |
| | | } |
| | | /deep/ .ant-form-item-label { |
| | | float: left; |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | import StandardFormRow from './StandardFormRow' |
| | | |
| | | export default StandardFormRow |
New file |
| | |
| | | Table 重封装组件说明 |
| | | ==== |
| | | |
| | | |
| | | 封装说明 |
| | | ---- |
| | | |
| | | > 基础的使用方式与 API 与 [官方版(Table)](https://vuecomponent.github.io/ant-design-vue/components/table-cn/) 本一致,在其基础上,封装了加载数据的方法。 |
| | | > |
| | | > 你无需在你是用表格的页面进行分页逻辑处理,仅需向 Table 组件传递绑定 `:data="Promise"` 对象即可 |
| | | |
| | | 该 `table` 由 [@Saraka](https://github.com/saraka-tsukai) 完成封装 |
| | | |
| | | 由 `小诺` 完成二次改进使用 |
| | | |
| | | 例子1 |
| | | ---- |
| | | (基础使用) |
| | | |
| | | ```vue |
| | | |
| | | <template> |
| | | <s-table |
| | | ref="table" |
| | | :rowKey="(record) => record.data.id" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }" |
| | | > |
| | | </s-table> |
| | | </template> |
| | | |
| | | <script> |
| | | import STable from '@/components' |
| | | |
| | | export default { |
| | | components: { |
| | | STable |
| | | }, |
| | | data() { |
| | | return { |
| | | columns: [ |
| | | { |
| | | title: '规则编号', |
| | | dataIndex: 'no' |
| | | }, |
| | | { |
| | | title: '描述', |
| | | dataIndex: 'description' |
| | | }, |
| | | { |
| | | title: '服务调用次数', |
| | | dataIndex: 'callNo', |
| | | sorter: true, |
| | | needTotal: true, |
| | | customRender: (text) => text + ' 次' |
| | | }, |
| | | { |
| | | title: '状态', |
| | | dataIndex: 'status', |
| | | needTotal: true |
| | | }, |
| | | { |
| | | title: '更新时间', |
| | | dataIndex: 'updatedAt', |
| | | sorter: true |
| | | } |
| | | ], |
| | | // 查询条件参数 |
| | | queryParam: {}, |
| | | // 加载数据方法 必须为 Promise 对象 |
| | | loadData: parameter => { |
| | | return this.$http.get('/service', { |
| | | params: Object.assign(parameter, this.queryParam) |
| | | }).then(res => { |
| | | return res.result |
| | | }) |
| | | }, |
| | | selectedRowKeys: [], |
| | | selectedRows: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | onSelectChange (selectedRowKeys, selectedRows) { |
| | | this.selectedRowKeys = selectedRowKeys |
| | | this.selectedRows = selectedRows |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | ``` |
| | | |
| | | |
| | | |
| | | 例子2 |
| | | ---- |
| | | |
| | | (简单的表格,最后一列是各种操作) |
| | | |
| | | ```vue |
| | | <template> |
| | | <s-table |
| | | ref="table" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | > |
| | | <span slot="action" slot-scope="text, record"> |
| | | <a>编辑</a> |
| | | <a-divider type="vertical"/> |
| | | <a-dropdown> |
| | | <a class="ant-dropdown-link"> |
| | | 更多 <a-icon type="down"/> |
| | | </a> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item> |
| | | <a href="javascript:;">1st menu item</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">2nd menu item</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">3rd menu item</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </span> |
| | | </s-table> |
| | | </template> |
| | | |
| | | <script> |
| | | import STable from '@/components/table/' |
| | | |
| | | export default { |
| | | components: { |
| | | STable |
| | | }, |
| | | data() { |
| | | return { |
| | | columns: [ |
| | | { |
| | | title: '规则编号', |
| | | dataIndex: 'no' |
| | | }, |
| | | { |
| | | title: '描述', |
| | | dataIndex: 'description' |
| | | }, |
| | | { |
| | | title: '服务调用次数', |
| | | dataIndex: 'callNo', |
| | | }, |
| | | { |
| | | title: '状态', |
| | | dataIndex: 'status', |
| | | }, |
| | | { |
| | | title: '更新时间', |
| | | dataIndex: 'updatedAt', |
| | | }, |
| | | { |
| | | table: '操作', |
| | | dataIndex: 'action', |
| | | scopedSlots: {customRender: 'action'}, |
| | | } |
| | | ], |
| | | // 查询条件参数 |
| | | queryParam: {}, |
| | | // 加载数据方法 必须为 Promise 对象 |
| | | loadData: parameter => { |
| | | return this.$http.get('/service', { |
| | | params: Object.assign(parameter, this.queryParam) |
| | | }).then(res => { |
| | | return res.result |
| | | }) |
| | | }, |
| | | } |
| | | }, |
| | | methods: { |
| | | edit(row) { |
| | | // axios 发送数据到后端 修改数据成功后 |
| | | // 调用 refresh() 重新加载列表数据 |
| | | // 这里 setTimeout 模拟发起请求的网络延迟.. |
| | | setTimeout(() => { |
| | | this.$refs.table.refresh() // refresh() 不传参默认值 false 不刷新到分页第一页 |
| | | }, 1500) |
| | | |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | ``` |
| | | |
| | | |
| | | |
| | | 内置方法 |
| | | ---- |
| | | |
| | | 通过 `this.$refs.table` 调用 |
| | | |
| | | `this.$refs.table.refresh(true)` 刷新列表 (用户新增/修改数据后,重载列表数据) |
| | | |
| | | > 注意:要调用 `refresh(bool)` 需要给表格组件设定 `ref` 值 |
| | | > |
| | | > `refresh()` 方法可以传一个 `bool` 值,当有传值 或值为 `true` 时,则刷新时会强制刷新到第一页(常用户页面 搜索 按钮进行搜索时,结果从第一页开始分页) |
| | | |
| | | |
| | | 内置属性 |
| | | ---- |
| | | > 除去 `a-table` 自带属性外,还而外提供了一些额外属性属性 |
| | | |
| | | |
| | | | 属性 | 说明 | 类型 | 默认值 | |
| | | | -------------- | ----------------------------------------------- | ----------------- | ------ | |
| | | | alert | 设置是否显示表格信息栏 | [object, boolean] | null | |
| | | | showPagination | 显示分页选择器,可传 'auto' \| boolean | [string, boolean] | 'auto' | |
| | | | data | 加载数据方法 必须为 `Promise` 对象 **必须绑定** | Promise | - | |
| | | |
| | | |
| | | `alert` 属性对象: |
| | | |
| | | ```javascript |
| | | alert: { |
| | | show: Boolean, |
| | | clear: [Function, Boolean] |
| | | } |
| | | ``` |
| | | |
| | | 注意事项 |
| | | ---- |
| | | |
| | | > 你可能需要为了与后端提供的接口返回结果一致而去修改以下代码: |
| | | > (需要注意的是,这里的修改是全局性的,意味着整个项目所有使用该 table 组件都需要遵守这个返回结果定义的字段。) |
| | | > |
| | | > 文档中的结构有可能由于组件 bug 进行修正而改动。实际修改请以当时最新版本为准 |
| | | |
| | | 修改 `@/components/table/index.js` 第 156 行起 |
| | | |
| | | |
| | | |
| | | ```javascript |
| | | result.then(r => { |
| | | this.localPagination = this.showPagination && Object.assign({}, this.localPagination, { |
| | | current: r.pageNo, // 返回结果中的当前分页数 |
| | | total: r.totalCount, // 返回结果中的总记录数 |
| | | showSizeChanger: this.showSizeChanger, |
| | | pageSize: (pagination && pagination.pageSize) || |
| | | this.localPagination.pageSize |
| | | }) || false |
| | | // 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页 |
| | | if (r.data.length === 0 && this.showPagination && this.localPagination.current > 1) { |
| | | this.localPagination.current-- |
| | | this.loadData() |
| | | return |
| | | } |
| | | |
| | | // 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小 |
| | | // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能 |
| | | try { |
| | | if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.pageNo * this.localPagination.pageSize))) { |
| | | this.localPagination.hideOnSinglePage = true |
| | | } |
| | | } catch (e) { |
| | | this.localPagination = false |
| | | } |
| | | console.log('loadData -> this.localPagination', this.localPagination) |
| | | this.localDataSource = r.data // 返回结果中的数组数据 |
| | | this.localLoading = false |
| | | }) |
| | | ``` |
| | | 返回 JSON 例子: |
| | | ```json |
| | | { |
| | | "message": "", |
| | | "result": { |
| | | "data": [{ |
| | | id: 1, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', |
| | | title: 'Alipay', |
| | | description: '那是一种内在的东西, 他们到达不了,也无法触及的', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 2, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', |
| | | title: 'Angular', |
| | | description: '希望是一个好东西,也许是最好的,好东西是不会消亡的', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 3, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png', |
| | | title: 'Ant Design', |
| | | description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 4, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png', |
| | | title: 'Snowy', |
| | | description: '那时候我只会想自己想要什么,从不想自己拥有什么', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 5, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', |
| | | title: 'Bootstrap', |
| | | description: '凛冬将至', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 6, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', |
| | | title: 'Vue', |
| | | description: '生命就像一盒巧克力,结果往往出人意料', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | } |
| | | ], |
| | | "pageSize": 10, |
| | | "pageNo": 0, |
| | | "totalPage": 6, |
| | | "totalCount": 57 |
| | | }, |
| | | "status": 200, |
| | | "timestamp": 1534955098193 |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | 更新时间 |
| | | ---- |
| | | |
| | | 该文档最后更新于: 2019-06-23 PM 17:19 |
New file |
| | |
| | | <template> |
| | | <div slot="overlay" class="ant-dropdown-menu s-tool-column ant-dropdown-content"> |
| | | <div class="s-tool-column-header s-tool-column-item"> |
| | | <a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange"> |
| | | 列展示 |
| | | </a-checkbox> |
| | | <a @click="reset">重置</a> |
| | | </div> |
| | | <a-divider /> |
| | | <div class="ant-checkbox-group"> |
| | | <div> |
| | | <draggable v-model="columnsSetting" animation="300" @end="emitColumnChange"> |
| | | <div class="s-tool-column-item" v-for="item in columnsSetting" :key="item.title"> |
| | | <div class="s-tool-column-handle" > |
| | | <a-icon type="more"/> |
| | | <a-icon type="more"/> |
| | | </div> |
| | | <a-checkbox v-model="item.checked" @change="onChange">{{ item.title }}</a-checkbox> |
| | | </div> |
| | | </draggable> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import draggable from 'vuedraggable' |
| | | |
| | | export default { |
| | | props: { |
| | | columns: { |
| | | type: Array, |
| | | default: () => ([]) |
| | | } |
| | | }, |
| | | components: { |
| | | draggable |
| | | }, |
| | | data() { |
| | | return { |
| | | indeterminate: false, |
| | | checkAll: true, |
| | | columnsSetting: [], |
| | | originColumns: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | reset() { |
| | | this.columnsSetting = JSON.parse(JSON.stringify(this.originColumns)) |
| | | this.indeterminate = false |
| | | this.checkAll = true |
| | | this.emitColumnChange() |
| | | }, |
| | | onChange() { |
| | | const checkedList = this.columnsSetting.filter(value => value.checked) |
| | | this.indeterminate = !!checkedList.length && checkedList.length < this.columnsSetting.length |
| | | this.checkAll = checkedList.length === this.columnsSetting.length |
| | | this.emitColumnChange() |
| | | }, |
| | | onCheckAllChange(e) { |
| | | const val = e.target.checked |
| | | Object.assign(this, { |
| | | indeterminate: false, |
| | | checkAll: val, |
| | | columnsSetting: this.columns.map(value => ({ ...value, checked: val })) |
| | | }) |
| | | this.emitColumnChange() |
| | | }, |
| | | emitColumnChange() { |
| | | this.$emit('columnChange', this.columnsSetting) |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.columnsSetting = this.columns.map(value => ({ ...value, checked: true })) |
| | | this.originColumns = JSON.parse(JSON.stringify(this.columnsSetting)) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | import T from 'ant-design-vue/es/table/Table' |
| | | import get from 'lodash.get' |
| | | import draggable from 'vuedraggable' |
| | | import columnSetting from './columnSetting' |
| | | import './index.less' |
| | | |
| | | export default { |
| | | components: { |
| | | draggable, columnSetting |
| | | }, |
| | | data () { |
| | | return { |
| | | needTotalList: [], |
| | | |
| | | selectedRows: [], |
| | | selectedRowKeys: [], |
| | | |
| | | localLoading: false, |
| | | localDataSource: [], |
| | | localPagination: Object.assign({}, this.pagination), |
| | | isFullscreen: false, |
| | | customSize: this.size, |
| | | columnsSetting: [] |
| | | } |
| | | }, |
| | | props: Object.assign({}, T.props, { |
| | | rowKey: { |
| | | type: [String, Function], |
| | | default: 'key' |
| | | }, |
| | | data: { |
| | | type: Function, |
| | | required: true |
| | | }, |
| | | pageNum: { |
| | | type: Number, |
| | | default: 1 |
| | | }, |
| | | pageSize: { |
| | | type: Number, |
| | | default: 10 |
| | | }, |
| | | showSizeChanger: { |
| | | type: Boolean, |
| | | default: true |
| | | }, |
| | | size: { |
| | | type: String, |
| | | default: 'middle' |
| | | }, |
| | | /** |
| | | * alert: { |
| | | * show: true, |
| | | * clear: Function |
| | | * } |
| | | */ |
| | | alert: { |
| | | type: [Object, Boolean], |
| | | default: null |
| | | }, |
| | | rowSelection: { |
| | | type: Object, |
| | | default: null |
| | | }, |
| | | /** @Deprecated */ |
| | | showAlertInfo: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | showPagination: { |
| | | type: String | Boolean, |
| | | default: 'auto' |
| | | }, |
| | | /** |
| | | * enable page URI mode |
| | | * |
| | | * e.g: |
| | | * /users/1 |
| | | * /users/2 |
| | | * /users/3?queryParam=test |
| | | * ... |
| | | */ |
| | | pageURI: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | extraTool: { |
| | | type: Array, |
| | | default: () => ([]) |
| | | } |
| | | }), |
| | | watch: { |
| | | 'localPagination.current' (val) { |
| | | this.pageURI && this.$router.push({ |
| | | ...this.$route, |
| | | name: this.$route.name, |
| | | params: Object.assign({}, this.$route.params, { |
| | | pageNo: val |
| | | }) |
| | | }) |
| | | }, |
| | | pageNum (val) { |
| | | Object.assign(this.localPagination, { |
| | | current: val |
| | | }) |
| | | }, |
| | | pageSize (val) { |
| | | Object.assign(this.localPagination, { |
| | | pageSize: val |
| | | }) |
| | | }, |
| | | showSizeChanger (val) { |
| | | Object.assign(this.localPagination, { |
| | | showSizeChanger: val |
| | | }) |
| | | }, |
| | | columns(v) { |
| | | this.columnsSetting = v |
| | | } |
| | | }, |
| | | created () { |
| | | const { pageNo } = this.$route.params |
| | | const localPageNum = this.pageURI && (pageNo && parseInt(pageNo)) || this.pageNum |
| | | this.localPagination = ['auto', true].includes(this.showPagination) && Object.assign({}, this.localPagination, { |
| | | current: localPageNum, |
| | | pageSize: this.pageSize, |
| | | showSizeChanger: this.showSizeChanger, |
| | | showTotal: (total, range) => { |
| | | return range[0] + '-' + range[1] + '共' + total + '条' |
| | | } |
| | | }) || false |
| | | this.needTotalList = this.initTotalList(this.columns) |
| | | this.loadData() |
| | | this.columnsSetting = this.columns |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 表格重新加载方法 |
| | | * 如果参数为 true, 则强制刷新到第一页 |
| | | * @param Boolean bool |
| | | */ |
| | | refresh (bool = false) { |
| | | bool && (this.localPagination = Object.assign({}, { |
| | | current: 1, pageSize: this.pageSize |
| | | })) |
| | | this.loadData() |
| | | }, |
| | | /** |
| | | * 加载数据方法 |
| | | * @param {Object} pagination 分页选项器 |
| | | * @param {Object} filters 过滤条件 |
| | | * @param {Object} sorter 排序条件 |
| | | */ |
| | | loadData (pagination, filters, sorter) { |
| | | this.localLoading = true |
| | | const parameter = Object.assign({ |
| | | pageNo: (pagination && pagination.current) || |
| | | this.showPagination && this.localPagination.current || this.pageNum, |
| | | pageSize: (pagination && pagination.pageSize) || |
| | | this.showPagination && this.localPagination.pageSize || this.pageSize |
| | | }, |
| | | (sorter && sorter.field && { |
| | | sortField: sorter.field |
| | | }) || {}, |
| | | (sorter && sorter.order && { |
| | | sortOrder: sorter.order |
| | | }) || {}, { |
| | | ...filters |
| | | } |
| | | ) |
| | | const result = this.data(parameter) |
| | | // 对接自己的通用数据接口需要修改下方代码中的 r.pageNo, r.totalCount, r.data |
| | | // eslint-disable-next-line |
| | | if ((typeof result === 'object' || typeof result === 'function') && typeof result.then === 'function') { |
| | | result.then(r => { |
| | | if (r == null) { |
| | | this.localLoading = false |
| | | return |
| | | } |
| | | this.localPagination = this.showPagination && Object.assign({}, this.localPagination, { |
| | | current: r.pageNo, // pageNo, // 返回结果中的当前分页数 |
| | | total: r.totalRows, // totalCount, // 返回结果中的总记录数 |
| | | showSizeChanger: this.showSizeChanger, |
| | | pageSize: (pagination && pagination.pageSize) || |
| | | this.localPagination.pageSize |
| | | }) || false |
| | | // 后端数据rows为null保存修复 |
| | | if (r.rows == null) { |
| | | r.rows = [] |
| | | } |
| | | // 为防止删除数据后导致页面当前页面数据长度为 0 ,自动翻页到上一页 |
| | | if (r.rows.length === 0 && this.showPagination && this.localPagination.current > 1) { |
| | | this.localPagination.current-- |
| | | this.loadData() |
| | | return |
| | | } |
| | | |
| | | // 这里用于判断接口是否有返回 r.totalCount 且 this.showPagination = true 且 pageNo 和 pageSize 存在 且 totalCount 小于等于 pageNo * pageSize 的大小 |
| | | // 当情况满足时,表示数据不满足分页大小,关闭 table 分页功能 |
| | | try { |
| | | if ((['auto', true].includes(this.showPagination) && r.totalCount <= (r.totalPage * this.localPagination.pageSize))) { |
| | | this.localPagination.hideOnSinglePage = true |
| | | } |
| | | } catch (e) { |
| | | this.localPagination = false |
| | | } |
| | | // 返回结果中的数组数据 |
| | | if (this.showPagination === false) { |
| | | // 因为按住小诺的套路,不分页的直接是在data中,我们在界面中直接就是返回了data |
| | | this.localDataSource = r |
| | | } else { |
| | | this.localDataSource = r.rows |
| | | } |
| | | this.localLoading = false |
| | | }) |
| | | } |
| | | }, |
| | | initTotalList (columns) { |
| | | const totalList = [] |
| | | columns && columns instanceof Array && columns.forEach(column => { |
| | | if (column.needTotal) { |
| | | totalList.push({ |
| | | ...column, |
| | | total: 0 |
| | | }) |
| | | } |
| | | }) |
| | | return totalList |
| | | }, |
| | | /** |
| | | * 用于更新已选中的列表数据 total 统计 |
| | | * @param selectedRowKeys |
| | | * @param selectedRows |
| | | */ |
| | | updateSelect (selectedRowKeys, selectedRows) { |
| | | this.selectedRows = selectedRows |
| | | this.selectedRowKeys = selectedRowKeys |
| | | const list = this.needTotalList |
| | | this.needTotalList = list.map(item => { |
| | | return { |
| | | ...item, |
| | | total: selectedRows.reduce((sum, val) => { |
| | | const total = sum + parseInt(get(val, item.dataIndex)) |
| | | return isNaN(total) ? 0 : total |
| | | }, 0) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 清空 table 已选中项 |
| | | */ |
| | | clearSelected () { |
| | | if (this.rowSelection) { |
| | | this.rowSelection.onChange([], []) |
| | | this.updateSelect([], []) |
| | | } |
| | | }, |
| | | /** |
| | | * 刷新并清空已选 |
| | | */ |
| | | clearRefreshSelected (bool = false) { |
| | | this.refresh(bool) |
| | | this.clearSelected() |
| | | }, |
| | | /** |
| | | * 处理交给 table 使用者去处理 clear 事件时,内部选中统计同时调用 |
| | | * @param callback |
| | | * @returns {*} |
| | | */ |
| | | renderClear (callback) { |
| | | if (this.selectedRowKeys.length <= 0) return null |
| | | return ( |
| | | <a style="margin-left: 24px" onClick={() => { |
| | | callback() |
| | | this.clearSelected() |
| | | }}>清空</a> |
| | | ) |
| | | }, |
| | | renderAlert () { |
| | | // 绘制统计列数据 |
| | | // eslint-disable-next-line no-unused-vars |
| | | const needTotalItems = this.needTotalList.map((item) => { |
| | | return (<span style="margin-right: 12px"> |
| | | {item.title}总计 <a style="font-weight: 600">{!item.customRender ? item.total : item.customRender(item.total)}</a> |
| | | </span>) |
| | | }) |
| | | |
| | | // 绘制 清空 按钮 |
| | | // eslint-disable-next-line no-unused-vars |
| | | const clearItem = (typeof this.alert.clear === 'boolean' && this.alert.clear) ? ( |
| | | this.renderClear(this.clearSelected) |
| | | ) : (this.alert !== null && typeof this.alert.clear === 'function') ? ( |
| | | this.renderClear(this.alert.clear) |
| | | ) : null |
| | | |
| | | // 绘制 alert 组件 |
| | | // 统一先去除alert组件 |
| | | return ( |
| | | <a-alert showIcon={true} style="margin-bottom: 16px"> |
| | | <template slot="message"> |
| | | <span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span> |
| | | {needTotalItems} |
| | | {clearItem} |
| | | </template> |
| | | </a-alert> |
| | | ) |
| | | }, |
| | | columnChange(val) { |
| | | this.columnsSetting = val |
| | | }, |
| | | renderHeader () { |
| | | let tools = [ |
| | | { |
| | | icon: 'reload', |
| | | title: '刷新', |
| | | onClick: () => { |
| | | this.refresh() |
| | | } |
| | | }, |
| | | { |
| | | icon: 'column-height', |
| | | title: '密度', |
| | | isDropdown: true, |
| | | menu: () => { |
| | | const onClick = ({ key }) => { |
| | | this.customSize = key |
| | | } |
| | | return ( |
| | | <a-menu slot="overlay" onClick={onClick} selectable defaultSelectedKeys={[this.customSize]}> |
| | | <a-menu-item key="default">默认</a-menu-item> |
| | | <a-menu-item key="middle">中等</a-menu-item> |
| | | <a-menu-item key="small">紧凑</a-menu-item> |
| | | </a-menu> |
| | | ) |
| | | }, |
| | | onClick: () => { |
| | | } |
| | | }, |
| | | { |
| | | icon: 'setting', |
| | | title: '列设置', |
| | | isDropdown: true, |
| | | menu: () => { |
| | | return <columnSetting slot="overlay" columns={this.columns} onColumnChange={this.columnChange} /> |
| | | }, |
| | | onClick: () => { |
| | | } |
| | | } |
| | | ] |
| | | if (this.extraTool.length) { |
| | | tools = tools.concat(this.extraTool) |
| | | } |
| | | |
| | | return ( |
| | | <div class="s-table-tool"> |
| | | <div class="s-table-tool-left"> |
| | | {this.$scopedSlots.operator && this.$scopedSlots.operator()} |
| | | </div> |
| | | <div class="s-table-tool-right"> |
| | | { |
| | | tools.map(tool => { |
| | | if (tool.isDropdown) { |
| | | return ( |
| | | <a-dropdown trigger={['click']}> |
| | | <a-tooltip title={tool.title} class="s-tool-item" onClick={tool.onClick}> |
| | | <a-icon type={tool.icon}/> |
| | | </a-tooltip> |
| | | { tool.menu() } |
| | | </a-dropdown> |
| | | ) |
| | | } |
| | | return ( |
| | | <a-tooltip title={tool.title} class="s-tool-item" onClick={tool.onClick}> |
| | | <a-icon type={tool.icon} /> |
| | | </a-tooltip> |
| | | ) |
| | | }) |
| | | } |
| | | </div> |
| | | </div> |
| | | ) |
| | | /* |
| | | return ( |
| | | <a-alert showIcon={true} style="margin-bottom: 16px"> |
| | | <template slot="message"> |
| | | <span style="margin-right: 12px">已选择: <a style="font-weight: 600">{this.selectedRows.length}</a></span> |
| | | {needTotalItems} |
| | | {clearItem} |
| | | </template> |
| | | </a-alert> |
| | | ) |
| | | */ |
| | | } |
| | | }, |
| | | |
| | | render () { |
| | | let props = {} |
| | | const localKeys = Object.keys(this.$data) |
| | | const showAlert = (typeof this.alert === 'object' && this.alert !== null && this.alert.show) && typeof this.rowSelection.selectedRowKeys !== 'undefined' || this.alert |
| | | |
| | | Object.keys(T.props).forEach(k => { |
| | | const localKey = `local${k.substring(0, 1).toUpperCase()}${k.substring(1)}` |
| | | if (localKeys.includes(localKey)) { |
| | | props[k] = this[localKey] |
| | | return props[k] |
| | | } |
| | | if (k === 'rowSelection') { |
| | | if (showAlert && this.rowSelection) { |
| | | // 如果需要使用alert,则重新绑定 rowSelection 事件 |
| | | props[k] = { |
| | | ...this.rowSelection, |
| | | selectedRows: this.selectedRows, |
| | | selectedRowKeys: this.selectedRowKeys, |
| | | onChange: (selectedRowKeys, selectedRows) => { |
| | | this.updateSelect(selectedRowKeys, selectedRows) |
| | | typeof this[k].onChange !== 'undefined' && this[k].onChange(selectedRowKeys, selectedRows) |
| | | } |
| | | } |
| | | return props[k] |
| | | } else if (!this.rowSelection) { |
| | | // 如果没打算开启 rowSelection 则清空默认的选择项 |
| | | props[k] = null |
| | | return props[k] |
| | | } |
| | | } |
| | | this[k] && (props[k] = this[k]) |
| | | // 此处配置表格大小与要显示的列 |
| | | props = { |
| | | ...props, |
| | | size: this.customSize, |
| | | columns: this.columnsSetting.filter(value => value.checked === undefined || value.checked) |
| | | } |
| | | return props[k] |
| | | }) |
| | | const table = ( |
| | | <a-table {...{ props, scopedSlots: { ...this.$scopedSlots } }} onChange={this.loadData} onExpand={ (expanded, record) => { this.$emit('expand', expanded, record) } }> |
| | | { Object.keys(this.$slots).map(name => (<template slot={name}>{this.$slots[name]}</template>)) } |
| | | </a-table> |
| | | ) |
| | | |
| | | return ( |
| | | <div class="table-wrapper"> |
| | | { this.renderHeader() } |
| | | { showAlert ? this.renderAlert() : null } |
| | | { table } |
| | | </div> |
| | | ) |
| | | } |
| | | } |
New file |
| | |
| | | .table-wrapper{ |
| | | background: #fff; |
| | | } |
| | | .s-table-tool{ |
| | | display: flex; |
| | | margin-bottom: 16px; |
| | | .s-table-tool-left{ |
| | | flex: 1; |
| | | } |
| | | .s-table-tool-right{ |
| | | display: inline-flex; |
| | | align-items: center; |
| | | .s-tool-item{ |
| | | font-size: 16px; |
| | | margin-left: 16px; |
| | | cursor: pointer; |
| | | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .s-tool-column-item{ |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 4px 16px 4px 4px; |
| | | .ant-checkbox-wrapper{ |
| | | flex: 1; |
| | | } |
| | | .s-tool-column-handle{ |
| | | opacity: .8; |
| | | cursor: move; |
| | | .anticon-more{ |
| | | font-size: 12px; |
| | | margin-top: 2px; |
| | | & + .anticon-more{ |
| | | margin: 2px 4px 0 -8px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .s-tool-column-header{ |
| | | padding: 5px 16px 10px 24px; |
| | | min-width: 180px; |
| | | } |
| | | .s-tool-column{ |
| | | .ant-divider{ |
| | | margin: 0; |
| | | } |
| | | .ant-checkbox-group{ |
| | | padding: 4px 0; |
| | | display: block; |
| | | } |
| | | } |
| | | |
New file |
| | |
| | | import { Tag } from 'ant-design-vue' |
| | | const { CheckableTag } = Tag |
| | | |
| | | export default { |
| | | name: 'TagSelectOption', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-tag-select-option' |
| | | }, |
| | | value: { |
| | | type: [String, Number, Object], |
| | | default: '' |
| | | }, |
| | | checked: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | localChecked: this.checked || false |
| | | } |
| | | }, |
| | | watch: { |
| | | 'checked' (val) { |
| | | this.localChecked = val |
| | | }, |
| | | '$parent.items': { |
| | | handler: function (val) { |
| | | this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value]) |
| | | }, |
| | | deep: true |
| | | } |
| | | }, |
| | | render () { |
| | | const { $slots, value } = this |
| | | const onChange = (checked) => { |
| | | this.$emit('change', { value, checked }) |
| | | } |
| | | return (<CheckableTag key={value} vModel={this.localChecked} onChange={onChange}> |
| | | {$slots.default} |
| | | </CheckableTag>) |
| | | } |
| | | } |
New file |
| | |
| | | import PropTypes from 'ant-design-vue/es/_util/vue-types' |
| | | import Option from './TagSelectOption.jsx' |
| | | import { filterEmpty } from '@/components/_util/util' |
| | | |
| | | export default { |
| | | Option, |
| | | name: 'TagSelect', |
| | | model: { |
| | | prop: 'checked', |
| | | event: 'change' |
| | | }, |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-tag-select' |
| | | }, |
| | | defaultValue: { |
| | | type: PropTypes.array, |
| | | default: null |
| | | }, |
| | | value: { |
| | | type: PropTypes.array, |
| | | default: null |
| | | }, |
| | | expandable: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | hideCheckAll: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | expand: false, |
| | | localCheckAll: false, |
| | | items: this.getItemsKey(filterEmpty(this.$slots.default)), |
| | | val: this.value || this.defaultValue || [] |
| | | } |
| | | }, |
| | | methods: { |
| | | onChange (checked) { |
| | | const key = Object.keys(this.items).filter(key => key === checked.value) |
| | | this.items[key] = checked.checked |
| | | const bool = Object.values(this.items).lastIndexOf(false) |
| | | if (bool === -1) { |
| | | this.localCheckAll = true |
| | | } else { |
| | | this.localCheckAll = false |
| | | } |
| | | }, |
| | | onCheckAll (checked) { |
| | | Object.keys(this.items).forEach(v => { |
| | | this.items[v] = checked.checked |
| | | }) |
| | | this.localCheckAll = checked.checked |
| | | }, |
| | | getItemsKey (items) { |
| | | const totalItem = {} |
| | | items.forEach(item => { |
| | | totalItem[item.componentOptions.propsData && item.componentOptions.propsData.value] = false |
| | | }) |
| | | return totalItem |
| | | }, |
| | | // CheckAll Button |
| | | renderCheckAll () { |
| | | const props = { |
| | | on: { |
| | | change: (checked) => { |
| | | this.onCheckAll(checked) |
| | | checked.value = 'total' |
| | | this.$emit('change', checked) |
| | | } |
| | | } |
| | | } |
| | | const checkAllElement = <Option key={'total'} checked={this.localCheckAll} {...props}>All</Option> |
| | | return !this.hideCheckAll && checkAllElement || null |
| | | }, |
| | | // expandable |
| | | renderExpandable () { |
| | | |
| | | }, |
| | | // render option |
| | | renderTags (items) { |
| | | const listeners = { |
| | | change: (checked) => { |
| | | this.onChange(checked) |
| | | this.$emit('change', checked) |
| | | } |
| | | } |
| | | |
| | | return items.map(vnode => { |
| | | const options = vnode.componentOptions |
| | | options.listeners = listeners |
| | | return vnode |
| | | }) |
| | | } |
| | | }, |
| | | render () { |
| | | const { $props: { prefixCls } } = this |
| | | const classString = { |
| | | [`${prefixCls}`]: true |
| | | } |
| | | const tagItems = filterEmpty(this.$slots.default) |
| | | return ( |
| | | <div class={classString}> |
| | | {this.renderCheckAll()} |
| | | {this.renderTags(tagItems)} |
| | | </div> |
| | | ) |
| | | } |
| | | } |
New file |
| | |
| | | import './style.less' |
| | | import { getStrFullLength, cutStrByFullLength } from '../_util/util' |
| | | import Input from 'ant-design-vue/es/input' |
| | | const TextArea = Input.TextArea |
| | | |
| | | export default { |
| | | name: 'LimitTextArea', |
| | | model: { |
| | | prop: 'value', |
| | | event: 'change' |
| | | }, |
| | | props: Object.assign({}, TextArea.props, { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-textarea-limit' |
| | | }, |
| | | // eslint-disable-next-line |
| | | value: { |
| | | type: String |
| | | }, |
| | | limit: { |
| | | type: Number, |
| | | default: 200 |
| | | } |
| | | }), |
| | | data () { |
| | | return { |
| | | currentLimit: 0 |
| | | } |
| | | }, |
| | | watch: { |
| | | value (val) { |
| | | this.calcLimitNum(val) |
| | | } |
| | | }, |
| | | created () { |
| | | this.calcLimitNum(this.value) |
| | | }, |
| | | methods: { |
| | | handleChange (e) { |
| | | const value = e.target.value |
| | | const len = getStrFullLength(value) |
| | | if (len <= this.limit) { |
| | | this.currentLimit = len |
| | | this.$emit('change', value) |
| | | return |
| | | } else { |
| | | const str = cutStrByFullLength(value, this.limit) |
| | | this.currentLimit = getStrFullLength(str) |
| | | this.$emit('change', str) |
| | | } |
| | | console.error('limit out! currentLimit:', this.currentLimit) |
| | | }, |
| | | calcLimitNum (val) { |
| | | const len = getStrFullLength(val) |
| | | this.currentLimit = len |
| | | } |
| | | }, |
| | | render () { |
| | | const { prefixCls, ...props } = this.$props |
| | | return ( |
| | | <div class={this.prefixCls}> |
| | | <TextArea {...{ props }} value={this.value} onChange={this.handleChange}> |
| | | </TextArea> |
| | | <span class="limit">{this.currentLimit}/{this.limit}</span> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
New file |
| | |
| | | .ant-textarea-limit { |
| | | position: relative; |
| | | |
| | | .limit { |
| | | position: absolute; |
| | | color: #909399; |
| | | background: #fff; |
| | | font-size: 12px; |
| | | bottom: 5px; |
| | | right: 10px; |
| | | } |
| | | } |
New file |
| | |
| | | import { Menu, Icon, Input } from 'ant-design-vue' |
| | | |
| | | const { Item, ItemGroup, SubMenu } = Menu |
| | | const { Search } = Input |
| | | |
| | | export default { |
| | | name: 'Tree', |
| | | props: { |
| | | dataSource: { |
| | | type: Array, |
| | | required: true |
| | | }, |
| | | openKeys: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | search: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | created () { |
| | | this.localOpenKeys = this.openKeys.slice(0) |
| | | }, |
| | | data () { |
| | | return { |
| | | localOpenKeys: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | handlePlus (item) { |
| | | this.$emit('add', item) |
| | | }, |
| | | handleTitleClick (...args) { |
| | | this.$emit('titleClick', { args }) |
| | | }, |
| | | |
| | | renderSearch () { |
| | | return ( |
| | | <Search |
| | | placeholder="input search text" |
| | | style="width: 100%; margin-bottom: 1rem" |
| | | /> |
| | | ) |
| | | }, |
| | | renderIcon (icon) { |
| | | return icon && (<Icon type={icon} />) || null |
| | | }, |
| | | renderMenuItem (item) { |
| | | return ( |
| | | <Item key={item.key}> |
| | | { this.renderIcon(item.icon) } |
| | | { item.title } |
| | | <a class="btn" style="width: 20px;z-index:1300" {...{ on: { click: () => this.handlePlus(item) } }}><a-icon type="plus"/></a> |
| | | </Item> |
| | | ) |
| | | }, |
| | | renderItem (item) { |
| | | return item.children ? this.renderSubItem(item, item.key) : this.renderMenuItem(item, item.key) |
| | | }, |
| | | renderItemGroup (item) { |
| | | const childrenItems = item.children.map(o => { |
| | | return this.renderItem(o, o.key) |
| | | }) |
| | | |
| | | return ( |
| | | <ItemGroup key={item.key}> |
| | | <template slot="title"> |
| | | <span>{ item.title }</span> |
| | | <a-dropdown> |
| | | <a class="btn"><a-icon type="ellipsis" /></a> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item key="1">新增</a-menu-item> |
| | | <a-menu-item key="2">合并</a-menu-item> |
| | | <a-menu-item key="3">移除</a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </template> |
| | | { childrenItems } |
| | | </ItemGroup> |
| | | ) |
| | | }, |
| | | renderSubItem (item, key) { |
| | | const childrenItems = item.children && item.children.map(o => { |
| | | return this.renderItem(o, o.key) |
| | | }) |
| | | |
| | | const title = ( |
| | | <span slot="title"> |
| | | { this.renderIcon(item.icon) } |
| | | <span>{ item.title }</span> |
| | | </span> |
| | | ) |
| | | |
| | | if (item.group) { |
| | | return this.renderItemGroup(item) |
| | | } |
| | | // titleClick={this.handleTitleClick(item)} |
| | | return ( |
| | | <SubMenu key={key}> |
| | | { title } |
| | | { childrenItems } |
| | | </SubMenu> |
| | | ) |
| | | } |
| | | }, |
| | | render () { |
| | | const { dataSource, search } = this.$props |
| | | |
| | | // this.localOpenKeys = openKeys.slice(0) |
| | | const list = dataSource.map(item => { |
| | | return this.renderItem(item) |
| | | }) |
| | | |
| | | return ( |
| | | <div class="tree-wrapper"> |
| | | { search ? this.renderSearch() : null } |
| | | <Menu mode="inline" class="custom-tree" {...{ on: { click: item => this.$emit('click', item), 'update:openKeys': val => { this.localOpenKeys = val } } }} openKeys={this.localOpenKeys}> |
| | | { list } |
| | | </Menu> |
| | | </div> |
| | | ) |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <div :class="[prefixCls, reverseColor && 'reverse-color' ]"> |
| | | <span> |
| | | <slot name="term"></slot> |
| | | <span class="item-text"> |
| | | <slot></slot> |
| | | </span> |
| | | </span> |
| | | <span :class="[flag]"><a-icon :type="`caret-${flag}`"/></span> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'Trend', |
| | | props: { |
| | | prefixCls: { |
| | | type: String, |
| | | default: 'ant-pro-trend' |
| | | }, |
| | | /** |
| | | * 上升下降标识:up|down |
| | | */ |
| | | flag: { |
| | | type: String, |
| | | required: true |
| | | }, |
| | | /** |
| | | * 颜色反转 |
| | | */ |
| | | reverseColor: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | @import "index"; |
| | | </style> |
New file |
| | |
| | | import Trend from './Trend.vue' |
| | | |
| | | export default Trend |
New file |
| | |
| | | @import "../index"; |
| | | |
| | | @trend-prefix-cls: ~"@{ant-pro-prefix}-trend"; |
| | | |
| | | .@{trend-prefix-cls} { |
| | | display: inline-block; |
| | | font-size: @font-size-base; |
| | | line-height: 22px; |
| | | |
| | | .up, |
| | | .down { |
| | | margin-left: 4px; |
| | | position: relative; |
| | | top: 1px; |
| | | |
| | | i { |
| | | font-size: 12px; |
| | | transform: scale(0.83); |
| | | } |
| | | } |
| | | |
| | | .item-text { |
| | | display: inline-block; |
| | | margin-left: 8px; |
| | | color: rgba(0,0,0,.85); |
| | | } |
| | | |
| | | .up { |
| | | color: @red-6; |
| | | } |
| | | .down { |
| | | color: @green-6; |
| | | top: -1px; |
| | | } |
| | | |
| | | &.reverse-color .up { |
| | | color: @green-6; |
| | | } |
| | | &.reverse-color .down { |
| | | color: @red-6; |
| | | } |
| | | } |
New file |
| | |
| | | # Trend 趋势标记 |
| | | |
| | | 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。 |
| | | |
| | | |
| | | |
| | | 引用方式: |
| | | |
| | | ```javascript |
| | | import Trend from '@/components/Trend' |
| | | |
| | | export default { |
| | | components: { |
| | | Trend |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | |
| | | |
| | | ## 代码演示 [demo](https://pro.loacg.com/test/home) |
| | | |
| | | ```html |
| | | <trend flag="up">5%</trend> |
| | | ``` |
| | | 或 |
| | | ```html |
| | | <trend flag="up"> |
| | | <span slot="term">工资</span> |
| | | 5% |
| | | </trend> |
| | | ``` |
| | | 或 |
| | | ```html |
| | | <trend flag="up" term="工资">5%</trend> |
| | | ``` |
| | | |
| | | |
| | | ## API |
| | | |
| | | | 参数 | 说明 | 类型 | 默认值 | |
| | | |----------|------------------------------------------|-------------|-------| |
| | | | flag | 上升下降标识:`up|down` | string | - | |
| | | | reverseColor | 颜色反转 | Boolean | false | |
| | | |
New file |
| | |
| | | <template> |
| | | <a-select |
| | | :mode="model" |
| | | showSearch |
| | | :value="selectValue" |
| | | :filter-option="false" |
| | | :placeholder="placeholder" |
| | | :not-found-content="fetching ? undefined : null" |
| | | @search="fetchUser" |
| | | @change="handleChange" |
| | | > |
| | | <a-spin v-if="fetching" slot="notFoundContent" size="small" /> |
| | | <a-select-option v-for="d in data" :key="d.value"> |
| | | {{ d.text }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </template> |
| | | <script> |
| | | import debounce from 'lodash/debounce' |
| | | import { getUserPage } from '@/api/modular/system/userManage' |
| | | |
| | | export default { |
| | | name: 'UserSelect', |
| | | props: { |
| | | placeholder: { |
| | | type: String |
| | | }, |
| | | value: { |
| | | type: String |
| | | }, |
| | | multiple: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data() { |
| | | const multiple = this.multiple |
| | | this.fetchUser = debounce(this.fetchUser, 800) |
| | | return { |
| | | data: [], |
| | | fetching: false, |
| | | selectValue: multiple ? [] : undefined, |
| | | model: multiple ? 'multiple' : 'default' |
| | | } |
| | | }, |
| | | methods: { |
| | | fetchUser(key) { |
| | | console.log('fetching user', key) |
| | | this.data = [] |
| | | this.fetching = true |
| | | |
| | | const params = { |
| | | pageNo: 1, |
| | | pageSize: 10, |
| | | searchValue: key |
| | | } |
| | | this.userFetching = true |
| | | |
| | | getUserPage(params).then((res) => { |
| | | this.data = res.data.rows.map(user => ({ |
| | | text: `${user.name} ${user.account}`, |
| | | value: user.id |
| | | })) |
| | | }).finally(() => { |
| | | this.fetching = false |
| | | }) |
| | | }, |
| | | handleChange(value) { |
| | | Object.assign(this, { |
| | | selectValue: value, |
| | | fetching: false |
| | | }) |
| | | this.$emit('change', value) |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import UserSelect from './UserSelect' |
| | | |
| | | export default UserSelect |
New file |
| | |
| | | /** |
| | | * components util |
| | | */ |
| | | |
| | | /** |
| | | * 清理空值,对象 |
| | | * @param children |
| | | * @returns {*[]} |
| | | */ |
| | | export function filterEmpty (children = []) { |
| | | return children.filter(c => c.tag || (c.text && c.text.trim() !== '')) |
| | | } |
| | | |
| | | /** |
| | | * 获取字符串长度,英文字符 长度1,中文字符长度2 |
| | | * @param {*} str |
| | | */ |
| | | export const getStrFullLength = (str = '') => |
| | | str.split('').reduce((pre, cur) => { |
| | | const charCode = cur.charCodeAt(0) |
| | | if (charCode >= 0 && charCode <= 128) { |
| | | return pre + 1 |
| | | } |
| | | return pre + 2 |
| | | }, 0) |
| | | |
| | | /** |
| | | * 截取字符串,根据 maxLength 截取后返回 |
| | | * @param {*} str |
| | | * @param {*} maxLength |
| | | */ |
| | | export const cutStrByFullLength = (str = '', maxLength) => { |
| | | let showLength = 0 |
| | | return str.split('').reduce((pre, cur) => { |
| | | const charCode = cur.charCodeAt(0) |
| | | if (charCode >= 0 && charCode <= 128) { |
| | | showLength += 1 |
| | | } else { |
| | | showLength += 2 |
| | | } |
| | | if (showLength <= maxLength) { |
| | | return pre + cur |
| | | } |
| | | return pre |
| | | }, '') |
| | | } |
New file |
| | |
| | | @import './index.less'; |
| | | |
| | | body { |
| | | |
| | | |
| | | } |
| | | |
| | | #app { |
| | | height: 100%; |
| | | |
| | | &.colorWeak { |
| | | filter: invert(80%); |
| | | } |
| | | &.userLayout { |
| | | overflow: auto; |
| | | } |
| | | } |
| | | |
| | | .layout.ant-layout { |
| | | height: auto; |
| | | overflow-x: hidden; |
| | | |
| | | &.mobile, |
| | | &.tablet { |
| | | .ant-layout-content { |
| | | .content { |
| | | margin: 24px 0 0; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ant-table-wrapper |
| | | * 覆盖的表格手机模式样式,如果想修改在手机上表格最低宽度,可以在这里改动 |
| | | */ |
| | | .ant-table-wrapper { |
| | | .ant-table-content { |
| | | overflow-y: auto; |
| | | } |
| | | .ant-table-body { |
| | | min-width: 800px; |
| | | } |
| | | } |
| | | .topmenu { |
| | | /* 必须为 topmenu 才能启用流式布局 */ |
| | | &.content-width-Fluid { |
| | | .header-index-wide { |
| | | margin-left: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.mobile { |
| | | .sidemenu { |
| | | .ant-header-fixedHeader { |
| | | &.ant-header-side-opened, |
| | | &.ant-header-side-closed { |
| | | width: 100%; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.ant-layout-has-sider { |
| | | flex-direction: row; |
| | | } |
| | | |
| | | .trigger { |
| | | font-size: 20px; |
| | | line-height: 55px; |
| | | padding: 0 24px; |
| | | cursor: pointer; |
| | | transition: color 0.3s; |
| | | &:hover { |
| | | background: rgba(0, 0, 0, 0.025); |
| | | } |
| | | } |
| | | |
| | | .topmenu { |
| | | .ant-header-fixedHeader { |
| | | position: fixed; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 9; |
| | | width: 100%; |
| | | transition: width 0.2s; |
| | | |
| | | &.ant-header-side-opened { |
| | | width: 100%; |
| | | } |
| | | |
| | | &.ant-header-side-closed { |
| | | width: 100%; |
| | | } |
| | | } |
| | | /* 必须为 topmenu 才能启用流式布局 */ |
| | | &.content-width-Fluid { |
| | | .header-index-wide { |
| | | max-width: unset; |
| | | .header-index-left { |
| | | flex: 1 1 1000px; |
| | | .logo{ |
| | | margin-left: 25px; |
| | | } |
| | | .ant-menu.ant-menu-horizontal{ |
| | | max-width: calc(100vw - 190px - 238px - 25px); |
| | | flex: 1 1 calc(100vw - 190px - 238px - 25px); |
| | | } |
| | | } |
| | | .header-index-right{ |
| | | margin-right:25px; |
| | | } |
| | | } |
| | | |
| | | .page-header-index-wide { |
| | | max-width: unset; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .sidemenu { |
| | | .ant-header-fixedHeader { |
| | | position: fixed; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 9; |
| | | width: 100%; |
| | | transition: width 0.2s; |
| | | |
| | | &.ant-header-side-opened { |
| | | width: calc(100% - 230px); |
| | | } |
| | | |
| | | &.ant-header-side-closed { |
| | | width: calc(100% - 80px); |
| | | } |
| | | } |
| | | } |
| | | |
| | | .header { |
| | | height: 55px; |
| | | // padding: 0 12px 0 0; |
| | | background: #fff; |
| | | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); |
| | | position: relative; |
| | | } |
| | | |
| | | .header, |
| | | .top-nav-header-index { |
| | | .user-wrapper { |
| | | float: right; |
| | | height: 100%; |
| | | |
| | | .action { |
| | | line-height: 55px; |
| | | cursor: pointer; |
| | | padding: 0 12px; |
| | | display: inline-block; |
| | | transition: all 0.3s; |
| | | height: 100%; |
| | | color: rgba(0, 0, 0, 0.65); |
| | | |
| | | &:hover { |
| | | background: rgba(0, 0, 0, 0.025); |
| | | } |
| | | |
| | | .avatar { |
| | | margin: 15px 8px 15px 0; |
| | | color: #1890ff; |
| | | background: hsla(0, 0%, 100%, 0.85); |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | .icon { |
| | | font-size: 16px; |
| | | padding: 4px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.dark { |
| | | .user-wrapper { |
| | | .action { |
| | | color: rgba(255, 255, 255, 0.85); |
| | | a { |
| | | color: rgba(255, 255, 255, 0.85); |
| | | } |
| | | |
| | | &:hover { |
| | | background: rgba(255, 255, 255, 0.16); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.mobile, |
| | | &.tablet { |
| | | .top-nav-header-index { |
| | | .header-index-wide { |
| | | .header-index-left { |
| | | .trigger { |
| | | color: rgba(255, 255, 255, 0.85); |
| | | padding: 0 12px; |
| | | } |
| | | |
| | | .logo.top-nav-header { |
| | | flex: 0 0 56px; |
| | | text-align: center; |
| | | line-height: 58px; |
| | | h1 { |
| | | display: none; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.light { |
| | | .header-index-wide { |
| | | .header-index-left { |
| | | .trigger { |
| | | color: rgba(0, 0, 0, 0.65); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.tablet { |
| | | // overflow: hidden; text-overflow:ellipsis; white-space: nowrap; |
| | | .top-nav-header-index { |
| | | .header-index-wide { |
| | | .header-index-left { |
| | | .logo > a { |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | .ant-menu.ant-menu-horizontal { |
| | | flex: 1 1 auto; |
| | | white-space: normal; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .top-nav-header-index { |
| | | box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); |
| | | position: relative; |
| | | transition: background 0.3s, width 0.2s; |
| | | |
| | | .header-index-wide { |
| | | max-width: 1200px; |
| | | margin: auto; |
| | | padding-left: 0; |
| | | display: flex; |
| | | height: 55px; |
| | | |
| | | .ant-menu.ant-menu-horizontal { |
| | | max-width: 835px; |
| | | flex: 0 1 835px; |
| | | border: none; |
| | | height: 55px; |
| | | line-height: 55px; |
| | | } |
| | | |
| | | .header-index-left { |
| | | flex: 0 1 1000px; |
| | | display: flex; |
| | | |
| | | .logo.top-nav-header { |
| | | flex: 0 0 165px; |
| | | width: 165px; |
| | | height: 55px; |
| | | position: relative; |
| | | line-height: 55px; |
| | | transition: all 0.3s; |
| | | overflow: hidden; |
| | | |
| | | img, |
| | | svg { |
| | | display: inline-block; |
| | | vertical-align: middle; |
| | | height: 32px; |
| | | width: 32px; |
| | | } |
| | | |
| | | h1 { |
| | | color: #fff; |
| | | display: inline-block; |
| | | vertical-align: top; |
| | | font-size: 16px; |
| | | margin: 0 0 0 12px; |
| | | font-weight: 400; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .header-index-right { |
| | | flex: 0 0 238px; |
| | | align-self: flex-end; |
| | | height: 55px; |
| | | overflow: hidden; |
| | | |
| | | .content-box { |
| | | float: right; |
| | | .action { |
| | | max-width: 140px; |
| | | overflow: hidden; |
| | | text-overflow:ellipsis; |
| | | white-space:nowrap; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | &.light { |
| | | background-color: #fff; |
| | | |
| | | .header-index-wide { |
| | | .header-index-left { |
| | | .logo { |
| | | h1 { |
| | | color: #002140; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 内容区 |
| | | .layout-content { |
| | | margin: 24px 24px 0px; |
| | | //height: 100%; |
| | | //height: 64px; |
| | | padding: 0 12px 0 0; |
| | | } |
| | | |
| | | // footer |
| | | .ant-layout-footer { |
| | | padding: 0; |
| | | } |
| | | } |
| | | |
| | | .topmenu { |
| | | .page-header-index-wide { |
| | | max-width: 1200px; |
| | | margin: 0 auto; |
| | | } |
| | | } |
| | | |
| | | // drawer-sider 自定义 |
| | | .ant-drawer.drawer-sider { |
| | | .sider { |
| | | box-shadow: none; |
| | | } |
| | | |
| | | &.dark { |
| | | .ant-drawer-content { |
| | | background-color: rgb(0, 21, 41); |
| | | } |
| | | } |
| | | &.light { |
| | | box-shadow: none; |
| | | .ant-drawer-content { |
| | | background-color: #fff; |
| | | } |
| | | } |
| | | |
| | | .ant-drawer-body { |
| | | padding: 0; |
| | | } |
| | | } |
| | | |
| | | // 菜单样式 |
| | | .sider { |
| | | box-shadow: 2px 0 6px rgba(0, 21, 41, 0.35); |
| | | position: relative; |
| | | z-index: @ant-global-sider-zindex; |
| | | min-height: 100vh; |
| | | |
| | | .ant-layout-sider-children { |
| | | overflow-y: hidden; |
| | | |
| | | &:hover { |
| | | overflow-y: auto; |
| | | } |
| | | } |
| | | |
| | | &.ant-fixed-sidemenu { |
| | | position: fixed; |
| | | height: 100%; |
| | | } |
| | | |
| | | // logo区域样式 |
| | | .logo { |
| | | position: relative; |
| | | height: 55px; |
| | | padding-left: 24px; |
| | | overflow: hidden; |
| | | line-height: 55px; |
| | | background: #002140; |
| | | transition: all .3s; |
| | | |
| | | img, |
| | | svg, |
| | | h1 { |
| | | display: inline-block; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | img, |
| | | svg { |
| | | height: 32px; |
| | | width: 32px; |
| | | } |
| | | |
| | | h1 { |
| | | color: #fff; |
| | | font-size: 20px; |
| | | margin: 0 0 0 12px; |
| | | font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; |
| | | font-weight: 600; |
| | | vertical-align: middle; |
| | | } |
| | | } |
| | | |
| | | &.light { |
| | | background-color: #fff; |
| | | box-shadow: 2px 0px 8px 0px rgba(29, 35, 41, 0.05); |
| | | |
| | | .logo { |
| | | background: #fff; |
| | | box-shadow: 1px 1px 0px 0px #e8e8e8; |
| | | |
| | | h1 { |
| | | color: unset; |
| | | } |
| | | } |
| | | |
| | | .ant-menu-light { |
| | | border-right-color: transparent; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 外置的样式控制 |
| | | .user-dropdown-menu { |
| | | span { |
| | | user-select: none; |
| | | } |
| | | } |
| | | .user-dropdown-menu-wrapper.ant-dropdown-menu { |
| | | padding: 4px 0; |
| | | |
| | | .ant-dropdown-menu-item { |
| | | width: 160px; |
| | | } |
| | | |
| | | .ant-dropdown-menu-item > .anticon:first-child, |
| | | .ant-dropdown-menu-item > a > .anticon:first-child, |
| | | .ant-dropdown-menu-submenu-title > .anticon:first-child .ant-dropdown-menu-submenu-title > a > .anticon:first-child { |
| | | min-width: 12px; |
| | | margin-right: 8px; |
| | | } |
| | | } |
| | | |
| | | // 数据列表 样式 |
| | | .table-alert { |
| | | margin-bottom: 16px; |
| | | } |
| | | |
| | | .table-page-search-wrapper { |
| | | .ant-form-inline { |
| | | .ant-form-item { |
| | | display: flex; |
| | | margin-bottom: 24px; |
| | | margin-right: 0; |
| | | |
| | | .ant-form-item-control-wrapper { |
| | | flex: 1 1; |
| | | display: inline-block; |
| | | vertical-align: middle; |
| | | } |
| | | |
| | | > .ant-form-item-label { |
| | | line-height: 32px; |
| | | padding-right: 8px; |
| | | width: auto; |
| | | } |
| | | .ant-form-item-control { |
| | | height: 32px; |
| | | line-height: 32px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .table-page-search-submitButtons { |
| | | display: block; |
| | | margin-bottom: 24px; |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | |
| | | .content { |
| | | .table-operator { |
| | | margin-bottom: 18px; |
| | | |
| | | button { |
| | | margin-right: 8px; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | // chart |
| | | import Bar from '@/components/Charts/Bar' |
| | | import ChartCard from '@/components/Charts/ChartCard' |
| | | import Liquid from '@/components/Charts/Liquid' |
| | | import MiniArea from '@/components/Charts/MiniArea' |
| | | import MiniSmoothArea from '@/components/Charts/MiniSmoothArea' |
| | | import MiniBar from '@/components/Charts/MiniBar' |
| | | import MiniProgress from '@/components/Charts/MiniProgress' |
| | | import Radar from '@/components/Charts/Radar' |
| | | import RankList from '@/components/Charts/RankList' |
| | | import TransferBar from '@/components/Charts/TransferBar' |
| | | import TagCloud from '@/components/Charts/TagCloud' |
| | | |
| | | // pro components |
| | | import AvatarList from '@/components/AvatarList' |
| | | import CountDown from '@/components/CountDown' |
| | | import Ellipsis from '@/components/Ellipsis' |
| | | import FooterToolbar from '@/components/FooterToolbar' |
| | | import NumberInfo from '@/components/NumberInfo' |
| | | import DescriptionList from '@/components/DescriptionList' |
| | | import Tree from '@/components/Tree/Tree' |
| | | import Trend from '@/components/Trend' |
| | | import STable from '@/components/Table' |
| | | import MultiTab from '@/components/MultiTab' |
| | | import Result from '@/components/Result' |
| | | import IconSelector from '@/components/IconSelector' |
| | | import TagSelect from '@/components/TagSelect' |
| | | import ExceptionPage from '@/components/Exception' |
| | | import StandardFormRow from '@/components/StandardFormRow' |
| | | import ArticleListContent from '@/components/ArticleListContent' |
| | | import AntdEditor from '@/components/Editor/WangEditor' |
| | | import Dialog from '@/components/Dialog' |
| | | |
| | | // xn components |
| | | import XCard from '@/components/xnComponents/XCard' |
| | | import XDown from '@/components/xnComponents/XDown' |
| | | |
| | | export { |
| | | AvatarList, |
| | | Bar, |
| | | ChartCard, |
| | | Liquid, |
| | | MiniArea, |
| | | MiniSmoothArea, |
| | | MiniBar, |
| | | MiniProgress, |
| | | Radar, |
| | | TagCloud, |
| | | RankList, |
| | | TransferBar, |
| | | Trend, |
| | | CountDown, |
| | | Ellipsis, |
| | | FooterToolbar, |
| | | NumberInfo, |
| | | DescriptionList, |
| | | // 兼容写法,请勿继续使用 |
| | | DescriptionList as DetailList, |
| | | Tree, |
| | | STable, |
| | | MultiTab, |
| | | Result, |
| | | ExceptionPage, |
| | | IconSelector, |
| | | TagSelect, |
| | | StandardFormRow, |
| | | ArticleListContent, |
| | | AntdEditor, |
| | | Dialog, |
| | | XCard, |
| | | XDown |
| | | } |
New file |
| | |
| | | @import "~ant-design-vue/lib/style/index"; |
| | | |
| | | // The prefix to use on all css classes from ant-pro. |
| | | @ant-pro-prefix : ant-pro; |
| | | @ant-global-sider-zindex : 106; |
| | | @ant-global-header-zindex : 105; |
New file |
| | |
| | | <template> |
| | | <a-breadcrumb class="breadcrumb"> |
| | | <a-breadcrumb-item v-for="(item, index) in breadList" :key="item.name"> |
| | | <router-link |
| | | v-if="item.name != name && index != 1" |
| | | :to="{ path: item.path === '' ? '/' : item.path }" |
| | | >{{ item.meta.title }}</router-link> |
| | | <span v-else>{{ item.meta.title }}</span> |
| | | </a-breadcrumb-item> |
| | | </a-breadcrumb> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data () { |
| | | return { |
| | | name: '', |
| | | breadList: [] |
| | | } |
| | | }, |
| | | created () { |
| | | this.getBreadcrumb() |
| | | }, |
| | | methods: { |
| | | getBreadcrumb () { |
| | | this.breadList = [] |
| | | // this.breadList.push({name: 'index', path: '/dashboard/', meta: {title: '首页'}}) |
| | | |
| | | this.name = this.$route.name |
| | | this.$route.matched.forEach(item => { |
| | | // item.name !== 'index' && this.breadList.push(item) |
| | | this.breadList.push(item) |
| | | }) |
| | | } |
| | | }, |
| | | watch: { |
| | | $route () { |
| | | this.getBreadcrumb() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
New file |
| | |
| | | <script> |
| | | /* WARNING: 兼容老引入,请勿继续使用 */ |
| | | import DescriptionList from '@/components/DescriptionList' |
| | | export default DescriptionList |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div class="head-info" :class="center && 'center'"> |
| | | <span>{{ title }}</span> |
| | | <p>{{ content }}</p> |
| | | <em v-if="bordered"/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'HeadInfo', |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | content: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | bordered: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | center: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .head-info { |
| | | position: relative; |
| | | text-align: left; |
| | | padding: 0 32px 0 0; |
| | | min-width: 125px; |
| | | |
| | | &.center { |
| | | text-align: center; |
| | | padding: 0 32px; |
| | | } |
| | | |
| | | span { |
| | | color: rgba(0, 0, 0, .45); |
| | | display: inline-block; |
| | | font-size: 14px; |
| | | line-height: 22px; |
| | | margin-bottom: 4px; |
| | | } |
| | | p { |
| | | color: rgba(0, 0, 0, .85); |
| | | font-size: 24px; |
| | | line-height: 32px; |
| | | margin: 0; |
| | | } |
| | | em { |
| | | background-color: #e8e8e8; |
| | | position: absolute; |
| | | height: 56px; |
| | | width: 1px; |
| | | top: 0; |
| | | right: 0; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-dropdown> |
| | | <span class="action global-lang"> |
| | | <a-icon type="global" style="font-size: 16px"/> |
| | | </span> |
| | | <a-menu slot="overlay" style="width: 150px;" @click="SwitchLang"> |
| | | <a-menu-item key="zh-CN"> |
| | | <a rel="noopener noreferrer"> |
| | | <span role="img" aria-label="简体中文">🇨🇳</span> 简体中文 |
| | | </a> |
| | | </a-menu-item> |
| | | <a-menu-item key="zh-TW"> |
| | | <a rel="noopener noreferrer"> |
| | | <span role="img" aria-label="繁体中文">🇭🇰</span> 繁体中文 |
| | | </a> |
| | | </a-menu-item> |
| | | <a-menu-item key="en-US"> |
| | | <a rel="noopener noreferrer"> |
| | | <span role="img" aria-label="English">🇬🇧</span> English |
| | | </a> |
| | | </a-menu-item> |
| | | <a-menu-item key="pt-BR"> |
| | | <a rel="noopener noreferrer"> |
| | | <span role="img" aria-label="Português">🇧🇷</span> Português |
| | | </a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </template> |
| | | |
| | | <script> |
| | | // import { mixin as langMixin } from '@/store/i18n-mixin' |
| | | |
| | | export default { |
| | | name: 'LangSelect', |
| | | // mixins: [langMixin], |
| | | data () { |
| | | return {} |
| | | }, |
| | | methods: { |
| | | // SwitchLang (row) { |
| | | // this.setLang(row.key) |
| | | // } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div class="logo"> |
| | | <router-link :to="{name:'Console'}"> |
| | | <LogoSvg alt="logo" /> |
| | | <h1 v-if="showTitle">{{ this.titles }}</h1> |
| | | </router-link> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import LogoSvg from '@/assets/logo.svg?inline' |
| | | import { mixin, mixinDevice } from '@/utils/mixin' |
| | | |
| | | export default { |
| | | name: 'Logo', |
| | | components: { |
| | | LogoSvg |
| | | }, |
| | | mixins: [mixin, mixinDevice], |
| | | data () { |
| | | return { |
| | | titles: '' |
| | | } |
| | | }, |
| | | props: { |
| | | title: { |
| | | type: String, |
| | | default: 'Snowy', |
| | | required: false |
| | | }, |
| | | showTitle: { |
| | | type: Boolean, |
| | | default: true, |
| | | required: false |
| | | } |
| | | }, |
| | | created () { |
| | | if (this.layoutMode === 'topmenu') { |
| | | if (this.title.length > 8) { |
| | | this.titles = this.title.substring(0, 7) + '...' |
| | | } else { |
| | | this.titles = this.title |
| | | } |
| | | } else { |
| | | if (this.title.length > 10) { |
| | | this.titles = this.title.substring(0, 9) + '...' |
| | | } else { |
| | | this.titles = this.title |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <!-- 两步验证 --> |
| | | <a-modal |
| | | centered |
| | | v-model="visible" |
| | | @cancel="handleCancel" |
| | | :maskClosable="false" |
| | | > |
| | | <div slot="title" :style="{ textAlign: 'center' }">两步验证</div> |
| | | <template slot="footer"> |
| | | <div :style="{ textAlign: 'center' }"> |
| | | <a-button key="back" @click="handleCancel">返回</a-button> |
| | | <a-button key="submit" type="primary" :loading="stepLoading" @click="handleStepOk"> |
| | | 继续 |
| | | </a-button> |
| | | </div> |
| | | </template> |
| | | |
| | | <a-spin :spinning="stepLoading"> |
| | | <a-form layout="vertical" :auto-form-create="(form)=>{this.form = form}"> |
| | | <div class="step-form-wrapper"> |
| | | <p style="text-align: center" v-if="!stepLoading">请在手机中打开 Google Authenticator 或两步验证 APP<br />输入 6 位动态码</p> |
| | | <p style="text-align: center" v-else>正在验证..<br/>请稍后</p> |
| | | <a-form-item |
| | | :style="{ textAlign: 'center' }" |
| | | hasFeedback |
| | | fieldDecoratorId="stepCode" |
| | | :fieldDecoratorOptions="{rules: [{ required: true, message: '请输入 6 位动态码!', pattern: /^\d{6}$/, len: 6 }]}" |
| | | > |
| | | <a-input :style="{ textAlign: 'center' }" @keyup.enter.native="handleStepOk" placeholder="000000" /> |
| | | </a-form-item> |
| | | <p style="text-align: center"> |
| | | <a @click="onForgeStepCode">遗失手机?</a> |
| | | </p> |
| | | </div> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | visible: { |
| | | type: Boolean, |
| | | default: false |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | stepLoading: false, |
| | | |
| | | form: null |
| | | } |
| | | }, |
| | | methods: { |
| | | handleStepOk () { |
| | | const vm = this |
| | | this.stepLoading = true |
| | | this.form.validateFields((err, values) => { |
| | | if (!err) { |
| | | console.log('values', values) |
| | | setTimeout(() => { |
| | | vm.stepLoading = false |
| | | vm.$emit('success', { values }) |
| | | }, 2000) |
| | | return |
| | | } |
| | | this.stepLoading = false |
| | | this.$emit('error', { err }) |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.visible = false |
| | | this.$emit('cancel') |
| | | }, |
| | | onForgeStepCode () { |
| | | |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less" scoped> |
| | | .step-form-wrapper { |
| | | margin: 0 auto; |
| | | width: 80%; |
| | | max-width: 400px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="user-wrapper"> |
| | | <div class="content-box"> |
| | | <span class="action" @click="toggleFullscreen"> |
| | | <a-icon type="fullscreen-exit" v-if="isFullscreen"/> |
| | | <a-icon type="fullscreen" v-else/> |
| | | </span> |
| | | <notice-icon class="action"/> |
| | | <a-dropdown> |
| | | <span class="action ant-dropdown-link user-dropdown-menu"> |
| | | <a-avatar class="avatar" size="small" :src="avatar"/> |
| | | <span>{{ nickname }}</span> |
| | | </span> |
| | | <a-menu slot="overlay" class="user-dropdown-menu-wrapper"> |
| | | <a-menu-item key="4" v-if="mode === 'sidemenu'"> |
| | | <a @click="appToggled()" > |
| | | <a-icon type="swap"/> |
| | | <span>切换应用</span> |
| | | </a> |
| | | </a-menu-item> |
| | | <a-menu-item key="0"> |
| | | <router-link :to="{ name: 'center' }"> |
| | | <a-icon type="user"/> |
| | | <span>个人中心</span> |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-item key="1"> |
| | | <router-link :to="{ name: 'settings' }"> |
| | | <a-icon type="setting"/> |
| | | <span>账户设置</span> |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-divider/> |
| | | <a-menu-item key="3"> |
| | | <a href="javascript:;" @click="handleLogout"> |
| | | <a-icon type="logout"/> |
| | | <span>退出登录</span> |
| | | </a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </div> |
| | | <a-modal |
| | | title="切换应用" |
| | | :visible="visible" |
| | | :footer="null" |
| | | :confirm-loading="confirmLoading" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-form :form="form1" > |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="选择应用" |
| | | > |
| | | <a-menu |
| | | mode="inline" |
| | | :default-selected-keys="this.defApp" |
| | | style="border-bottom:0px;lineHeight:55px;" |
| | | > |
| | | <a-menu-item v-for="(item) in userInfo.apps" :key="item.code" style="top:0px;" @click="switchApp(item.code)"> |
| | | {{ item.name }} |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-modal> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import screenfull from 'screenfull' |
| | | import NoticeIcon from '@/components/NoticeIcon' |
| | | import { mapActions, mapGetters } from 'vuex' |
| | | import { ALL_APPS_MENU } from '@/store/mutation-types' |
| | | import Vue from 'vue' |
| | | import { message } from 'ant-design-vue/es' |
| | | |
| | | export default { |
| | | name: 'UserMenu', |
| | | components: { |
| | | NoticeIcon, |
| | | screenfull |
| | | }, |
| | | props: { |
| | | mode: { |
| | | type: String, |
| | | // sidemenu, topmenu |
| | | default: 'sidemenu' |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 16 } |
| | | }, |
| | | visible: false, |
| | | confirmLoading: false, |
| | | form1: this.$form.createForm(this), |
| | | defApp: [], |
| | | isFullscreen: false |
| | | } |
| | | }, |
| | | |
| | | computed: { |
| | | ...mapGetters(['nickname', 'avatar', 'userInfo']) |
| | | }, |
| | | methods: { |
| | | ...mapActions(['Logout', 'MenuChange']), |
| | | |
| | | handleLogout () { |
| | | this.$confirm({ |
| | | title: '提示', |
| | | content: '真的要注销登录吗 ?', |
| | | okText: '确定', |
| | | cancelText: '取消', |
| | | onOk: () => { |
| | | return this.Logout({}).then(() => { |
| | | setTimeout(() => { |
| | | window.location.reload() |
| | | }, 16) |
| | | }).catch(err => { |
| | | this.$message.error({ |
| | | title: '错误', |
| | | description: err.message |
| | | }) |
| | | }) |
| | | }, |
| | | onCancel () { |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | /** |
| | | * 打开切换应用框 |
| | | */ |
| | | appToggled () { |
| | | this.visible = true |
| | | this.defApp.push(Vue.ls.get(ALL_APPS_MENU)[0].code) |
| | | }, |
| | | |
| | | switchApp (appCode) { |
| | | this.visible = false |
| | | this.defApp = [] |
| | | const applicationData = this.userInfo.apps.filter(item => item.code === appCode) |
| | | const hideMessage = message.loading('正在切换应用!', 0) |
| | | this.MenuChange(applicationData[0]).then((res) => { |
| | | hideMessage() |
| | | // eslint-disable-next-line handle-callback-err |
| | | }).catch((err) => { |
| | | message.error('应用切换异常') |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form1.resetFields() |
| | | this.visible = false |
| | | }, |
| | | /* 全屏切换 */ |
| | | toggleFullscreen () { |
| | | if (!screenfull.isEnabled) { |
| | | message.error('您的浏览器不支持全屏模式') |
| | | return |
| | | } |
| | | screenfull.toggle() |
| | | if (screenfull.isFullscreen) { |
| | | this.isFullscreen = false |
| | | } else { |
| | | this.isFullscreen = true |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .appRedio { |
| | | border:1px solid #91d5ff; |
| | | padding:10px 20px; |
| | | background: #e6f7ff; |
| | | border-radius:2px; |
| | | margin-bottom:10px; |
| | | color: #91d5ff; |
| | | /*display: inline; |
| | | margin-bottom:10px;*/ |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div :class="mode=='pop'?'mask':''" v-show="showBox"> |
| | | <div :class="mode=='pop'?'verifybox':''" :style="{'max-width':parseInt(imgSize.width)+30+'px'}"> |
| | | <div class="verifybox-top" v-if="mode=='pop'"> |
| | | 请完成安全验证 |
| | | <span @click="closeBox" class="verifybox-close"> |
| | | <i class="iconfont icon-close"></i> |
| | | </span> |
| | | </div> |
| | | <div :style="{padding:mode=='pop'?'15px':'0'}" class="verifybox-bottom"> |
| | | <!-- 验证码容器 --> |
| | | <components |
| | | :arith="arith" |
| | | :barSize="barSize" |
| | | :blockSize="blockSize" |
| | | :captchaType="captchaType" |
| | | :explain="explain" |
| | | :figure="figure" |
| | | :imgSize="imgSize" |
| | | :is="componentType" |
| | | :mode="mode" |
| | | :type="verifyType" |
| | | :vSpace="vSpace" |
| | | ref="instance" |
| | | v-if="componentType"></components> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script type="text/babel"> |
| | | /** |
| | | * Verify 验证码组件 |
| | | * @description 分发验证码使用 |
| | | * */ |
| | | import VerifySlide from './Verify/VerifySlide' |
| | | import VerifyPoints from './Verify/VerifyPoints' |
| | | |
| | | export default { |
| | | name: 'Vue2Verify', |
| | | props: { |
| | | // 双语化 |
| | | locale: { |
| | | require: false, |
| | | type: String, |
| | | default() { |
| | | // 默认语言不输入为浏览器语言 |
| | | if (navigator.language) { |
| | | var language = navigator.language |
| | | } else { |
| | | // eslint-disable-next-line no-redeclare |
| | | var language = navigator.browserLanguage |
| | | } |
| | | return language |
| | | } |
| | | }, |
| | | captchaType: { |
| | | type: String, |
| | | required: true |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | figure: { |
| | | type: Number |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | arith: { |
| | | type: Number |
| | | }, |
| | | mode: { |
| | | type: String, |
| | | default: 'pop' |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | vSpace: { |
| | | type: Number |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | explain: { |
| | | type: String |
| | | }, |
| | | imgSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '310px', |
| | | height: '155px' |
| | | } |
| | | } |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | blockSize: { |
| | | type: Object |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | barSize: { |
| | | type: Object |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | // showBox:true, |
| | | clickShow: false, |
| | | // 内部类型 |
| | | verifyType: undefined, |
| | | // 所用组件类型 |
| | | componentType: undefined |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * i18n |
| | | * @description 兼容vue-i18n 调用$t来转换ok |
| | | * @param {String} text-被转换的目标 |
| | | * @return {String} i18n的结果 |
| | | * */ |
| | | i18n(text) { |
| | | if (this.$t) { |
| | | return this.$t(text) |
| | | } else { |
| | | // 兼容不存在的语言 |
| | | const i18n = this.$options.i18n.messages[this.locale] || this.$options.i18n.messages['en-US'] |
| | | return i18n[text] |
| | | } |
| | | }, |
| | | /** |
| | | * refresh |
| | | * @description 刷新 |
| | | * */ |
| | | refresh() { |
| | | if (this.instance.refresh) { |
| | | this.instance.refresh() |
| | | } |
| | | }, |
| | | closeBox() { |
| | | this.clickShow = false |
| | | this.refresh() |
| | | }, |
| | | show() { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.mode == 'pop') { |
| | | this.clickShow = true |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | instance() { |
| | | return this.$refs.instance || {} |
| | | }, |
| | | showBox() { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.mode == 'pop') { |
| | | return this.clickShow |
| | | } else { |
| | | return true |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | captchaType: { |
| | | immediate: true, |
| | | handler(captchaType) { |
| | | switch (captchaType.toString()) { |
| | | case 'blockPuzzle': |
| | | this.verifyType = '2' |
| | | this.componentType = 'VerifySlide' |
| | | break |
| | | case 'clickWord': |
| | | this.verifyType = '' |
| | | this.componentType = 'VerifyPoints' |
| | | break |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | components: { |
| | | VerifySlide, |
| | | VerifyPoints |
| | | } |
| | | } |
| | | </script> |
| | | <style> |
| | | .verifybox{ |
| | | position: relative; |
| | | box-sizing: border-box; |
| | | border-radius: 2px; |
| | | border: 1px solid #e4e7eb; |
| | | background-color: #fff; |
| | | box-shadow: 0 0 10px rgba(0,0,0,.3); |
| | | left: 50%; |
| | | top:50%; |
| | | transform: translate(-50%,-50%); |
| | | } |
| | | .verifybox-top{ |
| | | padding: 0 15px; |
| | | height: 50px; |
| | | line-height: 50px; |
| | | text-align: left; |
| | | font-size: 16px; |
| | | color: #45494c; |
| | | border-bottom: 1px solid #e4e7eb; |
| | | box-sizing: border-box; |
| | | } |
| | | .verifybox-bottom{ |
| | | padding: 15px; |
| | | box-sizing: border-box; |
| | | } |
| | | .verifybox-close{ |
| | | position: absolute; |
| | | top: 13px; |
| | | right: 9px; |
| | | width: 24px; |
| | | height: 24px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | .mask{ |
| | | position: fixed; |
| | | top: 0; |
| | | left:0; |
| | | z-index: 1001; |
| | | width: 100%; |
| | | height: 100vh; |
| | | background: rgba(0,0,0,.3); |
| | | /* display: none; */ |
| | | transition: all .5s; |
| | | } |
| | | .verify-tips{ |
| | | position: absolute; |
| | | left: 0px; |
| | | bottom:0px; |
| | | width: 100%; |
| | | height: 30px; |
| | | line-height:30px; |
| | | color: #fff; |
| | | } |
| | | .suc-bg{ |
| | | background-color:rgba(92, 184, 92,.5); |
| | | filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7f5CB85C, endcolorstr=#7f5CB85C); |
| | | } |
| | | .err-bg{ |
| | | background-color:rgba(217, 83, 79,.5); |
| | | filter: progid:DXImageTransform.Microsoft.gradient(startcolorstr=#7fD9534F, endcolorstr=#7fD9534F); |
| | | } |
| | | .tips-enter,.tips-leave-to{ |
| | | bottom: -30px; |
| | | } |
| | | .tips-enter-active,.tips-leave-active{ |
| | | transition: bottom .5s; |
| | | } |
| | | /* ---------------------------- */ |
| | | /*常规验证码*/ |
| | | .verify-code { |
| | | font-size: 20px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | margin-bottom: 5px; |
| | | border: 1px solid #ddd; |
| | | } |
| | | |
| | | .cerify-code-panel { |
| | | height: 100%; |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .verify-code-area { |
| | | float: left; |
| | | } |
| | | |
| | | .verify-input-area { |
| | | float: left; |
| | | width: 60%; |
| | | padding-right: 10px; |
| | | |
| | | } |
| | | |
| | | .verify-change-area { |
| | | line-height: 30px; |
| | | float: left; |
| | | } |
| | | |
| | | .varify-input-code { |
| | | display: inline-block; |
| | | width: 100%; |
| | | height: 25px; |
| | | } |
| | | |
| | | .verify-change-code { |
| | | color: #337AB7; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .verify-btn { |
| | | width: 200px; |
| | | height: 30px; |
| | | background-color: #337AB7; |
| | | color: #FFFFFF; |
| | | border: none; |
| | | margin-top: 10px; |
| | | } |
| | | |
| | | /*滑动验证码*/ |
| | | .verify-bar-area { |
| | | position: relative; |
| | | background: #FFFFFF; |
| | | text-align: center; |
| | | -webkit-box-sizing: content-box; |
| | | -moz-box-sizing: content-box; |
| | | box-sizing: content-box; |
| | | border: 1px solid #ddd; |
| | | -webkit-border-radius: 4px; |
| | | } |
| | | |
| | | .verify-bar-area .verify-move-block { |
| | | position: absolute; |
| | | top: 0px; |
| | | left: 0; |
| | | background: #fff; |
| | | cursor: pointer; |
| | | -webkit-box-sizing: content-box; |
| | | -moz-box-sizing: content-box; |
| | | box-sizing: content-box; |
| | | box-shadow: 0 0 2px #888888; |
| | | -webkit-border-radius: 1px; |
| | | } |
| | | |
| | | .verify-bar-area .verify-move-block:hover { |
| | | background-color: #337ab7; |
| | | color: #FFFFFF; |
| | | } |
| | | |
| | | .verify-bar-area .verify-left-bar { |
| | | position: absolute; |
| | | top: -1px; |
| | | left: -1px; |
| | | background: #f0fff0; |
| | | cursor: pointer; |
| | | -webkit-box-sizing: content-box; |
| | | -moz-box-sizing: content-box; |
| | | box-sizing: content-box; |
| | | border: 1px solid #ddd; |
| | | } |
| | | |
| | | .verify-img-panel { |
| | | margin: 0; |
| | | -webkit-box-sizing: content-box; |
| | | -moz-box-sizing: content-box; |
| | | box-sizing: content-box; |
| | | border-top: 1px solid #ddd; |
| | | border-bottom: 1px solid #ddd; |
| | | border-radius: 3px; |
| | | position: relative; |
| | | } |
| | | |
| | | .verify-img-panel .verify-refresh { |
| | | width: 25px; |
| | | height: 25px; |
| | | text-align: center; |
| | | padding: 5px; |
| | | cursor: pointer; |
| | | position: absolute; |
| | | top: 0; |
| | | right: 0; |
| | | z-index: 2; |
| | | } |
| | | |
| | | .verify-img-panel .icon-refresh { |
| | | font-size: 20px; |
| | | color: #fff; |
| | | } |
| | | |
| | | .verify-img-panel .verify-gap { |
| | | background-color: #fff; |
| | | position: relative; |
| | | z-index: 2; |
| | | border: 1px solid #fff; |
| | | } |
| | | |
| | | .verify-bar-area .verify-move-block .verify-sub-block { |
| | | position: absolute; |
| | | text-align: center; |
| | | z-index: 3; |
| | | /* border: 1px solid #fff; */ |
| | | } |
| | | |
| | | .verify-bar-area .verify-move-block .verify-icon { |
| | | font-size: 18px; |
| | | } |
| | | |
| | | .verify-bar-area .verify-msg { |
| | | z-index: 3; |
| | | } |
| | | |
| | | /*字体图标的css*/ |
| | | /*@font-face {font-family: "iconfont";*/ |
| | | /*src: url('../fonts/iconfont.eot?t=1508229193188'); !* IE9*!*/ |
| | | /*src: url('../fonts/iconfont.eot?t=1508229193188#iefix') format('embedded-opentype'), !* IE6-IE8 *!*/ |
| | | /*url('data:application/x-font-woff;charset=utf-8;base64,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') format('woff'),*/ |
| | | /*url('../fonts/iconfont.ttf?t=1508229193188') format('truetype'), !* chrome, firefox, opera, Safari, Android, iOS 4.2+*!*/ |
| | | /*url('../fonts/iconfont.svg?t=1508229193188#iconfont') format('svg'); !* iOS 4.1- *!*/ |
| | | /*}*/ |
| | | |
| | | .iconfont { |
| | | font-family: "iconfont" !important; |
| | | font-size: 16px; |
| | | font-style: normal; |
| | | -webkit-font-smoothing: antialiased; |
| | | -moz-osx-font-smoothing: grayscale; |
| | | } |
| | | |
| | | .icon-check:before { |
| | | content: " "; |
| | | display: block; |
| | | width: 16px; |
| | | height: 16px; |
| | | position: absolute; |
| | | margin: auto; |
| | | left: 0; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | z-index: 9999; |
| | | background-image: url("data:image/png;base64,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"); |
| | | background-size: contain; |
| | | } |
| | | |
| | | .icon-close:before { |
| | | content: " "; |
| | | display: block; |
| | | width: 16px; |
| | | height: 16px; |
| | | position: absolute; |
| | | margin: auto; |
| | | left: 0; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | z-index: 9999; |
| | | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADIEAYAAAD9yHLdAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAASAAAAEgARslrPgAADwRJREFUeNrt3V1sU+cZwPHndTAjwZ0mbZPKR/hKm0GqtiJJGZ9CIvMCawJoUksvOpC2XjSi4kMECaa2SO0qFEEhgFCQSqWOVWqJEGJJuyYYWCG9QCIOhQvYlgGCIFmatrVSUhzixO8ujNM1gSZOfPye857/7wYlfPg5xj5/n/fExyIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABATizsWti1sCs/v6y0rLSsdMaMZ/Y8s+eZPZMnm54LQO6kn/fp/UB6v2B6LrdRpgcwZf7e+Xvn7505MxAIBAKBrVt1ja7RNdXVaqlaqpbOmTP0z+u9eq/ee/euFEqhFH7ySeCjwEeBj+rr299of6P9jb//3fT2AMhcWVlZWVnZ3Ln6uD6uj2/eLF3SJV1VVapW1ara6dOH/nn9hf5Cf3HzpupW3aq7qSl5LHkseay+/nLt5drLtbdvm96eXPNZQJQqn1Q+qXzS73+vN+gNesObb0q7tEv7xImZ/kv6kr6kL/X3q0PqkDpUXx/aFNoU2rRz53l1Xp1X/f2mtxTAcMv1cr1cT5jQfb37evf1ujrpkR7p2bxZ1agaVZOXl/E/WCM1UnP/vv5cf64/f+utjg87Puz4cPfu1G9qbXp7neaTgChVeqD0QOmBP/5RHVPH1LHf/CbrN1EplVLZ2iqt0iqtv/51NBqNRqP37pnecgDpI42CgtTz9OTJ1PO0sjLbt6PX6/V6/Z/+1LG5Y3PH5g0bHnzX2pBkXlyPKTtadrTs6Ouvq/fV++r9LVscu6EbckNuPPGEhCUs4UWLpsanxqfGT5yIxWKxWCyRMH0/AH40GI6whCXc3Cyn5bScDoeduj11RV1RV559dkrFlIopFX19sauxq7GrbW2m7wenBEwP4JT0OY7UV6+/nrMbjkhEIitWSIVUSEVLS0ljSWNJYyhk+v4A/GQwHHtkj+xpahp8XuaImqwmq8m7di2oXlC9oHr2bNP3h1OsDUhgfWB9YP2WLdIgDdLwgx/kfICzclbOLluW35Hfkd/x5z8PPqABOGbYEcd22S7bKypyPsiDc6v9df11/XWvvWb6fnGKtQHRj+nH9GOrV5ueY/CVz4MHNCEBsm9YOHJ8xPEo6og6oo64YD/k1PaZHiDbvruD/uYb0/MMUyEVUtHWFi+Pl8fLf/Wray9ee/Haiz09pscCvGjYUpWpI44RBE8FTwVPFRRcLLxYeLEwHjc9T7ZYdwSi2lSbavvxj03P8UgsbQHj5pqlqlFK9iZ7k70u3i+NkXUB6Tvcd7jv8H//a3qOEXGyHciY6ZPjYzXw0sBLAy95YL+UIeuWsNJK75feL71/545arBarxYWFpucZUVjCEj53LvWEqK7mfSTAt9x6jmNEi2WxLL59O3ooeih6aNYs0+Nkm3VHIIO6pEu6Pv3U9Bijxsl2YBjPhiOtUAql0EP7oQxZG5C8SXmT8ibt35++5IjpeUaNpS3As0tVabpBN+iGgQE5Lsfl+KFDpudxirUBuTT90vRL0//xj/S1qkzPkzFOtsOHvHZy/FFUsSpWxfv2pZai//Y30/M4xfpLmRR/VvxZ8Wd//Wvf7b7bfbd//vPBS454xU25KTdnz+YSKbCZ55eq0h5cE2/OB3M+mPPBb3977dq1a9eu2XstLGtPog+Vvp5/X1tfW19bU5N6V72r3v3FL0zPlTHeRwKLeOV9HCPaLbtl94UL8a/jX8e/fv55vzwvfROQNEICmEc47OC7gKQREiD3CIddfBuQNEICOI9w2Mn3AUkjJED2EQ67EZAhCAkwfoTDHwjIIxASIHOEw18IyAgICTAywuFPBGSUCAkwHOHwNwKSIUICEA6kEJAxIiTwI8KB/0dAxomQwA8IBx6GgGQJIYGNCAe+DwHJMkICGxAOjAYBcQghgRcRDmSCgDiMkMALCAfGgoDkCCGBGxEOjAcByTFCAjcgHMgGAmIIIYEJhAPZREAMIyTIBcIBJxAQlyAkcALhgJMIiMsQEmQD4UAuEBCXIiQYC8KBXCIgLkdIMBqEAyYQEI8gJHgYwgGTCIjHEBKIEA64AwHxKELiT4QDbkJAPI6Q+APhgBsREEsQEjsRDrgZAbEMIbED4YAXEBBLERJvIhzwEgJiOULiDYQDXkRAfIKQuBPhgJcREJ8hJO5AOGADAuJThMQMwgGbEBCfIyS5QThgIwICESEkTiEcsBkBwXcQkuwgHPADAoKHIiRjQzjgJwQE34uQjA7hgB8REIwKIXk4wgE/IyDICCFJIRwAAcEY+TUkhAP4FgHBuPglJIQDGI6AICtsDUl+XX5dfl0ySTiA4QgIsmrwlXpYwhJubpaIRCSyYoXpuTIWlrCEz50b/Nrr2xGRiESqq6PRaDQavXfP9FiwAwGBI6w5IvEqjjiQAwQEjiIkOUY4kEMEBDlBSBxGOGAAAUFOEZIsIxwwiIDACEIyToQDLkBAYBQhyRDhgIsQELgCIRkB4YALERC4CiEZgnDAxQgIXMn3ISEc8AACAlfzXUgIBzyEgMATrA8J4YAHERB4inUhIRzwsIDpAYBMJNYm1ibWKqUeV4+rx5X3XwCdkTNyxoLtgC/xwIUnWPN5HI/i8Ge2A04gIHA168MxFCGBhxAQuJLvwjEUIYEHEBC4iu/DMRQhgYsRELgC4RgBIYELERAYRTgyREjgIgQERhCOcSIkcAECgpwiHFlGSGAQAUFOEA6HERIYQEDgKMKRY4QEOURA4AjCYRghQQ7kmR4AdhkMR1jCEm5uliNyRI54MBxhCUv43DkpkiIpunVLbspNuTl7tumxRu2W3JJbM2cGC4IFwYKFC6fGp8anxk+ciMVisVgskTA9HuzAxRSRFcOOOCISkciKFabnylj66ril8dJ46Zo1wY3BjcGNVVV6m96mt505Y3q8jKX/HyqkQipaWkoaSxpLGkMh02PBDixhYVysWaoa4bLq1lxGnqUtZBEBwZj4JRxDERLgWwQEGfFrOIYiJAABwSgRjocjJPAzAoLvRThGh5DAjwgIHopwjA0hgZ8QEHwH4cgOQgI/ICAQEcLhFEICmxEQnyMcuUFIYCMC4lOEwwxCApsQEJ8hHO5ASGADAuIThMOdCAm8jIBYjnB4AyGBFxEQSxEObyIk8BICYhnCYQdCAi8gIJYgHHYiJHAzAuJxhMMfCAnciIB4FOHwJ0ICNyEgHkM4IEJI4A4ExCMIBx6GkMAkAuJyhAOjQUhgAgFxKcKBsSAkyCUC4jKEA9lASJALBMQlCAecQEjgJAJiGOFALhASOIGAGEI4YAIhQTYRkBwjHHADQoJsICA5QjjgRoQE4xEwPYDtbAtH4kriSuIKT1BbXCy8WHixMB6fuGzisonLVq/W2/Q2ve3MGdNzZeysnJWzy5blt+e357f/5S8ljSWNJY2hkOmxbMcRiENsDcfV7Ve3X93+zTemx4IzOCJBJghIlhEO2ICQYDQISJYQDtiIkOD7EJBxIhzwA0KChyEgY0Q44EeEBP+PgGSIcACEBCkEZJQIBzAcIfE3AjICwgGMjJD4EwF5BMIBZI6Q+AsBGYJwAONHSPyBgDxAOIDsIyR2831ACAfgPEJiJ98GhHAAuUdI7OK7gBAOwDxCYgffBIRwAO5DSLzN+oAs18v1cj1hQk95T3lP+aefpr77y1+anitje2SP7Dl7NhW+1auj0Wg0Gr13z/RYQDYMvsALS1jCzc0SkYhEVqwwPVfGKqVSKltbQ++E3gm9U1V1Xp1X51V/v+mxnGL9B0p1X+++3n29ri71FeEA3GjwcR2RiESqq1MhOXfO9FwZa5VWaa2s7DnYc7Dn4O7dpsdxmrUBKX+7/O3yt3/2M5krc2Xupk2m58lYeqkqmogmomvWEA74QfpxHtwY3BjcWFXl1U9I1Iv0Ir1o69b53fO753fPm2d6HqdYG5BkXjIvmbd1q3pOPaeemzDB9Dyjlj7i2Ck7ZeeqVZzjgB+lP2o3dU5kzRqvHZGoGlWjavLyAg2BhkDDa6+Znscp1gZEzVQz1cyqKtNzjBpLVcAwnl/aOi7H5biH9kMZsi4gCzoXdC7o/OEPZZ/sk33TppmeZ0QsVQEj8vbS1owZJY0ljSWNoZDpSbLNuoAMrBtYN7DuRz8yPceIWKoCMubVpa3Q/ND80HwP7JcyZF1ARIkS9e9/mx7jkTjiAMbNa0ckgUmBSYFJ//mP6Tmyzdr3gZTGS+Ol8Rs31FK1VC2dM8f0POkjjuCTwSeDT1ZXp19JmR4LsIFr30eyQ3bIjs7O6AvRF6IvFBebHifb7DsCeUA1qAbV0Nxseg7CATjPrSfb9VP6Kf2UC/ZDDrE2IMlkMplM7t8vNVIjNffv53yAIUtVhANwnluWtvRhfVgf7u1VL6uX1csHDpi+X5xibUAu116uvVx7+3bqqz/8IWc3nD7imBecF5y3ciUnx4HcM36yPSlJSb71VrQj2hHtuHPH9P3hlDzTAzgt1hRrijW1tU3ZMWXHlB1z5qgr6oq68uyzWb+h/bJf9re0BIuCRcGitWs54gDMi8VisVgskZganxqfGj9xInWtqvJyuSE35MYTT2T79vRJfVKfPHas4+mOpzuerq01vf1Osz4gabGWWEus5dSpaV9N+2raV4mE7JJdsmvJEmmXdmnP/J3q+pK+pC/190undErn3r1FkaJIUeR3vzv9yulXTr/S12d6ewF8Kx2S4gvFF4ovfPxxX29fb19vQYE+qo/qowsWqPfUe+q9QMYrMumlKlklq2TVm29+Nxxam95up1n7U1gjKSstKy0rnTFDr9Qr9cotW1SLalEtq1enfgy4qOjhf+vOHVkn62TdJ58M3B24O3C3vv7Lg18e/PJgZ6fp7QGQufQ18/QpfUqf2rw59d3nn0/9OmPGsL+wRJbIkn/+U7+qX9WvNjUFZgVmBWbV17cXtBe0F3R1md6eXPNtQB4l/fkEiTWJNYk1P/1p+n0lvF8D8I/BHwvWokX/5CehaCgaiv7rX6nLs/f2mp4PAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtvsf2vlfs7i0WI4AAAAldEVYdGRhdGU6Y3JlYXRlADIwMTctMTItMTVUMTU6NTc6MjcrMDg6MDCiEb4vAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE3LTEyLTE1VDE1OjU3OjI3KzA4OjAw00wGkwAAAE10RVh0c3ZnOmJhc2UtdXJpAGZpbGU6Ly8vaG9tZS9hZG1pbi9pY29uLWZvbnQvdG1wL2ljb25fY2sxYnphMHpqOWpqZGN4ci9jbG9zZS5zdmdHkn2WAAAAAElFTkSuQmCC"); |
| | | background-size: contain; |
| | | } |
| | | |
| | | .icon-right:before { |
| | | content: " "; |
| | | display: block; |
| | | width: 16px; |
| | | height: 16px; |
| | | position: absolute; |
| | | margin: auto; |
| | | left: 0; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | background-size: cover; |
| | | z-index: 9999; |
| | | background-image: url("data:image/png;base64,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"); |
| | | background-size: contain; |
| | | } |
| | | |
| | | .icon-refresh:before { |
| | | content: " "; |
| | | display: block; |
| | | width: 16px; |
| | | height: 16px; |
| | | position: absolute; |
| | | margin: auto; |
| | | left: 0; |
| | | right: 0; |
| | | top: 0; |
| | | bottom: 0; |
| | | z-index: 9999; |
| | | background-image: url("data:image/png;base64,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"); |
| | | background-size: contain; |
| | | } |
| | | </style> |
New file |
| | |
| | | /* eslint-disable no-unused-vars */ |
| | | <template> |
| | | <div |
| | | style="position: relative" |
| | | > |
| | | <div class="verify-img-out"> |
| | | <div |
| | | :style="{'width': setSize.imgWidth, |
| | | 'height': setSize.imgHeight, |
| | | 'background-size' : setSize.imgWidth + ' '+ setSize.imgHeight, |
| | | 'margin-bottom': vSpace + 'px'}" |
| | | class="verify-img-panel" |
| | | > |
| | | <div @click="refresh" class="verify-refresh" style="z-index:3" v-show="showRefresh"> |
| | | <i class="iconfont icon-refresh"></i> |
| | | </div> |
| | | <img |
| | | :src="'data:image/png;base64,'+pointBackImgBase" |
| | | @click="bindingClick?canvasClick($event):undefined" |
| | | alt="" |
| | | ref="canvas" |
| | | style="width:100%;height:100%;display:block"> |
| | | |
| | | <div |
| | | :key="index" |
| | | :style="{ |
| | | 'background-color':'#1abd6c', |
| | | color:'#fff', |
| | | 'z-index':9999, |
| | | width:'20px', |
| | | height:'20px', |
| | | 'text-align':'center', |
| | | 'line-height':'20px', |
| | | 'border-radius': '50%', |
| | | position:'absolute', |
| | | top:parseInt(tempPoint.y-10) + 'px', |
| | | left:parseInt(tempPoint.x-10) + 'px' |
| | | }" |
| | | class="point-area" |
| | | v-for="(tempPoint, index) in tempPoints"> |
| | | {{ index + 1 }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <!-- 'height': this.barSize.height, --> |
| | | <div |
| | | :style="{'width': setSize.imgWidth, |
| | | 'color': this.barAreaColor, |
| | | 'border-color': this.barAreaBorderColor, |
| | | 'line-height':this.barSize.height}" |
| | | class="verify-bar-area"> |
| | | <span class="verify-msg">{{ text }}</span> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script type="text/babel"> |
| | | /** |
| | | * VerifyPoints |
| | | * @description 点选 |
| | | * */ |
| | | import { resetSize } from './../utils/util' |
| | | import { aesEncrypt } from './../utils/ase' |
| | | import { reqGet, reqCheck } from '@/api/modular/system/loginManage' |
| | | |
| | | export default { |
| | | name: 'VerifyPoints', |
| | | props: { |
| | | // 弹出式pop,固定fixed |
| | | mode: { |
| | | type: String, |
| | | default: 'fixed' |
| | | }, |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | captchaType: { |
| | | type: String |
| | | }, |
| | | // 间隔 |
| | | vSpace: { |
| | | type: Number, |
| | | default: 5 |
| | | }, |
| | | imgSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '310px', |
| | | height: '155px' |
| | | } |
| | | } |
| | | }, |
| | | barSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '310px', |
| | | height: '40px' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | secretKey: '', // 后端返回的ase加密秘钥 |
| | | checkNum: 3, // 默认需要点击的字数 |
| | | fontPos: [], // 选中的坐标信息 |
| | | checkPosArr: [], // 用户点击的坐标 |
| | | num: 1, // 点击的记数 |
| | | pointBackImgBase: '', // 后端获取到的背景图片 |
| | | poinTextList: [], // 后端返回的点击字体顺序 |
| | | backToken: '', // 后端返回的token值 |
| | | setSize: { |
| | | imgHeight: 0, |
| | | imgWidth: 0, |
| | | barHeight: 0, |
| | | barWidth: 0 |
| | | }, |
| | | tempPoints: [], |
| | | text: '', |
| | | barAreaColor: undefined, |
| | | barAreaBorderColor: undefined, |
| | | showRefresh: true, |
| | | bindingClick: true |
| | | } |
| | | }, |
| | | computed: { |
| | | resetSize() { |
| | | return resetSize |
| | | } |
| | | }, |
| | | methods: { |
| | | init() { |
| | | // 加载页面 |
| | | this.fontPos.splice(0, this.fontPos.length) |
| | | this.checkPosArr.splice(0, this.checkPosArr.length) |
| | | this.num = 1 |
| | | this.getPictrue() |
| | | this.$nextTick(() => { |
| | | this.setSize = this.resetSize(this) // 重新设置宽度高度 |
| | | this.$parent.$emit('ready', this) |
| | | }) |
| | | }, |
| | | canvasClick(e) { |
| | | this.checkPosArr.push(this.getMousePos(this.$refs.canvas, e)) |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.num == this.checkNum) { |
| | | this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e)) |
| | | // 按比例转换坐标值 |
| | | this.checkPosArr = this.pointTransfrom(this.checkPosArr, this.setSize) |
| | | // 等创建坐标执行完 |
| | | setTimeout(() => { |
| | | // var flag = this.comparePos(this.fontPos, this.checkPosArr); |
| | | // 发送后端请求 |
| | | var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify(this.checkPosArr), this.secretKey) : this.backToken + '---' + JSON.stringify(this.checkPosArr) |
| | | const data = { |
| | | captchaType: this.captchaType, |
| | | 'pointJson': this.secretKey ? aesEncrypt(JSON.stringify(this.checkPosArr), this.secretKey) : JSON.stringify(this.checkPosArr), |
| | | 'token': this.backToken |
| | | } |
| | | reqCheck(data).then(res => { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (res.repCode == '0000') { |
| | | this.barAreaColor = '#4cae4c' |
| | | this.barAreaBorderColor = '#5cb85c' |
| | | this.text = '验证成功' |
| | | this.bindingClick = false |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.mode == 'pop') { |
| | | setTimeout(() => { |
| | | this.$parent.clickShow = false |
| | | this.refresh() |
| | | }, 1500) |
| | | } |
| | | this.$parent.$emit('success', { captchaVerification }) |
| | | } else { |
| | | this.$parent.$emit('error', this) |
| | | this.barAreaColor = '#d9534f' |
| | | this.barAreaBorderColor = '#d9534f' |
| | | this.text = '验证失败' |
| | | setTimeout(() => { |
| | | this.refresh() |
| | | }, 700) |
| | | } |
| | | }) |
| | | }, 400) |
| | | } |
| | | if (this.num < this.checkNum) { |
| | | this.num = this.createPoint(this.getMousePos(this.$refs.canvas, e)) |
| | | } |
| | | }, |
| | | |
| | | // 获取坐标 |
| | | getMousePos: function (obj, e) { |
| | | var x = e.offsetX |
| | | var y = e.offsetY |
| | | return { x, y } |
| | | }, |
| | | // 创建坐标点 |
| | | createPoint: function (pos) { |
| | | this.tempPoints.push(Object.assign({}, pos)) |
| | | return ++this.num |
| | | }, |
| | | refresh: function () { |
| | | this.tempPoints.splice(0, this.tempPoints.length) |
| | | this.barAreaColor = '#000' |
| | | this.barAreaBorderColor = '#ddd' |
| | | this.bindingClick = true |
| | | this.fontPos.splice(0, this.fontPos.length) |
| | | this.checkPosArr.splice(0, this.checkPosArr.length) |
| | | this.num = 1 |
| | | this.getPictrue() |
| | | this.text = '验证失败' |
| | | this.showRefresh = true |
| | | }, |
| | | |
| | | // 请求背景图片和验证图片 |
| | | getPictrue() { |
| | | const data = { |
| | | captchaType: this.captchaType |
| | | } |
| | | reqGet(data).then(res => { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (res.repCode == '0000') { |
| | | this.pointBackImgBase = res.repData.originalImageBase64 |
| | | this.backToken = res.repData.token |
| | | this.secretKey = res.repData.secretKey |
| | | this.poinTextList = res.repData.wordList |
| | | this.text = '请依次点击【' + this.poinTextList.join(',') + '】' |
| | | } else { |
| | | this.text = res.repMsg |
| | | } |
| | | }) |
| | | }, |
| | | // 坐标转换函数 |
| | | pointTransfrom(pointArr, imgSize) { |
| | | var newPointArr = pointArr.map(p => { |
| | | const x = Math.round(310 * p.x / parseInt(imgSize.imgWidth)) |
| | | const y = Math.round(155 * p.y / parseInt(imgSize.imgHeight)) |
| | | return { x, y } |
| | | }) |
| | | // console.log(newPointArr,"newPointArr"); |
| | | return newPointArr |
| | | } |
| | | }, |
| | | watch: { |
| | | // type变化则全面刷新 |
| | | type: { |
| | | immediate: true, |
| | | handler() { |
| | | this.init() |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | // 禁止拖拽 |
| | | this.$el.onselectstart = function () { |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div style="position: relative;"> |
| | | <div |
| | | :style="{height: (parseInt(setSize.imgHeight) + vSpace) + 'px'}" |
| | | class="verify-img-out" |
| | | v-if="type === '2'" |
| | | > |
| | | <div |
| | | :style="{width: setSize.imgWidth, |
| | | height: setSize.imgHeight,}" |
| | | class="verify-img-panel"> |
| | | <img :src="'data:image/png;base64,'+backImgBase" alt="" style="width:100%;height:100%;display:block"> |
| | | <div @click="refresh" class="verify-refresh" v-show="showRefresh"><i class="iconfont icon-refresh"></i> |
| | | </div> |
| | | <transition name="tips"> |
| | | <span :class="passFlag ?'suc-bg':'err-bg'" class="verify-tips" v-if="tipWords">{{ tipWords }}</span> |
| | | </transition> |
| | | </div> |
| | | </div> |
| | | <!-- 公共部分 --> |
| | | <div |
| | | :style="{width: setSize.imgWidth, |
| | | height: barSize.height, |
| | | 'line-height':barSize.height}" |
| | | class="verify-bar-area"> |
| | | <span class="verify-msg" v-text="text"></span> |
| | | <div |
| | | :style="{width: (leftBarWidth!==undefined)?leftBarWidth: barSize.height, height: barSize.height, 'border-color': leftBarBorderColor, transaction: transitionWidth}" |
| | | class="verify-left-bar"> |
| | | <span class="verify-msg" v-text="finishText"></span> |
| | | <div |
| | | :style="{width: barSize.height, height: barSize.height, 'background-color': moveBlockBackgroundColor, left: moveBlockLeft, transition: transitionLeft}" |
| | | @mousedown="start" |
| | | @touchstart="start" |
| | | class="verify-move-block"> |
| | | <i |
| | | :class="['verify-icon iconfont', iconClass]" |
| | | :style="{color: iconColor}"></i> |
| | | <div |
| | | :style="{'width':Math.floor(parseInt(setSize.imgWidth)*47/310)+ 'px', |
| | | 'height': setSize.imgHeight, |
| | | 'top':'-' + (parseInt(setSize.imgHeight) + vSpace) + 'px', |
| | | 'background-size': setSize.imgWidth + ' ' + setSize.imgHeight, |
| | | }" |
| | | class="verify-sub-block" |
| | | v-if="type === '2'"> |
| | | <img :src="'data:image/png;base64,'+blockBackImgBase" alt="" style="width:100%;height:100%;display:block"> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script type="text/babel"> |
| | | /** |
| | | * VerifySlide |
| | | * @description 滑块 |
| | | * */ |
| | | import { aesEncrypt } from './../utils/ase' |
| | | import { resetSize } from './../utils/util' |
| | | import { reqGet, reqCheck } from '@/api/modular/system/loginManage' |
| | | |
| | | // "captchaType":"blockPuzzle", |
| | | export default { |
| | | name: 'VerifySlide', |
| | | props: { |
| | | // eslint-disable-next-line vue/require-default-prop |
| | | captchaType: { |
| | | type: String |
| | | }, |
| | | type: { |
| | | type: String, |
| | | default: '1' |
| | | }, |
| | | // 弹出式pop,固定fixed |
| | | mode: { |
| | | type: String, |
| | | default: 'fixed' |
| | | }, |
| | | vSpace: { |
| | | type: Number, |
| | | default: 5 |
| | | }, |
| | | explain: { |
| | | type: String, |
| | | default: '向右滑动完成验证' |
| | | }, |
| | | imgSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '310px', |
| | | height: '155px' |
| | | } |
| | | } |
| | | }, |
| | | blockSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '50px', |
| | | height: '50px' |
| | | } |
| | | } |
| | | }, |
| | | barSize: { |
| | | type: Object, |
| | | default() { |
| | | return { |
| | | width: '310px', |
| | | height: '40px' |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | secretKey: '', // 后端返回的加密秘钥 字段 |
| | | passFlag: '', // 是否通过的标识 |
| | | backImgBase: '', // 验证码背景图片 |
| | | blockBackImgBase: '', // 验证滑块的背景图片 |
| | | backToken: '', // 后端返回的唯一token值 |
| | | startMoveTime: '', // 移动开始的时间 |
| | | endMovetime: '', // 移动结束的时间 |
| | | tipsBackColor: '', // 提示词的背景颜色 |
| | | tipWords: '', |
| | | text: '', |
| | | finishText: '', |
| | | setSize: { |
| | | imgHeight: 0, |
| | | imgWidth: 0, |
| | | barHeight: 0, |
| | | barWidth: 0 |
| | | }, |
| | | top: 0, |
| | | left: 0, |
| | | moveBlockLeft: undefined, |
| | | leftBarWidth: undefined, |
| | | // 移动中样式 |
| | | moveBlockBackgroundColor: undefined, |
| | | leftBarBorderColor: '#ddd', |
| | | iconColor: undefined, |
| | | iconClass: 'icon-right', |
| | | status: false, // 鼠标状态 |
| | | isEnd: false, // 是够验证完成 |
| | | showRefresh: true, |
| | | transitionLeft: '', |
| | | transitionWidth: '' |
| | | } |
| | | }, |
| | | computed: { |
| | | barArea() { |
| | | return this.$el.querySelector('.verify-bar-area') |
| | | }, |
| | | resetSize() { |
| | | return resetSize |
| | | } |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.text = this.explain |
| | | this.getPictrue() |
| | | this.$nextTick(() => { |
| | | const setSize = this.resetSize(this) // 重新设置宽度高度 |
| | | for (const key in setSize) { |
| | | this.$set(this.setSize, key, setSize[key]) |
| | | } |
| | | this.$parent.$emit('ready', this) |
| | | }) |
| | | |
| | | var _this = this |
| | | |
| | | window.removeEventListener('touchmove', function (e) { |
| | | _this.move(e) |
| | | }) |
| | | window.removeEventListener('mousemove', function (e) { |
| | | _this.move(e) |
| | | }) |
| | | |
| | | // 鼠标松开 |
| | | window.removeEventListener('touchend', function () { |
| | | _this.end() |
| | | }) |
| | | window.removeEventListener('mouseup', function () { |
| | | _this.end() |
| | | }) |
| | | |
| | | window.addEventListener('touchmove', function (e) { |
| | | _this.move(e) |
| | | }) |
| | | window.addEventListener('mousemove', function (e) { |
| | | _this.move(e) |
| | | }) |
| | | |
| | | // 鼠标松开 |
| | | window.addEventListener('touchend', function () { |
| | | _this.end() |
| | | }) |
| | | window.addEventListener('mouseup', function () { |
| | | _this.end() |
| | | }) |
| | | }, |
| | | |
| | | // 鼠标按下 |
| | | start: function (e) { |
| | | e = e || window.event |
| | | if (!e.touches) { // 兼容PC端 |
| | | var x = e.clientX |
| | | } else { // 兼容移动端 |
| | | // eslint-disable-next-line no-redeclare |
| | | var x = e.touches[0].pageX |
| | | } |
| | | this.startLeft = Math.floor(x - this.barArea.getBoundingClientRect().left) |
| | | this.startMoveTime = +new Date() // 开始滑动的时间 |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.isEnd == false) { |
| | | this.text = '' |
| | | this.moveBlockBackgroundColor = '#337ab7' |
| | | this.leftBarBorderColor = '#337AB7' |
| | | this.iconColor = '#fff' |
| | | e.stopPropagation() |
| | | this.status = true |
| | | } |
| | | }, |
| | | // 鼠标移动 |
| | | move: function (e) { |
| | | e = e || window.event |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.status && this.isEnd == false) { |
| | | if (!e.touches) { // 兼容PC端 |
| | | var x = e.clientX |
| | | } else { // 兼容移动端 |
| | | // eslint-disable-next-line no-redeclare |
| | | var x = e.touches[0].pageX |
| | | } |
| | | // eslint-disable-next-line camelcase |
| | | var bar_area_left = this.barArea.getBoundingClientRect().left |
| | | // eslint-disable-next-line camelcase |
| | | var move_block_left = x - bar_area_left // 小方块相对于父元素的left值 |
| | | // eslint-disable-next-line camelcase |
| | | if (move_block_left >= this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2) { |
| | | // eslint-disable-next-line camelcase |
| | | move_block_left = this.barArea.offsetWidth - parseInt(parseInt(this.blockSize.width) / 2) - 2 |
| | | } |
| | | // eslint-disable-next-line camelcase |
| | | if (move_block_left <= 0) { |
| | | // eslint-disable-next-line camelcase |
| | | move_block_left = parseInt(parseInt(this.blockSize.width) / 2) |
| | | } |
| | | // 拖动后小方块的left值 |
| | | // eslint-disable-next-line camelcase |
| | | this.moveBlockLeft = (move_block_left - this.startLeft) + 'px' |
| | | // eslint-disable-next-line camelcase |
| | | this.leftBarWidth = (move_block_left - this.startLeft) + 'px' |
| | | } |
| | | }, |
| | | |
| | | // 鼠标松开 |
| | | end: function () { |
| | | this.endMovetime = +new Date() |
| | | var _this = this |
| | | // 判断是否重合 |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.status && this.isEnd == false) { |
| | | var moveLeftDistance = parseInt((this.moveBlockLeft || '').replace('px', '')) |
| | | moveLeftDistance = moveLeftDistance * 310 / parseInt(this.setSize.imgWidth) |
| | | const data = { |
| | | captchaType: this.captchaType, |
| | | 'pointJson': this.secretKey ? aesEncrypt(JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : JSON.stringify({ x: moveLeftDistance, y: 5.0 }), |
| | | 'token': this.backToken |
| | | } |
| | | reqCheck(data).then(res => { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (res.repCode == '0000') { |
| | | this.moveBlockBackgroundColor = '#5cb85c' |
| | | this.leftBarBorderColor = '#5cb85c' |
| | | this.iconColor = '#fff' |
| | | this.iconClass = 'icon-check' |
| | | this.showRefresh = false |
| | | this.isEnd = true |
| | | // eslint-disable-next-line eqeqeq |
| | | if (this.mode == 'pop') { |
| | | setTimeout(() => { |
| | | this.$parent.clickShow = false |
| | | this.refresh() |
| | | }, 1500) |
| | | } |
| | | this.passFlag = true |
| | | this.tipWords = `${((this.endMovetime - this.startMoveTime) / 1000).toFixed(2)}s验证成功` |
| | | var captchaVerification = this.secretKey ? aesEncrypt(this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }), this.secretKey) : this.backToken + '---' + JSON.stringify({ x: moveLeftDistance, y: 5.0 }) |
| | | setTimeout(() => { |
| | | this.tipWords = '' |
| | | this.$parent.closeBox() |
| | | this.$parent.$emit('success', { captchaVerification }) |
| | | }, 1000) |
| | | } else { |
| | | this.moveBlockBackgroundColor = '#d9534f' |
| | | this.leftBarBorderColor = '#d9534f' |
| | | this.iconColor = '#fff' |
| | | this.iconClass = 'icon-close' |
| | | this.passFlag = false |
| | | setTimeout(function () { |
| | | _this.refresh() |
| | | }, 1000) |
| | | this.$parent.$emit('error', this) |
| | | this.tipWords = '验证失败' |
| | | setTimeout(() => { |
| | | this.tipWords = '' |
| | | }, 1000) |
| | | } |
| | | }) |
| | | this.status = false |
| | | } |
| | | }, |
| | | |
| | | refresh: function () { |
| | | this.showRefresh = true |
| | | this.finishText = '' |
| | | |
| | | this.transitionLeft = 'left .3s' |
| | | this.moveBlockLeft = 0 |
| | | |
| | | this.leftBarWidth = undefined |
| | | this.transitionWidth = 'width .3s' |
| | | |
| | | this.leftBarBorderColor = '#ddd' |
| | | this.moveBlockBackgroundColor = '#fff' |
| | | this.iconColor = '#000' |
| | | this.iconClass = 'icon-right' |
| | | this.isEnd = false |
| | | |
| | | this.getPictrue() |
| | | setTimeout(() => { |
| | | this.transitionWidth = '' |
| | | this.transitionLeft = '' |
| | | this.text = this.explain |
| | | }, 300) |
| | | }, |
| | | |
| | | // 请求背景图片和验证图片 |
| | | getPictrue() { |
| | | const data = { |
| | | captchaType: this.captchaType |
| | | } |
| | | reqGet(data).then(res => { |
| | | // eslint-disable-next-line eqeqeq |
| | | if (res.repCode == '0000') { |
| | | this.backImgBase = res.repData.originalImageBase64 |
| | | this.blockBackImgBase = res.repData.jigsawImageBase64 |
| | | this.backToken = res.repData.token |
| | | this.secretKey = res.repData.secretKey |
| | | } else { |
| | | this.tipWords = res.repMsg |
| | | } |
| | | }) |
| | | } |
| | | }, |
| | | watch: { |
| | | // type变化则全面刷新 |
| | | type: { |
| | | immediate: true, |
| | | handler() { |
| | | this.init() |
| | | } |
| | | } |
| | | }, |
| | | mounted() { |
| | | // 禁止拖拽 |
| | | this.$el.onselectstart = function () { |
| | | return false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | import CryptoJS from 'crypto-js' |
| | | /** |
| | | * @word 要加密的内容 |
| | | * @keyWord String 服务器随机返回的关键字 |
| | | */ |
| | | export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') { |
| | | var key = CryptoJS.enc.Utf8.parse(keyWord) |
| | | var srcs = CryptoJS.enc.Utf8.parse(word) |
| | | var encrypted = CryptoJS.AES.encrypt(srcs, key, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.Pkcs7 }) |
| | | return encrypted.toString() |
| | | } |
New file |
| | |
| | | import axios from 'axios' |
| | | |
| | | axios.defaults.baseURL = process.env.BASE_API |
| | | |
| | | const service = axios.create({ |
| | | timeout: 40000, |
| | | headers: { |
| | | 'X-Requested-With': 'XMLHttpRequest', |
| | | 'Content-Type': 'application/json; charset=UTF-8' |
| | | } |
| | | }) |
| | | service.interceptors.request.use( |
| | | config => { |
| | | return config |
| | | }, |
| | | error => { |
| | | Promise.reject(error) |
| | | } |
| | | ) |
| | | |
| | | // response interceptor |
| | | service.interceptors.response.use( |
| | | response => { |
| | | const res = response.data |
| | | return res |
| | | }, |
| | | () => { |
| | | } |
| | | ) |
| | | export default service |
New file |
| | |
| | | export function resetSize(vm) { |
| | | // eslint-disable-next-line camelcase |
| | | var img_width, img_height, bar_width, bar_height // 图片的宽度、高度,移动条的宽度、高度 |
| | | |
| | | var parentWidth = vm.$el.parentNode.offsetWidth || window.offsetWidth |
| | | var parentHeight = vm.$el.parentNode.offsetHeight || window.offsetHeight |
| | | |
| | | // eslint-disable-next-line eqeqeq |
| | | if (vm.imgSize.width.indexOf('%') != -1) { |
| | | // eslint-disable-next-line camelcase |
| | | img_width = parseInt(this.imgSize.width) / 100 * parentWidth + 'px' |
| | | } else { |
| | | // eslint-disable-next-line camelcase |
| | | img_width = this.imgSize.width |
| | | } |
| | | |
| | | // eslint-disable-next-line eqeqeq |
| | | if (vm.imgSize.height.indexOf('%') != -1) { |
| | | // eslint-disable-next-line camelcase |
| | | img_height = parseInt(this.imgSize.height) / 100 * parentHeight + 'px' |
| | | } else { |
| | | // eslint-disable-next-line camelcase |
| | | img_height = this.imgSize.height |
| | | } |
| | | |
| | | // eslint-disable-next-line eqeqeq |
| | | if (vm.barSize.width.indexOf('%') != -1) { |
| | | // eslint-disable-next-line camelcase |
| | | bar_width = parseInt(this.barSize.width) / 100 * parentWidth + 'px' |
| | | } else { |
| | | // eslint-disable-next-line camelcase |
| | | bar_width = this.barSize.width |
| | | } |
| | | |
| | | // eslint-disable-next-line eqeqeq |
| | | if (vm.barSize.height.indexOf('%') != -1) { |
| | | // eslint-disable-next-line camelcase |
| | | bar_height = parseInt(this.barSize.height) / 100 * parentHeight + 'px' |
| | | } else { |
| | | // eslint-disable-next-line camelcase |
| | | bar_height = this.barSize.height |
| | | } |
| | | |
| | | return { imgWidth: img_width, imgHeight: img_height, barWidth: bar_width, barHeight: bar_height } |
| | | } |
| | | |
| | | // eslint-disable-next-line camelcase |
| | | export const _code_chars = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] |
| | | // eslint-disable-next-line camelcase |
| | | export const _code_color1 = ['#fffff0', '#f0ffff', '#f0fff0', '#fff0f0'] |
| | | // eslint-disable-next-line camelcase |
| | | export const _code_color2 = ['#FF0033', '#006699', '#993366', '#FF9900', '#66CC66', '#FF33CC'] |
New file |
| | |
| | | /* eslint-disable no-undef */ |
| | | <!--onlyoffice 编辑器--> |
| | | <template> |
| | | <div id="editorDiv"></div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { handleDocType } from '@/utils/onlyofficeUtil' |
| | | |
| | | export default { |
| | | name: 'Editor', |
| | | props: { |
| | | option: { |
| | | type: Object, |
| | | default: () => { |
| | | return {} |
| | | } |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | doctype: '' |
| | | } |
| | | }, |
| | | mounted() { |
| | | if (this.option.url) { |
| | | this.setEditor(this.option) |
| | | } |
| | | }, |
| | | methods: { |
| | | setEditor(option) { |
| | | this.doctype = handleDocType(option.fileType) |
| | | const config = { |
| | | document: { |
| | | fileType: option.fileType, |
| | | key: option.key, |
| | | title: option.title, |
| | | permissions: { |
| | | comment: true, |
| | | download: true, |
| | | modifyContentControl: true, |
| | | modifyFilter: true, |
| | | print: false, |
| | | edit: option.isEdit, |
| | | fillForms: true |
| | | // review: false |
| | | }, |
| | | url: option.url |
| | | |
| | | }, |
| | | type: option.type, |
| | | documentType: this.doctype, |
| | | editorConfig: { |
| | | callbackUrl: option.callbackUrl, |
| | | lang: 'zh', |
| | | customization: { |
| | | commentAuthorOnly: false, |
| | | comments: true, |
| | | compactHeader: false, |
| | | compactToolbar: true, |
| | | feedback: false, |
| | | plugins: true |
| | | }, |
| | | user: { |
| | | id: option.user.id, |
| | | name: option.user.name |
| | | } |
| | | // mode: option.mode |
| | | }, |
| | | width: '100%', |
| | | height: '100%', |
| | | position: 'absolute', |
| | | token: option.token |
| | | } |
| | | // eslint-disable-next-line no-unused-vars |
| | | let docEditor = null |
| | | // eslint-disable-next-line no-undef |
| | | docEditor = new DocsAPI.DocEditor('editorDiv', config) |
| | | } |
| | | }, |
| | | watch: { |
| | | option: { |
| | | handler: function (n, o) { |
| | | this.setEditor(n) |
| | | this.doctype = handleDocType(n.fileType) |
| | | }, |
| | | deep: true |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-card :bordered="false" :bodyStyle="tstyle"> |
| | | <slot name="content"></slot> |
| | | </a-card> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'XCard', |
| | | data() { |
| | | return { |
| | | tstyle: { 'padding-bottom': '0px', 'margin-bottom': '10px' } |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-tooltip placement="top"> |
| | | <template slot="title"> |
| | | <span>导出所有数据</span> |
| | | </template> |
| | | <!-- 正常来说,这里加个插槽最好了,但是这个就是为导出准备的,一般这两个字不会变 --> |
| | | <a-button type="dashed" @click="batchExport" :loading="batchExportLoading"><a-icon type="export"/>导出</a-button> |
| | | </a-tooltip> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'XDown', |
| | | data () { |
| | | return { |
| | | batchExportLoading: false |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 本控件中点击按钮事件 |
| | | */ |
| | | batchExport () { |
| | | this.batchExportLoading = true |
| | | // 将其传达到上个界面 |
| | | this.$emit('batchExport', '') |
| | | }, |
| | | |
| | | /** |
| | | * 通过返回的元素通过浏览器下载 |
| | | * 也就是接受使用这个组件的地方吧下载的内容传过来, |
| | | * 但是这个组件本公子编写的时候只想的适用于导出excel来使用,下载文件呀图片方面的重新整个组件即可 |
| | | */ |
| | | downloadfile (res) { |
| | | this.batchExportLoading = false |
| | | var blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' }) |
| | | var contentDisposition = res.headers['content-disposition'] |
| | | var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') |
| | | var result = patt.exec(contentDisposition) |
| | | var filename = result[1] |
| | | var downloadElement = document.createElement('a') |
| | | var href = window.URL.createObjectURL(blob) // 创建下载的链接 |
| | | var reg = /^["](.*)["]$/g |
| | | downloadElement.style.display = 'none' |
| | | downloadElement.href = href |
| | | downloadElement.download = decodeURI(filename.replace(reg, '$1')) // 下载后文件名 |
| | | document.body.appendChild(downloadElement) |
| | | downloadElement.click() // 点击下载 |
| | | document.body.removeChild(downloadElement) // 下载完成移除元素 |
| | | window.URL.revokeObjectURL(href) |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | /** |
| | | * 项目默认配置项 |
| | | * primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage |
| | | * navTheme - sidebar theme ['dark', 'light'] 两种主题 |
| | | * colorWeak - 色盲模式 |
| | | * layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局 |
| | | * fixedHeader - 固定 Header : boolean |
| | | * fixSiderbar - 固定左侧菜单栏 : boolean |
| | | * autoHideHeader - 向下滚动时,隐藏 Header : boolean |
| | | * contentWidth - 内容区布局: 流式 | 固定 |
| | | * |
| | | * storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage) |
| | | * production: 变量暂先设定为 false,目的是各种环境都正常显示设置抽屉,真实环境请放开注释 |
| | | * |
| | | * |
| | | */ |
| | | |
| | | export default { |
| | | primaryColor: '#1890ff', // primary color of ant design |
| | | navTheme: 'dark', // theme for nav menu |
| | | layout: 'sidemenu', // nav menu position: sidemenu or topmenu |
| | | contentWidth: 'Fixed', // layout of content: Fluid or Fixed, only works when layout is topmenu |
| | | fixedHeader: false, // sticky header |
| | | fixSiderbar: false, // sticky siderbar |
| | | autoHideHeader: false, // auto hide header |
| | | colorWeak: false, |
| | | multiTab: false, |
| | | production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true', |
| | | // vue-ls options |
| | | storageOptions: { |
| | | namespace: 'pro__', // key prefix |
| | | name: 'ls', // name variable Vue.[ls] or this.[$ls], |
| | | storage: 'local' // storage name session, local, memory |
| | | } |
| | | } |
New file |
| | |
| | | // eslint-disable-next-line |
| | | import { UserLayout, BasicLayout, RouteView, BlankLayout, PageView } from '@/layouts' |
| | | import { bxAnaalyse } from '@/core/icons' |
| | | |
| | | export const asyncRouterMap = [ |
| | | |
| | | { |
| | | path: '/', |
| | | name: 'MenuIndex.vue', |
| | | component: BasicLayout, |
| | | meta: { title: '首页' }, |
| | | redirect: '/dashboard/workplace', |
| | | children: [ |
| | | // dashboard |
| | | { |
| | | path: 'dashboard', |
| | | name: 'dashboard', |
| | | redirect: '/dashboard/workplace', |
| | | component: RouteView, |
| | | // eslint-disable-next-line standard/object-curly-even-spacing |
| | | meta: { title: '仪表盘', keepAlive: true, icon: bxAnaalyse /* permission: [ 'dashboard' ] */ }, |
| | | children: [ |
| | | { |
| | | path: 'analysis/:pageNo([1-9]\\d*)?', |
| | | name: 'Analysis', |
| | | component: () => import('@/views/system/dashboard/Analysis'), |
| | | // eslint-disable-next-line standard/object-curly-even-spacing |
| | | meta: { title: '分析页', keepAlive: true /* permission: [ 'dashboard' ] */ } |
| | | }, |
| | | { |
| | | path: 'workplace', |
| | | name: 'Workplace', |
| | | component: () => import('@/views/system/dashboard/Workplace'), |
| | | // eslint-disable-next-line standard/object-curly-even-spacing |
| | | meta: { title: '工作台', keepAlive: false/*, permission: [ 'dashboard' ] */ } |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | path: '*', redirect: '/404', hidden: true |
| | | } |
| | | ] |
| | | |
| | | /** |
| | | * 基础路由 |
| | | * @type { *[] } |
| | | */ |
| | | export const constantRouterMap = [ |
| | | { |
| | | path: '/user', |
| | | component: UserLayout, |
| | | redirect: '/user/login', |
| | | hidden: true, |
| | | children: [ |
| | | { |
| | | path: 'login', |
| | | name: 'login', |
| | | component: () => import(/* webpackChunkName: "user" */ '@/views/userLoginReg/Login') |
| | | }, |
| | | { |
| | | path: 'register', |
| | | name: 'register', |
| | | component: () => import(/* webpackChunkName: "user" */ '@/views/userLoginReg/Register') |
| | | }, |
| | | { |
| | | path: 'register-result', |
| | | name: 'registerResult', |
| | | component: () => import(/* webpackChunkName: "user" */ '@/views/userLoginReg/RegisterResult') |
| | | }, |
| | | { |
| | | path: 'recover', |
| | | name: 'recover', |
| | | component: undefined |
| | | } |
| | | ] |
| | | }, |
| | | |
| | | { |
| | | path: '/404', |
| | | component: () => import(/* webpackChunkName: "fail" */ '@/views/system/exception/404') |
| | | } |
| | | |
| | | ] |
New file |
| | |
| | | import Vue from 'vue' |
| | | import store from '@/store/' |
| | | import { |
| | | ACCESS_TOKEN, |
| | | DEFAULT_COLOR, |
| | | DEFAULT_THEME, |
| | | DEFAULT_LAYOUT_MODE, |
| | | DEFAULT_COLOR_WEAK, |
| | | SIDEBAR_TYPE, |
| | | DEFAULT_FIXED_HEADER, |
| | | DEFAULT_FIXED_HEADER_HIDDEN, |
| | | DEFAULT_FIXED_SIDEMENU, |
| | | DEFAULT_CONTENT_WIDTH_TYPE, |
| | | DEFAULT_MULTI_TAB |
| | | } from '@/store/mutation-types' |
| | | |
| | | import config from '@/config/defaultSettings' |
| | | |
| | | export default function Initializer () { |
| | | store.commit('SET_SIDEBAR_TYPE', Vue.ls.get(SIDEBAR_TYPE, true)) |
| | | store.commit('TOGGLE_THEME', Vue.ls.get(DEFAULT_THEME, config.navTheme)) |
| | | store.commit('TOGGLE_LAYOUT_MODE', Vue.ls.get(DEFAULT_LAYOUT_MODE, config.layout)) |
| | | store.commit('TOGGLE_FIXED_HEADER', Vue.ls.get(DEFAULT_FIXED_HEADER, config.fixedHeader)) |
| | | store.commit('TOGGLE_FIXED_SIDERBAR', Vue.ls.get(DEFAULT_FIXED_SIDEMENU, config.fixSiderbar)) |
| | | store.commit('TOGGLE_CONTENT_WIDTH', Vue.ls.get(DEFAULT_CONTENT_WIDTH_TYPE, config.contentWidth)) |
| | | store.commit('TOGGLE_FIXED_HEADER_HIDDEN', Vue.ls.get(DEFAULT_FIXED_HEADER_HIDDEN, config.autoHideHeader)) |
| | | store.commit('TOGGLE_WEAK', Vue.ls.get(DEFAULT_COLOR_WEAK, config.colorWeak)) |
| | | store.commit('TOGGLE_COLOR', Vue.ls.get(DEFAULT_COLOR, config.primaryColor)) |
| | | store.commit('TOGGLE_MULTI_TAB', Vue.ls.get(DEFAULT_MULTI_TAB, config.multiTab)) |
| | | store.commit('SET_TOKEN', Vue.ls.get(ACCESS_TOKEN)) |
| | | } |
New file |
| | |
| | | import Vue from 'vue' |
| | | import store from '@/store' |
| | | |
| | | /** |
| | | * Action 权限指令 |
| | | * 指令用法: |
| | | * - 在需要控制 action 级别权限的组件上使用 v-action:[method] , 如下: |
| | | * <i-button v-action:add >添加用户</a-button> |
| | | * <a-button v-action:delete>删除用户</a-button> |
| | | * <a v-action:edit @click="edit(record)">修改</a> |
| | | * |
| | | * - 当前用户没有权限时,组件上使用了该指令则会被隐藏 |
| | | * - 当后台权限跟 pro 提供的模式不同时,只需要针对这里的权限过滤进行修改即可 |
| | | * |
| | | * @see https://github.com/sendya/ant-design-pro-vue/pull/53 |
| | | */ |
| | | const action = Vue.directive('action', { |
| | | inserted: function (el, binding, vnode) { |
| | | const actionName = binding.arg |
| | | const roles = store.getters.roles |
| | | const elVal = vnode.context.$route.meta.permission |
| | | const permissionId = elVal instanceof String && [elVal] || elVal |
| | | roles.permissions.forEach(p => { |
| | | if (!permissionId.includes(p.permissionId)) { |
| | | return |
| | | } |
| | | if (p.actionList && !p.actionList.includes(actionName)) { |
| | | el.parentNode && el.parentNode.removeChild(el) || (el.style.display = 'none') |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | |
| | | export default action |
New file |
| | |
| | | /** |
| | | * Custom icon list |
| | | * All icons are loaded here for easy management |
| | | * @see https://vue.ant.design/components/icon/#Custom-Font-Icon |
| | | * |
| | | * 自定义图标加载表 |
| | | * 所有图标均从这里加载,方便管理 |
| | | */ |
| | | import bxAnaalyse from '@/assets/icons/bx-analyse.svg?inline' // path to your '*.svg?inline' file. |
| | | |
| | | export { bxAnaalyse } |
New file |
| | |
| | | /** |
| | | * 该文件是为了按需加载,剔除掉了一些不需要的框架组件。 |
| | | * 减少了编译支持库包大小 |
| | | * @author yubaoshan |
| | | * 当需要更多组件依赖时,在该文件加入即可 |
| | | */ |
| | | import Vue from 'vue' |
| | | import { |
| | | ConfigProvider, |
| | | Layout, |
| | | Input, |
| | | InputNumber, |
| | | Button, |
| | | Switch, |
| | | Radio, |
| | | Checkbox, |
| | | Select, |
| | | Card, |
| | | Form, |
| | | Row, |
| | | Col, |
| | | Modal, |
| | | Table, |
| | | Tabs, |
| | | Icon, |
| | | Badge, |
| | | Popover, |
| | | Dropdown, |
| | | List, |
| | | Avatar, |
| | | Breadcrumb, |
| | | Steps, |
| | | Spin, |
| | | Menu, |
| | | Drawer, |
| | | Tooltip, |
| | | Alert, |
| | | Tag, |
| | | Divider, |
| | | DatePicker, |
| | | TimePicker, |
| | | Upload, |
| | | Progress, |
| | | Skeleton, |
| | | Popconfirm, |
| | | message, |
| | | notification, |
| | | TreeSelect, |
| | | Tree, |
| | | Transfer, |
| | | Empty, |
| | | PageHeader, |
| | | Descriptions, |
| | | Result |
| | | } from 'ant-design-vue' |
| | | // import VueCropper from 'vue-cropper' |
| | | |
| | | Vue.use(ConfigProvider) |
| | | Vue.use(Layout) |
| | | Vue.use(Input) |
| | | Vue.use(InputNumber) |
| | | Vue.use(Button) |
| | | Vue.use(Switch) |
| | | Vue.use(Radio) |
| | | Vue.use(Checkbox) |
| | | Vue.use(Select) |
| | | Vue.use(Card) |
| | | Vue.use(Form) |
| | | Vue.use(Row) |
| | | Vue.use(Col) |
| | | Vue.use(Modal) |
| | | Vue.use(Table) |
| | | Vue.use(Tabs) |
| | | Vue.use(Icon) |
| | | Vue.use(Badge) |
| | | Vue.use(Popover) |
| | | Vue.use(Dropdown) |
| | | Vue.use(List) |
| | | Vue.use(Avatar) |
| | | Vue.use(Breadcrumb) |
| | | Vue.use(Steps) |
| | | Vue.use(Spin) |
| | | Vue.use(Menu) |
| | | Vue.use(Drawer) |
| | | Vue.use(Tooltip) |
| | | Vue.use(Alert) |
| | | Vue.use(Tag) |
| | | Vue.use(Divider) |
| | | Vue.use(DatePicker) |
| | | Vue.use(TimePicker) |
| | | Vue.use(Upload) |
| | | Vue.use(Progress) |
| | | Vue.use(Skeleton) |
| | | Vue.use(Popconfirm) |
| | | // Vue.use(VueCropper) |
| | | Vue.use(notification) |
| | | Vue.use(TreeSelect) |
| | | Vue.use(Tree) |
| | | Vue.use(Transfer) |
| | | Vue.use(Empty) |
| | | Vue.use(PageHeader) |
| | | Vue.use(Descriptions) |
| | | Vue.use(Result) |
| | | |
| | | Vue.prototype.$confirm = Modal.confirm |
| | | Vue.prototype.$message = message |
| | | Vue.prototype.$notification = notification |
| | | Vue.prototype.$info = Modal.info |
| | | Vue.prototype.$success = Modal.success |
| | | Vue.prototype.$error = Modal.error |
| | | Vue.prototype.$warning = Modal.warning |
New file |
| | |
| | | import Vue from 'vue' |
| | | import VueStorage from 'vue-ls' |
| | | import config from '@/config/defaultSettings' |
| | | |
| | | // base library |
| | | import '@/core/lazy_lib/components_use' |
| | | import Viser from 'viser-vue' |
| | | |
| | | // ext library |
| | | import VueClipboard from 'vue-clipboard2' |
| | | import VueCropper from 'vue-cropper' |
| | | import MultiTab from '@/components/MultiTab' |
| | | import PageLoading from '@/components/PageLoading' |
| | | import PermissionHelper from '@/utils/helper/permission' |
| | | import './directives/action' |
| | | |
| | | VueClipboard.config.autoSetContainer = true |
| | | |
| | | Vue.use(Viser) |
| | | Vue.use(MultiTab) |
| | | Vue.use(PageLoading) |
| | | Vue.use(VueStorage, config.storageOptions) |
| | | Vue.use(VueClipboard) |
| | | Vue.use(PermissionHelper) |
| | | Vue.use(VueCropper) |
| | | |
| | | process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] NOTICE: Antd use lazy-load.') |
New file |
| | |
| | | import Vue from 'vue' |
| | | import VueStorage from 'vue-ls' |
| | | import config from '@/config/defaultSettings' |
| | | |
| | | // base library |
| | | import Antd from 'ant-design-vue' |
| | | import Viser from 'viser-vue' |
| | | import VueCropper from 'vue-cropper' |
| | | import 'ant-design-vue/dist/antd.less' |
| | | |
| | | // ext library |
| | | import VueClipboard from 'vue-clipboard2' |
| | | import MultiTab from '@/components/MultiTab' |
| | | import PageLoading from '@/components/PageLoading' |
| | | import PermissionHelper from '@/utils/helper/permission' |
| | | // import '@/components/use' |
| | | import './directives/action' |
| | | |
| | | VueClipboard.config.autoSetContainer = true |
| | | |
| | | Vue.use(Antd) |
| | | Vue.use(Viser) |
| | | Vue.use(MultiTab) |
| | | Vue.use(PageLoading) |
| | | Vue.use(VueStorage, config.storageOptions) |
| | | Vue.use(VueClipboard) |
| | | Vue.use(PermissionHelper) |
| | | Vue.use(VueCropper) |
| | | |
| | | process.env.NODE_ENV !== 'production' && console.warn('[antd-pro] WARNING: Antd now use fulled imported.') |
New file |
| | |
| | | <template> |
| | | <a-layout :class="['layout', device]"> |
| | | <!-- SideMenu --> |
| | | <a-drawer |
| | | v-if="isMobile()" |
| | | placement="left" |
| | | :wrapClassName="`drawer-sider ${navTheme}`" |
| | | :closable="false" |
| | | :visible="collapsed" |
| | | @close="drawerClose" |
| | | > |
| | | <side-menu |
| | | mode="inline" |
| | | :menus="menus" |
| | | :theme="navTheme" |
| | | :collapsed="false" |
| | | :collapsible="true" |
| | | @menuSelect="menuSelect" |
| | | ></side-menu> |
| | | </a-drawer> |
| | | |
| | | <side-menu |
| | | v-else-if="isSideMenu()" |
| | | mode="inline" |
| | | :menus="menus" |
| | | :theme="navTheme" |
| | | :collapsed="collapsed" |
| | | :collapsible="true" |
| | | ></side-menu> |
| | | |
| | | <a-layout :class="[layoutMode, `content-width-${contentWidth}`]" :style="{ paddingLeft: contentPaddingLeft, minHeight: '100vh' }"> |
| | | <!-- layout header --> |
| | | <global-header |
| | | :mode="layoutMode" |
| | | :menus="menus" |
| | | :theme="navTheme" |
| | | :collapsed="collapsed" |
| | | :device="device" |
| | | @toggle="toggle" |
| | | /> |
| | | |
| | | <!-- layout content --> |
| | | <a-layout-content :style="{ height: '100%', margin: '24px 24px 0', paddingTop: fixedHeader ? '55px' : '0' }"> |
| | | <multi-tab v-if="multiTab"></multi-tab> |
| | | <transition name="page-transition"> |
| | | <route-view /> |
| | | </transition> |
| | | </a-layout-content> |
| | | |
| | | <!-- layout footer --> |
| | | <a-layout-footer> |
| | | <global-footer /> |
| | | </a-layout-footer> |
| | | |
| | | <!-- Setting Drawer (show in development mode) --> |
| | | <setting-drawer v-if="!production"></setting-drawer> |
| | | </a-layout> |
| | | </a-layout> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | import { triggerWindowResizeEvent } from '@/utils/util' |
| | | import { mapState, mapActions } from 'vuex' |
| | | import { mixin, mixinDevice } from '@/utils/mixin' |
| | | import config from '@/config/defaultSettings' |
| | | |
| | | import RouteView from './RouteView' |
| | | import SideMenu from '@/components/Menu/SideMenu' |
| | | import GlobalHeader from '@/components/GlobalHeader' |
| | | import GlobalFooter from '@/components/GlobalFooter' |
| | | import SettingDrawer from '@/components/SettingDrawer' |
| | | import { convertRoutes } from '@/utils/routeConvert' |
| | | |
| | | export default { |
| | | name: 'BasicLayout', |
| | | mixins: [mixin, mixinDevice], |
| | | components: { |
| | | RouteView, |
| | | SideMenu, |
| | | GlobalHeader, |
| | | GlobalFooter, |
| | | SettingDrawer |
| | | }, |
| | | data () { |
| | | return { |
| | | production: config.production, |
| | | collapsed: false, |
| | | menus: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState({ |
| | | // 动态主路由 |
| | | mainMenu: state => state.permission.addRouters |
| | | }), |
| | | contentPaddingLeft () { |
| | | if (!this.fixSidebar || this.isMobile()) { |
| | | return '0' |
| | | } |
| | | if (this.sidebarOpened) { |
| | | return '230px' |
| | | } |
| | | return '80px' |
| | | } |
| | | }, |
| | | watch: { |
| | | sidebarOpened (val) { |
| | | this.collapsed = !val |
| | | }, |
| | | // 菜单变化 |
| | | mainMenu (val) { |
| | | this.setMenus() |
| | | } |
| | | }, |
| | | created () { |
| | | this.setMenus() |
| | | /* const routes = convertRoutes(this.mainMenu.find(item => item.path === '/')) |
| | | this.menus = (routes && routes.children) || [] */ |
| | | this.collapsed = !this.sidebarOpened |
| | | }, |
| | | mounted () { |
| | | const userAgent = navigator.userAgent |
| | | if (userAgent.indexOf('Edge') > -1) { |
| | | this.$nextTick(() => { |
| | | this.collapsed = !this.collapsed |
| | | setTimeout(() => { |
| | | this.collapsed = !this.collapsed |
| | | }, 16) |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | ...mapActions(['setSidebar']), |
| | | // 重新生成 |
| | | setMenus () { |
| | | const routes = convertRoutes(this.mainMenu.find(item => item.path === '/')) |
| | | this.menus = (routes && routes.children) || [] |
| | | }, |
| | | toggle () { |
| | | this.collapsed = !this.collapsed |
| | | this.setSidebar(!this.collapsed) |
| | | triggerWindowResizeEvent() |
| | | }, |
| | | paddingCalc () { |
| | | let left = '' |
| | | if (this.sidebarOpened) { |
| | | left = this.isDesktop() ? '230px' : '80px' |
| | | } else { |
| | | left = (this.isMobile() && '0') || ((this.fixSidebar && '80px') || '0') |
| | | } |
| | | return left |
| | | }, |
| | | menuSelect () { |
| | | }, |
| | | drawerClose () { |
| | | this.collapsed = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less"> |
| | | /* |
| | | * The following styles are auto-applied to elements with |
| | | * transition="page-transition" when their visibility is toggled |
| | | * by Vue.js. |
| | | * |
| | | * You can easily play with the page transition by editing |
| | | * these styles. |
| | | */ |
| | | |
| | | .page-transition-enter { |
| | | opacity: 0; |
| | | } |
| | | |
| | | .page-transition-leave-active { |
| | | opacity: 0; |
| | | } |
| | | |
| | | .page-transition-enter .page-transition-container, |
| | | .page-transition-leave-active .page-transition-container { |
| | | -webkit-transform: scale(1.1); |
| | | transform: scale(1.1); |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <router-view /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | |
| | | export default { |
| | | name: 'BlankLayout' |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | |
| | | <script> |
| | | import Vue from 'vue' |
| | | import { ACCESS_TOKEN } from '@/store/mutation-types' |
| | | |
| | | export default { |
| | | name: 'Iframe', |
| | | data () { |
| | | return { |
| | | } |
| | | }, |
| | | render () { |
| | | const { $route: { meta: { link } } } = this |
| | | if ({ link }.link === '') { |
| | | return '404' |
| | | } |
| | | let url = '' |
| | | if ({ link }.link.indexOf('token=') > -1) { |
| | | url = { link }.link + Vue.ls.get(ACCESS_TOKEN) |
| | | } else { |
| | | url = { link }.link |
| | | } |
| | | let height = '' |
| | | const deviceHeight = document.documentElement.clientHeight |
| | | height = (Number(deviceHeight) - 260) + 'px' |
| | | return <iframe id="iframe" height={height} src={url} style="width:100%;overflow:hidden;" frameBorder="0"></iframe> |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div :style="!$route.meta.hiddenHeaderContent ? 'margin: -24px -24px 0px;' : null"> |
| | | <!-- pageHeader , route meta :true on hide --> |
| | | <page-header v-if="!$route.meta.hiddenHeaderContent" :title="pageTitle" :logo="logo" :avatar="avatar"> |
| | | <slot slot="action" name="action"></slot> |
| | | <slot slot="content" name="headerContent"></slot> |
| | | <div slot="content" v-if="!this.$slots.headerContent && description"> |
| | | <p style="font-size: 14px;color: rgba(0,0,0,.65)">{{ description }}</p> |
| | | <div class="link"> |
| | | </div> |
| | | </div> |
| | | <slot slot="extra" name="extra"> |
| | | <div class="extra-img"> |
| | | <img v-if="typeof extraImage !== 'undefined'" :src="extraImage"/> |
| | | </div> |
| | | </slot> |
| | | <div slot="pageMenu"> |
| | | <div class="page-menu-search" v-if="search"> |
| | | <a-input-search |
| | | style="width: 80%; max-width: 522px;" |
| | | placeholder="请输入..." |
| | | size="large" |
| | | enterButton="搜索" |
| | | /> |
| | | </div> |
| | | <div class="page-menu-tabs" v-if="tabs && tabs.items"> |
| | | <!-- @change="callback" :activeKey="activeKey" --> |
| | | <a-tabs :tabBarStyle="{margin: 0}" :activeKey="tabs.active()" @change="tabs.callback"> |
| | | <a-tab-pane v-for="item in tabs.items" :tab="item.title" :key="item.key"></a-tab-pane> |
| | | </a-tabs> |
| | | </div> |
| | | </div> |
| | | </page-header> |
| | | <div class="content"> |
| | | <div class="page-header-index-wide"> |
| | | <slot> |
| | | <!-- keep-alive --> |
| | | <keep-alive v-if="multiTab"> |
| | | <router-view ref="content" /> |
| | | </keep-alive> |
| | | <router-view v-else ref="content" style="margin: -12px -14px 0;"/> |
| | | </slot> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapState } from 'vuex' |
| | | import PageHeader from '@/components/PageHeader' |
| | | |
| | | export default { |
| | | name: 'PageView', |
| | | components: { |
| | | PageHeader |
| | | }, |
| | | props: { |
| | | avatar: { |
| | | type: String, |
| | | default: null |
| | | }, |
| | | title: { |
| | | type: [String, Boolean], |
| | | default: true |
| | | }, |
| | | logo: { |
| | | type: String, |
| | | default: null |
| | | }, |
| | | directTabs: { |
| | | type: Object, |
| | | default: null |
| | | } |
| | | }, |
| | | data () { |
| | | return { |
| | | pageTitle: null, |
| | | description: null, |
| | | linkList: [], |
| | | extraImage: '', |
| | | search: false, |
| | | tabs: {} |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState({ |
| | | multiTab: state => state.app.multiTab |
| | | }) |
| | | }, |
| | | mounted () { |
| | | this.tabs = this.directTabs |
| | | this.getPageMeta() |
| | | }, |
| | | updated () { |
| | | this.getPageMeta() |
| | | }, |
| | | methods: { |
| | | getPageMeta () { |
| | | // eslint-disable-next-line |
| | | //文字样式//(typeof(this.title) === 'string' || !this.title) ? this.title : this.$route.meta.title |
| | | // 为了简洁改为无 |
| | | this.pageTitle = '' |
| | | |
| | | const content = this.$refs.content |
| | | if (content) { |
| | | if (content.pageMeta) { |
| | | Object.assign(this, content.pageMeta) |
| | | } else { |
| | | this.description = content.description |
| | | this.linkList = content.linkList |
| | | this.extraImage = content.extraImage |
| | | this.search = content.search === true |
| | | this.tabs = content.tabs |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .content { |
| | | margin: 24px 24px 0; |
| | | .link { |
| | | margin-top: 16px; |
| | | &:not(:empty) { |
| | | margin-bottom: 16px; |
| | | } |
| | | a { |
| | | margin-right: 32px; |
| | | height: 24px; |
| | | line-height: 24px; |
| | | display: inline-block; |
| | | i { |
| | | font-size: 24px; |
| | | margin-right: 8px; |
| | | vertical-align: middle; |
| | | } |
| | | span { |
| | | height: 24px; |
| | | line-height: 24px; |
| | | display: inline-block; |
| | | vertical-align: middle; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .page-menu-search { |
| | | text-align: center; |
| | | margin-bottom: 16px; |
| | | } |
| | | .page-menu-tabs { |
| | | margin-top: 48px; |
| | | } |
| | | |
| | | .extra-img { |
| | | margin-top: -60px; |
| | | text-align: center; |
| | | width: 195px; |
| | | |
| | | img { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .mobile { |
| | | .extra-img{ |
| | | margin-top: 0; |
| | | text-align: center; |
| | | width: 96px; |
| | | |
| | | img{ |
| | | width: 100%; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <script> |
| | | export default { |
| | | name: 'RouteView', |
| | | props: { |
| | | keepAlive: { |
| | | type: Boolean, |
| | | default: true |
| | | } |
| | | }, |
| | | data () { |
| | | return {} |
| | | }, |
| | | render () { |
| | | const { $route: { meta }, $store: { getters } } = this |
| | | const inKeep = ( |
| | | <keep-alive> |
| | | <router-view /> |
| | | </keep-alive> |
| | | ) |
| | | const notKeep = ( |
| | | <router-view /> |
| | | ) |
| | | // 这里增加了 multiTab 的判断,当开启了 multiTab 时 |
| | | // 应当全部组件皆缓存,否则会导致切换页面后页面还原成原始状态 |
| | | // 若确实不需要,可改为 return meta.keepAlive ? inKeep : notKeep |
| | | if (!getters.multiTab && !meta.keepAlive) { |
| | | return notKeep |
| | | } |
| | | return this.keepAlive || getters.multiTab || meta.keepAlive ? inKeep : notKeep |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div id="userLayout" :class="['user-layout-wrapper', device]"> |
| | | <div class="container"> |
| | | <div class="top"> |
| | | <div class="header"> |
| | | <a href="/"> |
| | | <img src="~@/assets/logo.png" class="logo" alt="logo"> |
| | | <span class="title">Snowy快速开发平台</span> |
| | | </a> |
| | | </div> |
| | | <div class="desc"> |
| | | |
| | | </div> |
| | | </div> |
| | | |
| | | <route-view></route-view> |
| | | |
| | | <div class="footer"> |
| | | <div class="links"> |
| | | <a href="_self">帮助</a> |
| | | <a href="_self">隐私</a> |
| | | <a href="_self">条款</a> |
| | | </div> |
| | | <div class="copyright"> |
| | | Copyright © 2020 <a target="_blank" href="https://www.xiaonuo.vip/">小诺开源技术</a> All rights reserved. Snowy 1.8 |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import RouteView from './RouteView' |
| | | import { mixinDevice } from '@/utils/mixin' |
| | | |
| | | export default { |
| | | name: 'UserLayout', |
| | | components: { RouteView }, |
| | | mixins: [mixinDevice], |
| | | data () { |
| | | return {} |
| | | }, |
| | | mounted () { |
| | | document.body.classList.add('userLayout') |
| | | }, |
| | | beforeDestroy () { |
| | | document.body.classList.remove('userLayout') |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | #userLayout.user-layout-wrapper { |
| | | height: 100%; |
| | | |
| | | &.mobile { |
| | | .container { |
| | | .main { |
| | | max-width: 368px; |
| | | width: 98%; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .container { |
| | | width: 100%; |
| | | min-height: 100%; |
| | | background-color: #ffffff; |
| | | background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='800' height='800' viewBox='0 0 200 200'%3E%3Cdefs%3E%3ClinearGradient id='a' gradientUnits='userSpaceOnUse' x1='88' y1='88' x2='0' y2='0'%3E%3Cstop offset='0' stop-color='%23064e77'/%3E%3Cstop offset='1' stop-color='%230a7dbe'/%3E%3C/linearGradient%3E%3ClinearGradient id='b' gradientUnits='userSpaceOnUse' x1='75' y1='76' x2='168' y2='160'%3E%3Cstop offset='0' stop-color='%238f8f8f'/%3E%3Cstop offset='0.09' stop-color='%23b3b3b3'/%3E%3Cstop offset='0.18' stop-color='%23c9c9c9'/%3E%3Cstop offset='0.31' stop-color='%23dbdbdb'/%3E%3Cstop offset='0.44' stop-color='%23e8e8e8'/%3E%3Cstop offset='0.59' stop-color='%23f2f2f2'/%3E%3Cstop offset='0.75' stop-color='%23fafafa'/%3E%3Cstop offset='1' stop-color='%23FFFFFF'/%3E%3C/linearGradient%3E%3Cfilter id='c' x='0' y='0' width='200%25' height='200%25'%3E%3CfeGaussianBlur in='SourceGraphic' stdDeviation='12' /%3E%3C/filter%3E%3C/defs%3E%3Cpolygon fill='url(%23a)' points='0 174 0 0 174 0'/%3E%3Cpath fill='%23000' fill-opacity='.5' filter='url(%23c)' d='M121.8 174C59.2 153.1 0 174 0 174s63.5-73.8 87-94c24.4-20.9 87-80 87-80S107.9 104.4 121.8 174z'/%3E%3Cpath fill='url(%23b)' d='M142.7 142.7C59.2 142.7 0 174 0 174s42-66.3 74.9-99.3S174 0 174 0S142.7 62.6 142.7 142.7z'/%3E%3C/svg%3E"); |
| | | background-attachment: fixed; |
| | | background-repeat: no-repeat; |
| | | background-position: top left; |
| | | padding: 110px 0 144px; |
| | | position: relative; |
| | | |
| | | a { |
| | | text-decoration: none; |
| | | } |
| | | |
| | | .top { |
| | | text-align: center; |
| | | |
| | | .header { |
| | | height: 44px; |
| | | line-height: 44px; |
| | | |
| | | .badge { |
| | | position: absolute; |
| | | display: inline-block; |
| | | line-height: 1; |
| | | vertical-align: middle; |
| | | margin-left: -12px; |
| | | margin-top: -10px; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .logo { |
| | | height: 44px; |
| | | vertical-align: top; |
| | | margin-right: 16px; |
| | | border-style: none; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 33px; |
| | | color: rgba(0, 0, 0, .85); |
| | | font-family: Avenir, 'Helvetica Neue', Arial, Helvetica, sans-serif; |
| | | font-weight: 600; |
| | | position: relative; |
| | | top: 2px; |
| | | } |
| | | } |
| | | .desc { |
| | | font-size: 14px; |
| | | color: rgba(0, 0, 0, 0.45); |
| | | margin-top: 12px; |
| | | margin-bottom: 40px; |
| | | } |
| | | } |
| | | |
| | | .main { |
| | | min-width: 260px; |
| | | width: 368px; |
| | | margin: 0 auto; |
| | | } |
| | | |
| | | .footer { |
| | | position: absolute; |
| | | width: 100%; |
| | | bottom: 0; |
| | | padding: 0 16px; |
| | | margin: 48px 0 24px; |
| | | text-align: center; |
| | | |
| | | .links { |
| | | margin-bottom: 8px; |
| | | font-size: 14px; |
| | | a { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | transition: all 0.3s; |
| | | &:not(:last-child) { |
| | | margin-right: 40px; |
| | | } |
| | | } |
| | | } |
| | | .copyright { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import UserLayout from './UserLayout' |
| | | import BlankLayout from './BlankLayout' |
| | | import BasicLayout from './BasicLayout' |
| | | import RouteView from './RouteView' |
| | | import PageView from './PageView' |
| | | import Iframe from './Iframe' |
| | | |
| | | export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView, Iframe } |
New file |
| | |
| | | import 'core-js/stable' |
| | | import 'regenerator-runtime/runtime' |
| | | import Vue from 'vue' |
| | | import App from './App.vue' |
| | | import router from './router' |
| | | import store from './store/' |
| | | import { VueAxios } from './utils/request' |
| | | import bootstrap from './core/bootstrap' |
| | | import './core/lazy_use' |
| | | import './permission' |
| | | import './utils/filter' |
| | | import './components/global.less' |
| | | import { Dialog } from '@/components' |
| | | import { hasBtnPermission } from './utils/permissions' |
| | | import { sysApplication } from './utils/applocation' |
| | | |
| | | Vue.use(VueAxios) |
| | | Vue.use(Dialog) |
| | | Vue.prototype.hasPerm = hasBtnPermission |
| | | Vue.prototype.applocation = sysApplication |
| | | Vue.config.productionTip = false |
| | | |
| | | new Vue({ |
| | | router, |
| | | store, |
| | | created: bootstrap, |
| | | render: h => h(App) |
| | | }).$mount('#app') |
New file |
| | |
| | | import Vue from 'vue' |
| | | import router from './router' |
| | | import store from './store' |
| | | |
| | | import NProgress from 'nprogress' // progress bar |
| | | import '@/components/NProgress/nprogress.less' // progress bar custom style |
| | | import { setDocumentTitle, domTitle } from '@/utils/domUtil' |
| | | import { ACCESS_TOKEN, ALL_APPS_MENU } from '@/store/mutation-types' |
| | | |
| | | import { Modal, notification } from 'ant-design-vue' // NProgress Configuration |
| | | import { timeFix } from '@/utils/util'/// es/notification |
| | | NProgress.configure({ showSpinner: false }) |
| | | const whiteList = ['login', 'register', 'registerResult'] // no redirect whitelist |
| | | // 无默认首页的情况 |
| | | const defaultRoutePath = '/welcome' |
| | | |
| | | router.beforeEach((to, from, next) => { |
| | | NProgress.start() // start progress bar |
| | | to.meta && (typeof to.meta.title !== 'undefined' && setDocumentTitle(`${to.meta.title} - ${domTitle}`)) |
| | | if (Vue.ls.get(ACCESS_TOKEN)) { |
| | | /* has token */ |
| | | if (to.path === '/user/login') { |
| | | next({ path: defaultRoutePath }) |
| | | NProgress.done() |
| | | } else { |
| | | if (store.getters.roles.length === 0) { |
| | | store |
| | | .dispatch('GetInfo') |
| | | .then(res => { |
| | | if (res.menus.length < 1) { |
| | | Modal.error({ |
| | | title: '提示:', |
| | | content: '无菜单权限,请联系管理员', |
| | | okText: '确定', |
| | | onOk: () => { |
| | | store.dispatch('Logout').then(() => { |
| | | window.location.reload() |
| | | }) |
| | | } |
| | | }) |
| | | return |
| | | } |
| | | // eslint-disable-next-line camelcase |
| | | const all_app_menu = Vue.ls.get(ALL_APPS_MENU) |
| | | let antDesignmenus |
| | | // eslint-disable-next-line camelcase |
| | | if (all_app_menu == null) { |
| | | const applocation = [] |
| | | res.apps.forEach(item => { |
| | | const apps = { 'code': '', 'name': '', 'active': '', 'menu': '' } |
| | | if (item.active) { |
| | | apps.code = item.code |
| | | apps.name = item.name |
| | | apps.active = item.active |
| | | apps.menu = res.menus |
| | | antDesignmenus = res.menus |
| | | } else { |
| | | apps.code = item.code |
| | | apps.name = item.name |
| | | apps.active = item.active |
| | | apps.menu = '' |
| | | } |
| | | applocation.push(apps) |
| | | }) |
| | | Vue.ls.set(ALL_APPS_MENU, applocation, 7 * 24 * 60 * 60 * 1000) |
| | | // 延迟 1 秒显示欢迎信息 |
| | | setTimeout(() => { |
| | | notification.success({ |
| | | message: '欢迎', |
| | | description: `${timeFix()},欢迎回来` |
| | | }) |
| | | }, 1000) |
| | | } else { |
| | | antDesignmenus = Vue.ls.get(ALL_APPS_MENU)[0].menu |
| | | } |
| | | store.dispatch('GenerateRoutes', { antDesignmenus }).then(() => { |
| | | // 动态添加可访问路由表 |
| | | router.addRoutes(store.getters.addRouters) |
| | | // 请求带有 redirect 重定向时,登录自动重定向到该地址 |
| | | const redirect = decodeURIComponent(from.query.redirect || to.path) |
| | | if (to.path === redirect) { |
| | | next({ path: redirect }) |
| | | // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record |
| | | next({ ...to, replace: true }) |
| | | } else { |
| | | // 跳转到目的路由 |
| | | next({ path: redirect }) |
| | | } |
| | | }) |
| | | }) |
| | | .catch(() => { |
| | | store.dispatch('Logout').then(() => { |
| | | next({ path: '/user/login', query: { redirect: to.fullPath } }) |
| | | }) |
| | | }) |
| | | } else { |
| | | next() |
| | | } |
| | | } |
| | | } else { |
| | | if (whiteList.includes(to.name)) { |
| | | // 在免登录白名单,直接进入 |
| | | next() |
| | | } else { |
| | | next({ path: '/user/login', query: { redirect: to.fullPath } }) |
| | | NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it |
| | | } |
| | | } |
| | | }) |
| | | |
| | | router.afterEach(() => { |
| | | NProgress.done() // finish progress bar |
| | | }) |
New file |
| | |
| | | |
| | | import { BasicLayout, BlankLayout, PageView, RouteView, Iframe } from '@/layouts' |
| | | |
| | | // 前端路由表 |
| | | const constantRouterComponents = { |
| | | // 基础页面 layout 必须引入 |
| | | BasicLayout: BasicLayout, |
| | | BlankLayout: BlankLayout, |
| | | RouteView: RouteView, |
| | | PageView: PageView, |
| | | Iframe: Iframe, |
| | | '403': () => import('@/views/system/exception/403'), |
| | | '404': () => import('@/views/system/exception/404'), |
| | | '500': () => import('@/views/system/exception/500'), |
| | | |
| | | 'Workplace': () => import('@/views/system/dashboard/Workplace'), |
| | | // account |
| | | 'AccountCenter': () => import('@/views/system/account/center/Index'), |
| | | 'AccountSettings': () => import('@/views/system/account/settings/Index'), |
| | | 'BaseSettings': () => import('@/views/system/account/settings/BaseSetting'), |
| | | 'SecuritySettings': () => import('@/views/system/account/settings/Security'), |
| | | 'CustomSettings': () => import('@/views/system/account/settings/Custom'), |
| | | 'BindingSettings': () => import('@/views/system/account/settings/Binding'), |
| | | 'NotificationSettings': () => import('@/views/system/account/settings/Notification'), |
| | | |
| | | // 默认首页 |
| | | 'Console': () => import('@/views/system/index/welcome') |
| | | } |
| | | |
| | | // 前端未找到页面路由(固定不用改)、原来为 /404 |
| | | const notFoundRouter = { |
| | | path: '*', redirect: '/welcome', hidden: true |
| | | } |
| | | // 个人中心页面 |
| | | const userAccount = [ |
| | | // account |
| | | { |
| | | 'name': 'account', |
| | | 'pid': 0, |
| | | 'id': 10028, |
| | | 'meta': { |
| | | 'title': '个人页', |
| | | 'icon': 'user', |
| | | 'show': false |
| | | }, |
| | | 'redirect': '/account/center', |
| | | 'component': 'RouteView' |
| | | }, |
| | | { |
| | | 'name': 'center', |
| | | 'pid': 10028, |
| | | 'id': 10029, |
| | | 'meta': { |
| | | 'title': '个人中心', |
| | | 'show': false |
| | | }, |
| | | 'component': 'AccountCenter' |
| | | }, |
| | | // 特殊三级菜单 |
| | | { |
| | | 'name': 'settings', |
| | | 'pid': '10028', |
| | | 'id': '10030', |
| | | 'meta': { |
| | | 'title': '个人设置', |
| | | 'hideHeader': true, |
| | | 'hideChildren': true, |
| | | 'show': false |
| | | }, |
| | | 'redirect': '/account/settings/base', |
| | | 'component': 'AccountSettings' |
| | | }, |
| | | { |
| | | 'name': 'BaseSettings', |
| | | 'path': '/account/settings/base', |
| | | 'pid': 10030, |
| | | 'id': 10031, |
| | | 'meta': { |
| | | 'title': '基本设置', |
| | | 'show': false |
| | | }, |
| | | 'component': 'BaseSettings' |
| | | }, |
| | | { |
| | | 'name': 'SecuritySettings', |
| | | 'path': '/account/settings/security', |
| | | 'pid': 10030, |
| | | 'id': 10032, |
| | | 'meta': { |
| | | 'title': '安全设置', |
| | | 'show': false |
| | | }, |
| | | 'component': 'SecuritySettings' |
| | | }, |
| | | { |
| | | 'name': 'CustomSettings', |
| | | 'path': '/account/settings/custom', |
| | | 'pid': 10030, |
| | | 'id': 10033, |
| | | 'meta': { |
| | | 'title': '个性化设置', |
| | | 'show': false |
| | | }, |
| | | 'component': 'CustomSettings' |
| | | }, |
| | | { |
| | | 'name': 'BindingSettings', |
| | | 'path': '/account/settings/binding', |
| | | 'pid': 10030, |
| | | 'id': 10034, |
| | | 'meta': { |
| | | 'title': '账户绑定', |
| | | 'show': false |
| | | }, |
| | | 'component': 'BindingSettings' |
| | | }, |
| | | { |
| | | 'name': 'NotificationSettings', |
| | | 'path': '/account/settings/notification', |
| | | 'pid': 10030, |
| | | 'id': 10034, |
| | | 'meta': { |
| | | 'title': '新消息通知', |
| | | 'show': false |
| | | }, |
| | | 'component': 'NotificationSettings' |
| | | }, |
| | | { |
| | | 'name': 'Console', |
| | | 'path': '/welcome', |
| | | 'pid': 0, |
| | | 'id': 183183, |
| | | 'meta': { |
| | | 'title': '首页', |
| | | 'show': false |
| | | }, |
| | | 'component': 'Console' |
| | | } |
| | | |
| | | ] |
| | | |
| | | // 根级菜单 |
| | | const rootRouter = { |
| | | key: '', |
| | | name: 'MenuIndex.vue', |
| | | path: '', |
| | | component: 'BasicLayout', |
| | | redirect: '/welcome', |
| | | meta: { |
| | | title: '首页' |
| | | }, |
| | | children: [] |
| | | } |
| | | |
| | | /** |
| | | * 动态生成菜单 |
| | | * @param data |
| | | * @returns {Promise<Router>} |
| | | */ |
| | | export const generatorDynamicRouter = (data) => { |
| | | return new Promise((resolve, reject) => { |
| | | const resNav = data.antDesignmenus |
| | | const menuNav = [] |
| | | const childrenNav = [] |
| | | // 后端数据, 根级树数组, 根级 PID |
| | | listToTree(resNav, childrenNav, 0) |
| | | |
| | | /** |
| | | * 增加静态网页 |
| | | */ |
| | | listToTree(userAccount, childrenNav, 0) |
| | | rootRouter.children = childrenNav |
| | | menuNav.push(rootRouter) |
| | | const routers = generator(menuNav) |
| | | routers.push(notFoundRouter) |
| | | resolve(routers) |
| | | }).catch(err => { |
| | | // reject('加载菜单失败') |
| | | return Promise.reject(err) |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 格式化树形结构数据 生成 vue-router 层级路由表 |
| | | * |
| | | * @param routerMap |
| | | * @param parent |
| | | * @returns {*} |
| | | */ |
| | | export const generator = (routerMap, parent) => { |
| | | return routerMap.map(item => { |
| | | // eslint-disable-next-line no-unused-vars |
| | | const { title, show, hideChildren, hiddenHeaderContent, target, icon, link } = item.meta || {} |
| | | const currentRouter = { |
| | | // 如果路由设置了 path,则作为默认 path,否则 路由地址 动态拼接生成如 /dashboard/workplace |
| | | path: item.path || `${parent && parent.path || ''}/${item.key}`, |
| | | // 路由名称,建议唯一 |
| | | name: item.name || item.key || '', |
| | | // 该路由对应页面的 组件 :方案1 |
| | | // component: constantRouterComponents[item.component || item.key], |
| | | // 该路由对应页面的 组件 :方案2 (动态加载) |
| | | component: (constantRouterComponents[item.component || item.key]) || (() => import(`@/views/${item.component}`)), |
| | | // meta: 页面标题, 菜单图标, 页面权限(供指令权限用,可去掉) |
| | | meta: { |
| | | title: title, |
| | | icon: icon || undefined, |
| | | // hiddenHeaderContent: hiddenHeaderContent, |
| | | target: target, |
| | | link: link |
| | | } |
| | | } |
| | | // 是否设置了隐藏菜单 |
| | | if (show === false) { |
| | | currentRouter.hidden = true |
| | | } |
| | | // 是否设置了隐藏子菜单 |
| | | if (hideChildren) { |
| | | currentRouter.hideChildrenInMenu = true |
| | | } |
| | | // 为了防止出现后端返回结果不规范,处理有可能出现拼接出两个 反斜杠 |
| | | if (!currentRouter.path.startsWith('http')) { |
| | | currentRouter.path = currentRouter.path.replace('//', '/') |
| | | } |
| | | // 重定向 |
| | | item.redirect && (currentRouter.redirect = item.redirect) |
| | | // 是否有子菜单,并递归处理 |
| | | if (item.children && item.children.length > 0) { |
| | | // Recursion |
| | | currentRouter.children = generator(item.children, currentRouter) |
| | | } |
| | | return currentRouter |
| | | }) |
| | | } |
| | | |
| | | /** |
| | | * 数组转树形结构 |
| | | * @param list 源数组 |
| | | * @param tree 树 |
| | | * @param parentId 父ID |
| | | */ |
| | | const listToTree = (list, tree, parentId) => { |
| | | list.forEach(item => { |
| | | // 判断是否为父级菜单 |
| | | // eslint-disable-next-line eqeqeq |
| | | if (item.pid == parentId) { |
| | | const child = { |
| | | ...item, |
| | | key: item.key || item.name, |
| | | children: [] |
| | | } |
| | | // 迭代 list, 找到当前菜单相符合的所有子菜单 |
| | | listToTree(list, child.children, item.id) |
| | | // 删掉不存在 children 值的属性 |
| | | if (child.children.length <= 0) { |
| | | delete child.children |
| | | } |
| | | // 加入到树中 |
| | | tree.push(child) |
| | | } |
| | | }) |
| | | } |
New file |
| | |
| | | import Vue from 'vue' |
| | | import Router from 'vue-router' |
| | | import { constantRouterMap } from '@/config/router.config' |
| | | |
| | | // hack router push callback |
| | | const originalPush = Router.prototype.push |
| | | Router.prototype.push = function push (location, onResolve, onReject) { |
| | | if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject) |
| | | return originalPush.call(this, location).catch(err => err) |
| | | } |
| | | |
| | | Vue.use(Router) |
| | | |
| | | export default new Router({ |
| | | mode: 'history', |
| | | base: process.env.BASE_URL, |
| | | scrollBehavior: () => ({ y: 0 }), |
| | | routes: constantRouterMap |
| | | }) |
New file |
| | |
| | | const getters = { |
| | | device: state => state.app.device, |
| | | theme: state => state.app.theme, |
| | | color: state => state.app.color, |
| | | token: state => state.user.token, |
| | | avatar: state => state.user.avatar, |
| | | nickname: state => state.user.name, |
| | | welcome: state => state.user.welcome, |
| | | roles: state => state.user.roles, |
| | | buttons: state => state.user.buttons, |
| | | admintype: state => state.user.admintype, |
| | | userInfo: state => state.user.info, |
| | | addRouters: state => state.permission.addRouters, |
| | | multiTab: state => state.app.multiTab, |
| | | lang: state => state.i18n.lang |
| | | } |
| | | |
| | | export default getters |
New file |
| | |
| | | import Vue from 'vue' |
| | | import Vuex from 'vuex' |
| | | |
| | | import app from './modules/app' |
| | | import user from './modules/user' |
| | | |
| | | // default router permission control |
| | | // import permission from './modules/permission' |
| | | |
| | | // dynamic router permission control (Experimental) |
| | | import permission from './modules/async-router' |
| | | import getters from './getters' |
| | | |
| | | Vue.use(Vuex) |
| | | |
| | | export default new Vuex.Store({ |
| | | modules: { |
| | | app, |
| | | user, |
| | | permission |
| | | }, |
| | | state: { |
| | | |
| | | }, |
| | | mutations: { |
| | | |
| | | }, |
| | | actions: { |
| | | |
| | | }, |
| | | getters |
| | | }) |
New file |
| | |
| | | import Vue from 'vue' |
| | | import { |
| | | SIDEBAR_TYPE, |
| | | DEFAULT_THEME, |
| | | DEFAULT_LAYOUT_MODE, |
| | | DEFAULT_COLOR, |
| | | DEFAULT_COLOR_WEAK, |
| | | DEFAULT_FIXED_HEADER, |
| | | DEFAULT_FIXED_SIDEMENU, |
| | | DEFAULT_FIXED_HEADER_HIDDEN, |
| | | DEFAULT_CONTENT_WIDTH_TYPE, |
| | | DEFAULT_MULTI_TAB |
| | | } from '@/store/mutation-types' |
| | | |
| | | const app = { |
| | | state: { |
| | | sidebar: true, |
| | | device: 'desktop', |
| | | theme: '', |
| | | layout: '', |
| | | contentWidth: '', |
| | | fixedHeader: false, |
| | | fixSiderbar: false, |
| | | autoHideHeader: false, |
| | | color: null, |
| | | weak: false, |
| | | multiTab: true, |
| | | hasError: false |
| | | }, |
| | | mutations: { |
| | | SET_SIDEBAR_TYPE: (state, type) => { |
| | | state.sidebar = type |
| | | Vue.ls.set(SIDEBAR_TYPE, type) |
| | | }, |
| | | CLOSE_SIDEBAR: (state) => { |
| | | Vue.ls.set(SIDEBAR_TYPE, true) |
| | | state.sidebar = false |
| | | }, |
| | | TOGGLE_DEVICE: (state, device) => { |
| | | state.device = device |
| | | }, |
| | | TOGGLE_THEME: (state, theme) => { |
| | | // setStore('_DEFAULT_THEME', theme) |
| | | Vue.ls.set(DEFAULT_THEME, theme) |
| | | state.theme = theme |
| | | }, |
| | | TOGGLE_LAYOUT_MODE: (state, layout) => { |
| | | Vue.ls.set(DEFAULT_LAYOUT_MODE, layout) |
| | | state.layout = layout |
| | | }, |
| | | TOGGLE_FIXED_HEADER: (state, fixed) => { |
| | | Vue.ls.set(DEFAULT_FIXED_HEADER, fixed) |
| | | state.fixedHeader = fixed |
| | | }, |
| | | TOGGLE_FIXED_SIDERBAR: (state, fixed) => { |
| | | Vue.ls.set(DEFAULT_FIXED_SIDEMENU, fixed) |
| | | state.fixSiderbar = fixed |
| | | }, |
| | | TOGGLE_FIXED_HEADER_HIDDEN: (state, show) => { |
| | | Vue.ls.set(DEFAULT_FIXED_HEADER_HIDDEN, show) |
| | | state.autoHideHeader = show |
| | | }, |
| | | TOGGLE_CONTENT_WIDTH: (state, type) => { |
| | | Vue.ls.set(DEFAULT_CONTENT_WIDTH_TYPE, type) |
| | | state.contentWidth = type |
| | | }, |
| | | TOGGLE_COLOR: (state, color) => { |
| | | Vue.ls.set(DEFAULT_COLOR, color) |
| | | state.color = color |
| | | }, |
| | | TOGGLE_WEAK: (state, flag) => { |
| | | Vue.ls.set(DEFAULT_COLOR_WEAK, flag) |
| | | state.weak = flag |
| | | }, |
| | | TOGGLE_MULTI_TAB: (state, bool) => { |
| | | Vue.ls.set(DEFAULT_MULTI_TAB, bool) |
| | | state.multiTab = bool |
| | | }, |
| | | SET_HAS_ERROR: (state, bool) => { |
| | | state.hasError = bool |
| | | } |
| | | }, |
| | | actions: { |
| | | setSidebar ({ commit }, type) { |
| | | commit('SET_SIDEBAR_TYPE', type) |
| | | }, |
| | | CloseSidebar ({ commit }) { |
| | | commit('CLOSE_SIDEBAR') |
| | | }, |
| | | ToggleDevice ({ commit }, device) { |
| | | commit('TOGGLE_DEVICE', device) |
| | | }, |
| | | ToggleTheme ({ commit }, theme) { |
| | | commit('TOGGLE_THEME', theme) |
| | | }, |
| | | ToggleLayoutMode ({ commit }, mode) { |
| | | commit('TOGGLE_LAYOUT_MODE', mode) |
| | | }, |
| | | ToggleFixedHeader ({ commit }, fixedHeader) { |
| | | if (!fixedHeader) { |
| | | commit('TOGGLE_FIXED_HEADER_HIDDEN', false) |
| | | } |
| | | commit('TOGGLE_FIXED_HEADER', fixedHeader) |
| | | }, |
| | | ToggleFixSiderbar ({ commit }, fixSiderbar) { |
| | | commit('TOGGLE_FIXED_SIDERBAR', fixSiderbar) |
| | | }, |
| | | ToggleFixedHeaderHidden ({ commit }, show) { |
| | | commit('TOGGLE_FIXED_HEADER_HIDDEN', show) |
| | | }, |
| | | ToggleContentWidth ({ commit }, type) { |
| | | commit('TOGGLE_CONTENT_WIDTH', type) |
| | | }, |
| | | ToggleColor ({ commit }, color) { |
| | | commit('TOGGLE_COLOR', color) |
| | | }, |
| | | ToggleWeak ({ commit }, weakFlag) { |
| | | commit('TOGGLE_WEAK', weakFlag) |
| | | }, |
| | | ToggleMultiTab ({ commit }, bool) { |
| | | commit('TOGGLE_MULTI_TAB', bool) |
| | | }, |
| | | SetHasError ({ commit }, bool) { |
| | | commit('SET_HAS_ERROR', bool) |
| | | } |
| | | } |
| | | } |
| | | |
| | | export default app |
New file |
| | |
| | | /** |
| | | * 向后端请求用户的菜单,动态生成路由 |
| | | */ |
| | | import { constantRouterMap } from '@/config/router.config' |
| | | import { generatorDynamicRouter } from '@/router/generator-routers' |
| | | |
| | | const permission = { |
| | | state: { |
| | | routers: constantRouterMap, |
| | | addRouters: [] |
| | | }, |
| | | mutations: { |
| | | SET_ROUTERS: (state, routers) => { |
| | | state.addRouters = routers |
| | | state.routers = constantRouterMap.concat(routers) |
| | | } |
| | | }, |
| | | actions: { |
| | | GenerateRoutes ({ commit }, data) { |
| | | return new Promise(resolve => { |
| | | generatorDynamicRouter(data).then(routers => { |
| | | commit('SET_ROUTERS', routers) |
| | | resolve() |
| | | }) |
| | | }).catch(err => { |
| | | // eslint-disable-next-line no-undef |
| | | reject(err) |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | export default permission |
New file |
| | |
| | | import { asyncRouterMap, constantRouterMap } from '@/config/router.config' |
| | | |
| | | /** |
| | | * 过滤账户是否拥有某一个权限,并将菜单从加载列表移除 |
| | | * |
| | | * @param permission |
| | | * @param route |
| | | * @returns {boolean} |
| | | */ |
| | | function hasPermission (permission, route) { |
| | | // if (route.meta && route.meta.permission) { |
| | | // let flag = false |
| | | // for (let i = 0, len = permission.length; i < len; i++) { |
| | | // flag = route.meta.permission.includes(permission[i]) |
| | | // if (flag) { |
| | | // return true |
| | | // } |
| | | // } |
| | | // return false |
| | | // } |
| | | return true |
| | | } |
| | | |
| | | /** |
| | | * 单账户多角色时,使用该方法可过滤角色不存在的菜单 |
| | | * |
| | | * @param roles |
| | | * @param route |
| | | * @returns {*} |
| | | */ |
| | | // eslint-disable-next-line |
| | | function hasRole(roles, route) { |
| | | if (route.meta && route.meta.roles) { |
| | | return route.meta.roles.includes(roles.id) |
| | | } else { |
| | | return true |
| | | } |
| | | } |
| | | |
| | | function filterAsyncRouter (routerMap, roles) { |
| | | const accessedRouters = routerMap.filter(route => { |
| | | if (hasPermission(roles.permissionList, route)) { |
| | | if (route.children && route.children.length) { |
| | | route.children = filterAsyncRouter(route.children, roles) |
| | | } |
| | | return true |
| | | } |
| | | return false |
| | | }) |
| | | return accessedRouters |
| | | } |
| | | |
| | | const permission = { |
| | | state: { |
| | | routers: constantRouterMap, |
| | | addRouters: [] |
| | | }, |
| | | mutations: { |
| | | SET_ROUTERS: (state, routers) => { |
| | | state.addRouters = routers |
| | | state.routers = constantRouterMap.concat(routers) |
| | | } |
| | | }, |
| | | actions: { |
| | | GenerateRoutes ({ commit }, data) { |
| | | return new Promise(resolve => { |
| | | const { roles } = data |
| | | const accessedRouters = filterAsyncRouter(asyncRouterMap, roles) |
| | | // console.log('动态获取到的菜单列表:'+JSON.stringify(accessedRouters)) |
| | | commit('SET_ROUTERS', accessedRouters) |
| | | resolve() |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | export default permission |
New file |
| | |
| | | import Vue from 'vue' |
| | | import { login, getLoginUser, logout } from '@/api/modular/system/loginManage' |
| | | import { sysDictTypeTree } from '@/api/modular/system/dictManage' |
| | | import { sysMenuChange } from '@/api/modular/system/menuManage' |
| | | import { ACCESS_TOKEN, ALL_APPS_MENU, DICT_TYPE_TREE_DATA } from '@/store/mutation-types' |
| | | |
| | | import { welcome } from '@/utils/util' |
| | | import store from '../index' |
| | | import router from '../../router' |
| | | |
| | | const user = { |
| | | state: { |
| | | token: '', |
| | | name: '', |
| | | welcome: '', |
| | | avatar: '', |
| | | buttons: [], // 按钮权限 |
| | | admintype: '', // 是否是超管 |
| | | roles: [], |
| | | info: {} |
| | | }, |
| | | |
| | | mutations: { |
| | | SET_TOKEN: (state, token) => { |
| | | state.token = token |
| | | }, |
| | | SET_NAME: (state, { name, welcome }) => { |
| | | state.name = name |
| | | state.welcome = welcome |
| | | }, |
| | | SET_AVATAR: (state, avatar) => { |
| | | state.avatar = avatar |
| | | }, |
| | | SET_ROLES: (state, roles) => { |
| | | state.roles = roles |
| | | }, |
| | | SET_INFO: (state, info) => { |
| | | state.info = info |
| | | }, |
| | | SET_BUTTONS: (state, buttons) => { |
| | | state.buttons = buttons |
| | | }, |
| | | SET_ADMINTYPE: (state, admintype) => { |
| | | state.admintype = admintype |
| | | } |
| | | }, |
| | | |
| | | actions: { |
| | | // 登录 |
| | | Login ({ commit }, userInfo) { |
| | | return new Promise((resolve, reject) => { |
| | | login(userInfo).then(response => { |
| | | if (!response.success) { |
| | | reject(response.message) |
| | | return |
| | | } |
| | | const result = response.data |
| | | Vue.ls.set(ACCESS_TOKEN, result, 7 * 24 * 60 * 60 * 1000) |
| | | commit('SET_TOKEN', result) |
| | | resolve() |
| | | // eslint-disable-next-line handle-callback-err |
| | | }).catch(error => { |
| | | // eslint-disable-next-line prefer-promise-reject-errors |
| | | reject('后端未启动或代理错误') |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // 获取用户信息 |
| | | GetInfo ({ commit }) { |
| | | return new Promise((resolve, reject) => { |
| | | getLoginUser().then(response => { |
| | | if (response.success) { |
| | | const data = response.data |
| | | commit('SET_ADMINTYPE', data.adminType) |
| | | commit('SET_ROLES', 1) |
| | | commit('SET_BUTTONS', data.permissions) |
| | | commit('SET_INFO', data) |
| | | commit('SET_NAME', { name: data.name, welcome: welcome() }) |
| | | if (data.avatar != null) { |
| | | commit('SET_AVATAR', process.env.VUE_APP_API_BASE_URL + '/sysFileInfo/preview?id=' + data.avatar) |
| | | } |
| | | resolve(data) |
| | | } else { |
| | | // eslint-disable-next-line no-undef |
| | | reject(new Error(data.message)) |
| | | } |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // 登出 |
| | | Logout ({ commit, state }) { |
| | | return new Promise((resolve) => { |
| | | logout(state.token).then(() => { |
| | | resolve() |
| | | }).catch(() => { |
| | | resolve() |
| | | }).finally(() => { |
| | | commit('SET_TOKEN', '') |
| | | commit('SET_ROLES', []) |
| | | commit('SET_BUTTONS', []) |
| | | commit('SET_ADMINTYPE', '') |
| | | Vue.ls.remove(ACCESS_TOKEN) |
| | | Vue.ls.remove(ALL_APPS_MENU) |
| | | Vue.ls.remove(DICT_TYPE_TREE_DATA) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // 加载所有字典数据 |
| | | dictTypeData () { |
| | | return new Promise((resolve, reject) => { |
| | | sysDictTypeTree().then((data) => { |
| | | if (data.success) { |
| | | const result = data.data |
| | | Vue.ls.set(DICT_TYPE_TREE_DATA, result) |
| | | resolve() |
| | | } else { |
| | | // eslint-disable-next-line no-undef |
| | | reject(new Error(data.message)) |
| | | } |
| | | }).catch(error => { |
| | | reject(error) |
| | | }) |
| | | }) |
| | | }, |
| | | |
| | | // 切换应用菜单 |
| | | MenuChange ({ commit }, application) { |
| | | return new Promise((resolve) => { |
| | | sysMenuChange({ application: application.code }).then((res) => { |
| | | const apps = { 'code': '', 'name': '', 'active': '', 'menu': '' } |
| | | apps.active = true |
| | | apps.menu = res.data |
| | | // eslint-disable-next-line camelcase |
| | | const all_app_menu = Vue.ls.get(ALL_APPS_MENU) |
| | | // eslint-disable-next-line camelcase |
| | | const new_false_all_app_menu = [] |
| | | // 先去除所有默认的,以为此时切换的即将成为前端缓存默认的应用 |
| | | all_app_menu.forEach(item => { |
| | | if (item.active) { |
| | | item.active = false |
| | | } |
| | | new_false_all_app_menu.push(item) |
| | | }) |
| | | // 此时缓存中全部都是不默认的应用 |
| | | Vue.ls.set(ALL_APPS_MENU, new_false_all_app_menu) |
| | | apps.name = application.name |
| | | apps.code = application.code |
| | | const applocationR = [] |
| | | applocationR.push(apps) |
| | | Vue.ls.set(ALL_APPS_MENU, applocationR) |
| | | resolve(res) |
| | | const antDesignmenus = res.data |
| | | store.dispatch('GenerateRoutes', { antDesignmenus }).then(() => { |
| | | router.addRoutes(store.getters.addRouters) |
| | | }) |
| | | // 切换应用刷新整体界面,暂且取消 |
| | | // window.location.reload() |
| | | }).catch(() => { |
| | | resolve() |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | export default user |
New file |
| | |
| | | export const ACCESS_TOKEN = 'Access-Token' |
| | | export const SIDEBAR_TYPE = 'SIDEBAR_TYPE' |
| | | export const ALL_APPS_MENU = 'ALL_APPS_MENU' |
| | | export const DEFAULT_THEME = 'DEFAULT_THEME' |
| | | export const DEFAULT_LAYOUT_MODE = 'DEFAULT_LAYOUT_MODE' |
| | | export const DEFAULT_COLOR = 'DEFAULT_COLOR' |
| | | export const DEFAULT_COLOR_WEAK = 'DEFAULT_COLOR_WEAK' |
| | | export const DEFAULT_FIXED_HEADER = 'DEFAULT_FIXED_HEADER' |
| | | export const DEFAULT_FIXED_SIDEMENU = 'DEFAULT_FIXED_SIDEMENU' |
| | | export const DEFAULT_FIXED_HEADER_HIDDEN = 'DEFAULT_FIXED_HEADER_HIDDEN' |
| | | export const DEFAULT_CONTENT_WIDTH_TYPE = 'DEFAULT_CONTENT_WIDTH_TYPE' |
| | | export const DEFAULT_MULTI_TAB = 'DEFAULT_MULTI_TAB' |
| | | export const DICT_TYPE_TREE_DATA = 'DICT_TYPE_TREE_DATA' |
| | | |
| | | export const CONTENT_WIDTH_TYPE = { |
| | | Fluid: 'Fluid', |
| | | Fixed: 'Fixed' |
| | | } |
New file |
| | |
| | | import store from '@/store' |
| | | |
| | | /** |
| | | * 缓存中的已选中应用 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/06/27 02:34 |
| | | */ |
| | | export function sysApplication () { |
| | | return store.getters.applocation |
| | | } |
New file |
| | |
| | | const VueAxios = { |
| | | vm: {}, |
| | | // eslint-disable-next-line no-unused-vars |
| | | install (Vue, instance) { |
| | | if (this.installed) { |
| | | return |
| | | } |
| | | this.installed = true |
| | | |
| | | if (!instance) { |
| | | // eslint-disable-next-line no-console |
| | | console.error('You have to install axios') |
| | | return |
| | | } |
| | | |
| | | Vue.axios = instance |
| | | |
| | | Object.defineProperties(Vue.prototype, { |
| | | axios: { |
| | | get: function get () { |
| | | return instance |
| | | } |
| | | }, |
| | | $http: { |
| | | get: function get () { |
| | | return instance |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | export { |
| | | VueAxios |
| | | } |
New file |
| | |
| | | import enquireJs from 'enquire.js' |
| | | |
| | | export const DEVICE_TYPE = { |
| | | DESKTOP: 'desktop', |
| | | TABLET: 'tablet', |
| | | MOBILE: 'mobile' |
| | | } |
| | | |
| | | export const deviceEnquire = function (callback) { |
| | | const matchDesktop = { |
| | | match: () => { |
| | | callback && callback(DEVICE_TYPE.DESKTOP) |
| | | } |
| | | } |
| | | |
| | | const matchLablet = { |
| | | match: () => { |
| | | callback && callback(DEVICE_TYPE.TABLET) |
| | | } |
| | | } |
| | | |
| | | const matchMobile = { |
| | | match: () => { |
| | | callback && callback(DEVICE_TYPE.MOBILE) |
| | | } |
| | | } |
| | | |
| | | // screen and (max-width: 1087.99px) |
| | | enquireJs |
| | | .register('screen and (max-width: 576px)', matchMobile) |
| | | .register('screen and (min-width: 576px) and (max-width: 1199px)', matchLablet) |
| | | .register('screen and (min-width: 1200px)', matchDesktop) |
| | | } |
New file |
| | |
| | | export const setDocumentTitle = function (title) { |
| | | document.title = title |
| | | const ua = navigator.userAgent |
| | | // eslint-disable-next-line |
| | | const regex = /\bMicroMessenger\/([\d\.]+)/ |
| | | if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) { |
| | | const i = document.createElement('iframe') |
| | | i.src = '/favicon.ico' |
| | | i.style.display = 'none' |
| | | i.onload = function () { |
| | | setTimeout(function () { |
| | | i.remove() |
| | | }, 9) |
| | | } |
| | | document.body.appendChild(i) |
| | | } |
| | | } |
| | | |
| | | export const domTitle = 'Snowy' |
New file |
| | |
| | | import Vue from 'vue' |
| | | import { DICT_TYPE_TREE_DATA } from '@/store/mutation-types' |
| | | import moment from 'moment' |
| | | import 'moment/locale/zh-cn' |
| | | moment.locale('zh-cn') |
| | | |
| | | Vue.filter('NumberFormat', function (value) { |
| | | if (!value) { |
| | | return '0' |
| | | } |
| | | const intPartFormat = value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') // 将整数部分逢三一断 |
| | | return intPartFormat |
| | | }) |
| | | |
| | | Vue.filter('dayjs', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { |
| | | return moment(dataStr).format(pattern) |
| | | }) |
| | | |
| | | Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { |
| | | return moment(dataStr).format(pattern) |
| | | }) |
| | | |
| | | /** |
| | | * 金额格式化 ,使用方法:{{ val | Fmoney }} |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020-9-15 15:02:20 |
| | | */ |
| | | Vue.filter('Fmoney', function (val) { |
| | | // eslint-disable-next-line no-useless-escape |
| | | val = val.toString().replace(/\$|\,/g, '') |
| | | if (isNaN(val)) { |
| | | val = '0' |
| | | } |
| | | // eslint-disable-next-line eqeqeq |
| | | const sign = (val == (val = Math.abs(val))) |
| | | val = Math.floor(val * 100 + 0.50000000001) |
| | | let cents = val % 100 |
| | | val = Math.floor(val / 100).toString() |
| | | if (cents < 10) { |
| | | cents = '0' + cents |
| | | } |
| | | // eslint-disable-next-line no-undef |
| | | for (let i = 0; i < Math.floor((val.length - (1 + i)) / 3); I++) { |
| | | val = val.substring(0, val.length - (4 * i + 3)) + ',' + val.substring(val.length - (4 * i + 3)) |
| | | } |
| | | return (((sign) ? '' : '-') + val + '.' + cents) |
| | | }) |
| | | |
| | | /** |
| | | * 翻译使用方法,直接返回翻译后的name {{ code | dictType(value) }} |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020-9-15 15:02:20 |
| | | */ |
| | | Vue.filter('dictType', function (code, value) { |
| | | const dictTypeTree = Vue.ls.get(DICT_TYPE_TREE_DATA) |
| | | if (dictTypeTree === undefined) { |
| | | return '需重新登录' |
| | | } |
| | | // eslint-disable-next-line eqeqeq |
| | | const tree = dictTypeTree.filter(item => item.code == code)[0].children |
| | | if (tree === undefined || tree.length === 0) { |
| | | return '无此字典' |
| | | } |
| | | // eslint-disable-next-line eqeqeq |
| | | const values = tree.filter(item => item.code == value) |
| | | if (values.length === undefined || values.length === 0) { |
| | | return '无此字典' |
| | | } |
| | | return values[0].name |
| | | }) |
| | | |
| | | /** |
| | | * 获取某个code下字典的列表,多用于字典下拉框,使用方法:{{ code | dictData }} |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020-9-19 22:40:22 |
| | | */ |
| | | Vue.filter('dictData', function (code) { |
| | | const dictTypeTree = Vue.ls.get(DICT_TYPE_TREE_DATA) |
| | | if (dictTypeTree === undefined) { |
| | | return [] |
| | | } |
| | | // eslint-disable-next-line eqeqeq |
| | | const tree = dictTypeTree.filter(item => item.code == code)[0].children |
| | | if (tree === undefined) { |
| | | return [] |
| | | } |
| | | return tree |
| | | }) |
| | | |
| | | /** |
| | | * 获取所有字典数组 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2021-2-8 01:13 |
| | | */ |
| | | Vue.filter('dictDataAll', function () { |
| | | return Vue.ls.get(DICT_TYPE_TREE_DATA) |
| | | }) |
New file |
| | |
| | | export const PERMISSION_ENUM = { |
| | | 'add': { key: 'add', label: '新增' }, |
| | | 'delete': { key: 'delete', label: '删除' }, |
| | | 'edit': { key: 'edit', label: '修改' }, |
| | | 'query': { key: 'query', label: '查询' }, |
| | | 'get': { key: 'get', label: '详情' }, |
| | | 'enable': { key: 'enable', label: '启用' }, |
| | | 'disable': { key: 'disable', label: '禁用' }, |
| | | 'import': { key: 'import', label: '导入' }, |
| | | 'export': { key: 'export', label: '导出' } |
| | | } |
| | | |
| | | function plugin (Vue) { |
| | | if (plugin.installed) { |
| | | return |
| | | } |
| | | |
| | | !Vue.prototype.$auth && Object.defineProperties(Vue.prototype, { |
| | | $auth: { |
| | | get () { |
| | | const _this = this |
| | | return (permissions) => { |
| | | const [permission, action] = permissions.split('.') |
| | | const permissionList = _this.$store.getters.roles.permissions |
| | | return permissionList.find((val) => { |
| | | return val.permissionId === permission |
| | | }).actionList.findIndex((val) => { |
| | | return val === action |
| | | }) > -1 |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | |
| | | !Vue.prototype.$enum && Object.defineProperties(Vue.prototype, { |
| | | $enum: { |
| | | get () { |
| | | // const _this = this; |
| | | return (val) => { |
| | | let result = PERMISSION_ENUM |
| | | val && val.split('.').forEach(v => { |
| | | result = result && result[v] || null |
| | | }) |
| | | return result |
| | | } |
| | | } |
| | | } |
| | | }) |
| | | } |
| | | |
| | | export default plugin |
New file |
| | |
| | | // import Vue from 'vue' |
| | | import { deviceEnquire, DEVICE_TYPE } from '@/utils/device' |
| | | import { mapState } from 'vuex' |
| | | |
| | | // const mixinsComputed = Vue.config.optionMergeStrategies.computed |
| | | // const mixinsMethods = Vue.config.optionMergeStrategies.methods |
| | | |
| | | const mixin = { |
| | | computed: { |
| | | ...mapState({ |
| | | layoutMode: state => state.app.layout, |
| | | navTheme: state => state.app.theme, |
| | | primaryColor: state => state.app.color, |
| | | colorWeak: state => state.app.weak, |
| | | fixedHeader: state => state.app.fixedHeader, |
| | | fixSiderbar: state => state.app.fixSiderbar, |
| | | fixSidebar: state => state.app.fixSiderbar, |
| | | contentWidth: state => state.app.contentWidth, |
| | | autoHideHeader: state => state.app.autoHideHeader, |
| | | sidebarOpened: state => state.app.sidebar, |
| | | multiTab: state => state.app.multiTab |
| | | }) |
| | | }, |
| | | methods: { |
| | | isTopMenu () { |
| | | return this.layoutMode === 'topmenu' |
| | | }, |
| | | isSideMenu () { |
| | | return !this.isTopMenu() |
| | | } |
| | | } |
| | | } |
| | | |
| | | const mixinDevice = { |
| | | computed: { |
| | | ...mapState({ |
| | | device: state => state.app.device |
| | | }) |
| | | }, |
| | | methods: { |
| | | isMobile () { |
| | | return this.device === DEVICE_TYPE.MOBILE |
| | | }, |
| | | isDesktop () { |
| | | return this.device === DEVICE_TYPE.DESKTOP |
| | | }, |
| | | isTablet () { |
| | | return this.device === DEVICE_TYPE.TABLET |
| | | } |
| | | } |
| | | } |
| | | |
| | | const AppDeviceEnquire = { |
| | | mounted () { |
| | | const { $store } = this |
| | | deviceEnquire(deviceType => { |
| | | switch (deviceType) { |
| | | case DEVICE_TYPE.DESKTOP: |
| | | $store.commit('TOGGLE_DEVICE', 'desktop') |
| | | $store.dispatch('setSidebar', true) |
| | | break |
| | | case DEVICE_TYPE.TABLET: |
| | | $store.commit('TOGGLE_DEVICE', 'tablet') |
| | | $store.dispatch('setSidebar', false) |
| | | break |
| | | case DEVICE_TYPE.MOBILE: |
| | | default: |
| | | $store.commit('TOGGLE_DEVICE', 'mobile') |
| | | $store.dispatch('setSidebar', true) |
| | | break |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | |
| | | export { mixin, AppDeviceEnquire, mixinDevice } |
New file |
| | |
| | | export function handleDocType(fileType) { |
| | | let docType = '' |
| | | const fileTypesDoc = [ |
| | | 'doc', 'docm', 'docx', 'dot', 'dotm', 'dotx', 'epub', 'fodt', 'htm', 'html', 'mht', 'odt', 'ott', 'pdf', 'rtf', 'txt', 'djvu', 'xps' |
| | | ] |
| | | const fileTypesCsv = [ |
| | | 'csv', 'fods', 'ods', 'ots', 'xls', 'xlsm', 'xlsx', 'xlt', 'xltm', 'xltx' |
| | | ] |
| | | const fileTypesPPt = [ |
| | | 'fodp', 'odp', 'otp', 'pot', 'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx' |
| | | ] |
| | | if (fileTypesDoc.includes(fileType)) { |
| | | docType = 'text' |
| | | } |
| | | if (fileTypesCsv.includes(fileType)) { |
| | | docType = 'spreadsheet' |
| | | } |
| | | if (fileTypesPPt.includes(fileType)) { |
| | | docType = 'presentation' |
| | | } |
| | | return docType |
| | | } |
New file |
| | |
| | | import store from '@/store' |
| | | |
| | | export function actionToObject (json) { |
| | | try { |
| | | return JSON.parse(json) |
| | | } catch (e) { |
| | | console.log('err', e.message) |
| | | } |
| | | return [] |
| | | } |
| | | |
| | | /** |
| | | * 控制按钮是否显示 |
| | | * |
| | | * @author yubaoshan |
| | | * @date 2020/06/27 02:34 |
| | | */ |
| | | export function hasBtnPermission (permission) { |
| | | const myBtns = store.getters.buttons |
| | | const admintype = store.getters.admintype |
| | | // eslint-disable-next-line eqeqeq |
| | | if (admintype == '1') { |
| | | return true |
| | | } |
| | | return myBtns.indexOf(permission) > -1 |
| | | } |
New file |
| | |
| | | import Vue from 'vue' |
| | | import axios from 'axios' |
| | | import store from '@/store' |
| | | // import router from './router' |
| | | import { message, Modal, notification } from 'ant-design-vue' /// es/notification |
| | | import { VueAxios } from './axios' |
| | | import { ACCESS_TOKEN } from '@/store/mutation-types' |
| | | |
| | | // 创建 axios 实例 |
| | | const service = axios.create({ |
| | | baseURL: '/api', // api base_url |
| | | timeout: 6000 // 请求超时时间 |
| | | }) |
| | | |
| | | const err = (error) => { |
| | | if (error.response) { |
| | | const data = error.response.data |
| | | const token = Vue.ls.get(ACCESS_TOKEN) |
| | | |
| | | if (error.response.status === 403) { |
| | | console.log('服务器403啦,要重新登录!') |
| | | notification.error({ |
| | | message: 'Forbidden', |
| | | description: data.message |
| | | }) |
| | | } |
| | | if (error.response.status === 500) { |
| | | if (data.message.length > 0) { |
| | | message.error(data.message) |
| | | } |
| | | } |
| | | if (error.response.status === 401 && !(data.result && data.result.isLogin)) { |
| | | if (token) { |
| | | store.dispatch('Logout').then(() => { |
| | | setTimeout(() => { |
| | | window.location.reload() |
| | | }, 1500) |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | return Promise.reject(error) |
| | | } |
| | | |
| | | // request interceptor |
| | | service.interceptors.request.use(config => { |
| | | const token = Vue.ls.get(ACCESS_TOKEN) |
| | | if (token) { |
| | | config.headers['Authorization'] = 'Bearer ' + token |
| | | } |
| | | return config |
| | | }, err) |
| | | |
| | | /** |
| | | * response interceptor |
| | | * 所有请求统一返回 |
| | | */ |
| | | service.interceptors.response.use((response) => { |
| | | if (response.request.responseType === 'blob') { |
| | | return response |
| | | } |
| | | const resData = response.data |
| | | const code = response.data.code |
| | | if (!store.state.app.hasError) { |
| | | if (code === 1011006 || code === 1011007 || code === 1011008 || code === 1011009) { |
| | | Modal.error({ |
| | | title: '提示:', |
| | | content: resData.message, |
| | | okText: '重新登录', |
| | | onOk: () => { |
| | | Vue.ls.remove(ACCESS_TOKEN) |
| | | store.dispatch('SetHasError', false) |
| | | window.location.reload() |
| | | } |
| | | }) |
| | | store.dispatch('SetHasError', true) |
| | | } |
| | | if (code === 1013002 || code === 1016002 || code === 1015002) { |
| | | message.error(response.data.message) |
| | | return response.data |
| | | } |
| | | } |
| | | return resData |
| | | }, err) |
| | | |
| | | const installer = { |
| | | vm: {}, |
| | | install (Vue) { |
| | | Vue.use(VueAxios, service) |
| | | } |
| | | } |
| | | |
| | | export { |
| | | installer as VueAxios, |
| | | service as axios |
| | | } |
New file |
| | |
| | | import cloneDeep from 'lodash.clonedeep' |
| | | |
| | | export function convertRoutes (nodes) { |
| | | if (!nodes) return null |
| | | |
| | | nodes = cloneDeep(nodes) |
| | | |
| | | let queue = Array.isArray(nodes) ? nodes.concat() : [nodes] |
| | | |
| | | while (queue.length) { |
| | | const levelSize = queue.length |
| | | |
| | | for (let i = 0; i < levelSize; i++) { |
| | | const node = queue.shift() |
| | | |
| | | if (!node.children || !node.children.length) continue |
| | | |
| | | node.children.forEach(child => { |
| | | // 转化相对路径 |
| | | if (child.path[0] !== '/' && !child.path.startsWith('http')) { |
| | | child.path = node.path.replace(/(\w*)[/]*$/, `$1/${child.path}`) |
| | | } |
| | | }) |
| | | |
| | | queue = queue.concat(node.children) |
| | | } |
| | | } |
| | | |
| | | return nodes |
| | | } |
New file |
| | |
| | | export function timeFix () { |
| | | const time = new Date() |
| | | const hour = time.getHours() |
| | | return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好' |
| | | } |
| | | |
| | | export function welcome () { |
| | | const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 LOL', '我猜你可能累了'] |
| | | const index = Math.floor(Math.random() * arr.length) |
| | | return arr[index] |
| | | } |
| | | |
| | | /** |
| | | * 触发 window.resize |
| | | */ |
| | | export function triggerWindowResizeEvent () { |
| | | const event = document.createEvent('HTMLEvents') |
| | | event.initEvent('resize', true, true) |
| | | event.eventType = 'message' |
| | | window.dispatchEvent(event) |
| | | } |
| | | |
| | | export function handleScrollHeader (callback) { |
| | | let timer = 0 |
| | | |
| | | let beforeScrollTop = window.pageYOffset |
| | | callback = callback || function () {} |
| | | window.addEventListener( |
| | | 'scroll', |
| | | event => { |
| | | clearTimeout(timer) |
| | | timer = setTimeout(() => { |
| | | let direction = 'up' |
| | | const afterScrollTop = window.pageYOffset |
| | | const delta = afterScrollTop - beforeScrollTop |
| | | if (delta === 0) { |
| | | return false |
| | | } |
| | | direction = delta > 0 ? 'down' : 'up' |
| | | callback(direction) |
| | | beforeScrollTop = afterScrollTop |
| | | }, 50) |
| | | }, |
| | | false |
| | | ) |
| | | } |
| | | |
| | | export function isIE () { |
| | | const bw = window.navigator.userAgent |
| | | const compare = (s) => bw.indexOf(s) >= 0 |
| | | const ie11 = (() => 'ActiveXObject' in window)() |
| | | return compare('MSIE') || ie11 |
| | | } |
| | | |
| | | /** |
| | | * Remove loading animate |
| | | * @param id parent element id or class |
| | | * @param timeout |
| | | */ |
| | | export function removeLoadingAnimate (id = '', timeout = 1500) { |
| | | if (id === '') { |
| | | return |
| | | } |
| | | setTimeout(() => { |
| | | document.body.removeChild(document.getElementById(id)) |
| | | }, timeout) |
| | | } |
New file |
| | |
| | | .textOverflow() { |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | word-break: break-all; |
| | | } |
| | | |
| | | .textOverflowMulti(@line: 3, @bg: #fff) { |
| | | position: relative; |
| | | max-height: @line * 1.5em; |
| | | margin-right: -1em; |
| | | padding-right: 1em; |
| | | overflow: hidden; |
| | | line-height: 1.5em; |
| | | text-align: justify; |
| | | &::before { |
| | | position: absolute; |
| | | right: 14px; |
| | | bottom: 0; |
| | | padding: 0 1px; |
| | | background: @bg; |
| | | content: '...'; |
| | | } |
| | | &::after { |
| | | position: absolute; |
| | | right: 14px; |
| | | width: 1em; |
| | | height: 1em; |
| | | margin-top: 0.2em; |
| | | background: white; |
| | | content: ''; |
| | | } |
| | | } |
| | | |
| | | // mixins for clearfix |
| | | // ------------------------ |
| | | .clearfix() { |
| | | zoom: 1; |
| | | &::before, |
| | | &::after { |
| | | display: table; |
| | | content: ' '; |
| | | } |
| | | &::after { |
| | | clear: both; |
| | | height: 0; |
| | | font-size: 0; |
| | | visibility: hidden; |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <div> |
| | | 404 page |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: '404' |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="新增代码生成配置" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="生成表" |
| | | has-feedback |
| | | > |
| | | <a-select style="width: 100%" placeholder="请选择数据库表" v-decorator="['tableName', {rules: [{ required: true, message: '请选择数据库表!' }]}]" > |
| | | <a-select-option v-for="(item,index) in tableNameData" :key="index" :value="item.tableName" @click="tableNameSele(item)">{{ item.tableName }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="移除前缀" |
| | | > |
| | | <a-radio-group v-decorator="['tablePrefix',{rules: [{ required: true, message: '请选择是否移除前缀!' }]}]" > |
| | | <a-radio v-for="(item,index) in tablePrefixData" :key="index" :value="item.code" @click="tablePrefixRadio(item.code)">{{ item.name }}</a-radio> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="功能名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入功能名" v-decorator="['tableComment', {rules: [{required: true, message: '请输入功能名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="类名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入类名" v-decorator="['className', {rules: [{required: true, message: '请输入类名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="业务名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入业务名" v-decorator="['busName', {rules: [{required: true, message: '请输入业务名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="生成方式" |
| | | > |
| | | <a-radio-group v-decorator="['generateType',{rules: [{ required: true, message: '请选择生成方式!' }]}]" > |
| | | <a-radio v-for="(item,index) in generateTypeData" :key="index" :value="item.code" @click="generateTypeRadio(item.code)">{{ item.name }}</a-radio> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="所属应用" |
| | | has-feedback |
| | | > |
| | | <a-select style="width: 100%" placeholder="请选择应用分类" v-decorator="['appCode', {rules: [{ required: true, message: '请选择应用分类!' }]}]" > |
| | | <a-select-option v-for="(item,index) in appData" :key="index" :value="item.code" @click="changeApplication(item.code)">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="父级菜单" |
| | | has-feedback |
| | | > |
| | | <a-tree-select |
| | | v-decorator="['menuPid', {rules: [{ required: true, message: '请选择父级菜单!' }]}]" |
| | | style="width: 100%" |
| | | :dropdownStyle="{ maxHeight: '300px', overflow: 'auto' }" |
| | | :treeData="menuTreeData" |
| | | placeholder="请选择父级菜单" |
| | | treeDefaultExpandAll |
| | | > |
| | | <span slot="title" slot-scope="{ id }">{{ id }} |
| | | </span> |
| | | </a-tree-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="作者姓名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入作者姓名" v-decorator="['authorName', {rules: [{required: true, message: '请输入作者姓名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24" v-show="packageNameShow"> |
| | | <a-form-item |
| | | label="代码包名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入代码包名" v-decorator="['packageName', {rules: [{required: true, message: '请输入代码包名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | import { codeGenerateInformationList, codeGenerateAdd } from '@/api/modular/gen/codeGenerateManage' |
| | | import { getAppList } from '@/api/modular/system/appManage' |
| | | import { getMenuTree } from '@/api/modular/system/menuManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | visible: false, |
| | | tableNameData: [], |
| | | tablePrefixData: [], |
| | | generateTypeData: [], |
| | | confirmLoading: false, |
| | | tablePrefixValue: 'N', |
| | | tableNameValue: '', |
| | | packageNameShow: true, |
| | | appData: [], |
| | | menuTreeData: [], |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | add () { |
| | | this.visible = true |
| | | this.codeGenerateInformationList() |
| | | this.dataTypeItem() |
| | | this.selectedByDefault() |
| | | this.getSysApplist() |
| | | }, |
| | | /** |
| | | * 默认选中项 |
| | | */ |
| | | selectedByDefault () { |
| | | this.form.getFieldDecorator('packageName', { initialValue: 'vip.xiaonuo' }) |
| | | this.form.getFieldDecorator('tablePrefix', { valuePropName: 'checked', initialValue: 'N' }) |
| | | this.form.getFieldDecorator('generateType', { valuePropName: 'checked', initialValue: '1' }) |
| | | this.tablePrefixValue = 'N' |
| | | }, |
| | | /** |
| | | * 获得所有数据库的表 |
| | | */ |
| | | codeGenerateInformationList () { |
| | | codeGenerateInformationList().then((res) => { |
| | | this.tableNameData = res.data |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取应用列表 |
| | | */ |
| | | getSysApplist () { |
| | | return getAppList().then((res) => { |
| | | if (res.success) { |
| | | this.appData = res.data |
| | | } else { |
| | | this.$message.warning(res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 通过应用获取菜单 |
| | | */ |
| | | changeApplication (value) { |
| | | this.form.resetFields(`menuPid`, []) |
| | | getMenuTree({ 'application': value }).then((res) => { |
| | | if (res.success) { |
| | | this.menuTreeData = [{ |
| | | 'id': '-1', |
| | | 'parentId': '0', |
| | | 'title': '顶级', |
| | | 'value': '0', |
| | | 'pid': '0', |
| | | 'children': res.data |
| | | }] |
| | | } else { |
| | | this.$message.warning(res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取字典数据 |
| | | */ |
| | | dataTypeItem () { |
| | | this.tablePrefixData = this.$options.filters['dictData']('yes_or_no') |
| | | this.generateTypeData = this.$options.filters['dictData']('code_gen_create_type') |
| | | }, |
| | | /** |
| | | * 提交表单 |
| | | */ |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | this.confirmLoading = true |
| | | codeGenerateAdd(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('新增成功') |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('新增失败:' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | // 清空他们三个 |
| | | this.form.getFieldDecorator('className', { initialValue: '' }) |
| | | this.form.getFieldDecorator('busName', { initialValue: '' }) |
| | | this.form.getFieldDecorator('tableComment', { initialValue: '' }) |
| | | }, |
| | | /** |
| | | * 选择数据库列表 |
| | | */ |
| | | tableNameSele (item) { |
| | | this.tableNameValue = item.tableName |
| | | this.form.getFieldDecorator('tableComment', { initialValue: item.tableComment }) |
| | | this.settingDefaultValue() |
| | | }, |
| | | /** |
| | | * 选择是否移除前缀触发 |
| | | */ |
| | | tablePrefixRadio (tablePrefixType) { |
| | | this.tablePrefixValue = tablePrefixType |
| | | this.settingDefaultValue() |
| | | }, |
| | | /** |
| | | * 设置默认值 |
| | | */ |
| | | settingDefaultValue () { |
| | | const tableName = this.classNameToHump() |
| | | this.form.getFieldDecorator('className', { initialValue: tableName }) |
| | | this.form.getFieldDecorator('busName', { initialValue: tableName.toLowerCase() }) |
| | | }, |
| | | /** |
| | | * 设置类名为数据库表的驼峰命名 |
| | | */ |
| | | classNameToHump () { |
| | | const arr = this.tableNameValue.toLowerCase().split('_') |
| | | if (this.tablePrefixValue === 'Y') { |
| | | arr.splice(0, 1) |
| | | } |
| | | for (let i = 0; i < arr.length; i++) { |
| | | // charAt()方法得到第一个字母,slice()得到第二个字母以后的字符串 |
| | | arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1) |
| | | } |
| | | return arr.join('') |
| | | }, |
| | | /** |
| | | * 选择生成方式 |
| | | */ |
| | | generateTypeRadio (generateType) { |
| | | if (generateType === '1') { |
| | | this.packageNameShow = true |
| | | } else { |
| | | this.packageNameShow = false |
| | | this.form.setFieldsValue({ packageName: 'vip.xiaonuo' }) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="编辑代码生成配置" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item v-show="false"><a-input v-decorator="['id']" /></a-form-item> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="生成表" |
| | | has-feedback |
| | | > |
| | | <a-select style="width: 100%" placeholder="请选择数据库表" v-decorator="['tableName', {rules: [{ required: true, message: '请选择数据库表!' }]}]" > |
| | | <a-select-option v-for="(item,index) in tableNameData" :key="index" :value="item.tableName" @click="tableNameSele(item)">{{ item.tableName }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="移除前缀" |
| | | > |
| | | <a-radio-group v-decorator="['tablePrefix',{rules: [{ required: true, message: '请选择是否移除前缀!' }]}]" > |
| | | <a-radio v-for="(item,index) in tablePrefixData" :key="index" :value="item.code" @click="tablePrefixRadio(item.code)">{{ item.name }}</a-radio> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="功能名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入功能名" v-decorator="['tableComment', {rules: [{required: true, message: '请输入功能名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="类名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入类名" v-decorator="['className', {rules: [{required: true, message: '请输入类名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="业务名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入业务名" v-decorator="['busName', {rules: [{required: true, message: '请输入业务名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="生成方式" |
| | | > |
| | | <a-radio-group v-decorator="['generateType',{rules: [{ required: true, message: '请选择生成方式!' }]}]" > |
| | | <a-radio v-for="(item,index) in generateTypeData" :key="index" :value="item.code" @click="generateTypeRadio(item.code)">{{ item.name }}</a-radio> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="所属应用" |
| | | has-feedback |
| | | > |
| | | <a-select style="width: 100%" placeholder="请选择应用分类" v-decorator="['appCode', {rules: [{ required: true, message: '请选择应用分类!' }]}]" > |
| | | <a-select-option v-for="(item,index) in appData" :key="index" :value="item.code" @click="changeApplication(item.code)">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="父级菜单" |
| | | has-feedback |
| | | > |
| | | <a-tree-select |
| | | v-decorator="['menuPid', {rules: [{ required: true, message: '请选择父级菜单!' }]}]" |
| | | style="width: 100%" |
| | | :dropdownStyle="{ maxHeight: '300px', overflow: 'auto' }" |
| | | :treeData="menuTreeData" |
| | | placeholder="请选择父级菜单" |
| | | treeDefaultExpandAll |
| | | > |
| | | <span slot="title" slot-scope="{ id }">{{ id }} |
| | | </span> |
| | | </a-tree-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="12" :sm="24"> |
| | | <a-form-item |
| | | label="作者姓名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入作者姓名" v-decorator="['authorName', {rules: [{required: true, message: '请输入作者姓名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="12" :sm="24" v-show="packageNameShow"> |
| | | <a-form-item |
| | | label="代码包名" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入代码包名" v-decorator="['packageName', {rules: [{required: true, message: '请输入代码包名!'}]}]" /> |
| | | </a-form-item> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | import { codeGenerateInformationList, codeGenerateEdit } from '@/api/modular/gen/codeGenerateManage' |
| | | import { getAppList } from '@/api/modular/system/appManage' |
| | | import { getMenuTree } from '@/api/modular/system/menuManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | visible: false, |
| | | tableNameData: [], |
| | | tablePrefixData: [], |
| | | generateTypeData: [], |
| | | confirmLoading: false, |
| | | tablePrefixValue: 'N', |
| | | tableNameValue: '', |
| | | packageNameShow: true, |
| | | appData: [], |
| | | menuTreeData: [], |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | edit (record) { |
| | | this.visible = true |
| | | this.codeGenerateInformationList() |
| | | this.dataTypeItem() |
| | | this.getSysApplist() |
| | | setTimeout(() => { |
| | | this.form.setFieldsValue( |
| | | { |
| | | id: record.id, |
| | | tableName: record.tableName, |
| | | tablePrefix: record.tablePrefix, |
| | | tableComment: record.tableComment, |
| | | className: record.className, |
| | | busName: record.busName, |
| | | generateType: record.generateType, |
| | | authorName: record.authorName, |
| | | packageName: record.packageName, |
| | | appCode: record.appCode, |
| | | menuPid: record.menuPid |
| | | } |
| | | ) |
| | | }, 100) |
| | | this.changeApplication(record.appCode) |
| | | this.tableNameValue = record.tableName |
| | | this.tablePrefixValue = record.tablePrefix |
| | | }, |
| | | /** |
| | | * 获得所有数据库的表 |
| | | */ |
| | | codeGenerateInformationList () { |
| | | codeGenerateInformationList().then((res) => { |
| | | this.tableNameData = res.data |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取应用列表 |
| | | */ |
| | | getSysApplist () { |
| | | return getAppList().then((res) => { |
| | | if (res.success) { |
| | | this.appData = res.data |
| | | } else { |
| | | this.$message.warning(res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 通过应用获取菜单 |
| | | */ |
| | | changeApplication (value) { |
| | | this.form.resetFields(`menuPid`, []) |
| | | getMenuTree({ 'application': value }).then((res) => { |
| | | if (res.success) { |
| | | this.menuTreeData = [{ |
| | | 'id': '-1', |
| | | 'parentId': '0', |
| | | 'title': '顶级', |
| | | 'value': '0', |
| | | 'pid': '0', |
| | | 'children': res.data |
| | | }] |
| | | } else { |
| | | this.$message.warning(res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 获取字典数据 |
| | | */ |
| | | dataTypeItem () { |
| | | this.tablePrefixData = this.$options.filters['dictData']('yes_or_no') |
| | | this.generateTypeData = this.$options.filters['dictData']('code_gen_create_type') |
| | | }, |
| | | /** |
| | | * 提交表单 |
| | | */ |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | this.confirmLoading = true |
| | | codeGenerateEdit(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('编辑成功') |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('编辑失败' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | }, |
| | | /** |
| | | * 选择数据库列表 |
| | | */ |
| | | tableNameSele (item) { |
| | | this.tableNameValue = item.tableName |
| | | this.form.setFieldsValue({ className: item.tableComment }) |
| | | this.settingDefaultValue() |
| | | }, |
| | | /** |
| | | * 选择是否移除前缀触发 |
| | | */ |
| | | tablePrefixRadio (tablePrefixType) { |
| | | this.tablePrefixValue = tablePrefixType |
| | | this.settingDefaultValue() |
| | | }, |
| | | /** |
| | | * 设置默认值 |
| | | */ |
| | | settingDefaultValue () { |
| | | const tableName = this.classNameToHump() |
| | | this.form.setFieldsValue( |
| | | { |
| | | className: tableName, |
| | | busName: tableName.toLowerCase() |
| | | } |
| | | ) |
| | | }, |
| | | /** |
| | | * 设置类名为数据库表的驼峰命名 |
| | | */ |
| | | classNameToHump () { |
| | | const arr = this.tableNameValue.toLowerCase().split('_') |
| | | if (this.tablePrefixValue === 'Y') { |
| | | arr.splice(0, 1) |
| | | } |
| | | for (let i = 0; i < arr.length; i++) { |
| | | // charAt()方法得到第一个字母,slice()得到第二个字母以后的字符串 |
| | | arr[i] = arr[i].charAt(0).toUpperCase() + arr[i].slice(1) |
| | | } |
| | | return arr.join('') |
| | | }, |
| | | /** |
| | | * 选择生成方式 |
| | | */ |
| | | generateTypeRadio (generateType) { |
| | | if (generateType === '1') { |
| | | this.packageNameShow = true |
| | | } else { |
| | | this.packageNameShow = false |
| | | this.form.setFieldsValue({ packageName: 'vip.xiaonuo' }) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <a-card :bordered="false" v-show="indexOpenShow" :bodyStyle="tstyle"> |
| | | <a-spin :spinning="Loading"> |
| | | <div class="table-page-search-wrapper" v-if="hasPerm('codeGenerate:page')"> |
| | | <a-form layout="inline"> |
| | | <a-row :gutter="48"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="表名称" > |
| | | <a-input v-model="queryParam.tableName" allow-clear placeholder="请输入表名称"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-button type="primary" @click="$refs.table.refresh(true)">查询</a-button> |
| | | <a-button style="margin-left: 8px" @click="() => queryParam = {}">重置</a-button> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </div> |
| | | </a-spin> |
| | | </a-card> |
| | | <a-card :bordered="false" v-show="indexOpenShow"> |
| | | <a-spin :spinning="Loading"> |
| | | <s-table |
| | | ref="table" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | :alert="true" |
| | | :rowKey="(record) => record.id" |
| | | :rowSelection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }" |
| | | > |
| | | <div slot="operator" v-if="hasPerm('codeGenerate:add')" > |
| | | <a-button type="primary" v-if="hasPerm('codeGenerate:add')" icon="plus" @click="$refs.addForm.add()">新增</a-button> |
| | | </div> |
| | | <span slot="tableName" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="packageName" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="busName" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="className" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="tableComment" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="tablePrefix" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ 'yes_or_no' | dictType(text) }}</ellipsis> |
| | | </span> |
| | | <span slot="generateType" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ 'code_gen_create_type' | dictType(text) }}</ellipsis> |
| | | </span> |
| | | <span slot="action" slot-scope="text, record"> |
| | | <span v-if="record.generateType === '1'"> |
| | | <a v-if="hasPerm('codeGenerate:runDown')" @click="runDownCodeGenerate(record)">开始生成</a> |
| | | </span> |
| | | <span v-else> |
| | | <a-popconfirm v-if="hasPerm('codeGenerate:runLocal')" placement="topRight" title="确定生成代码到本项目?" @confirm="() => runLocalCodeGenerate(record)"> |
| | | <a>开始生成</a> |
| | | </a-popconfirm> |
| | | </span> |
| | | <a-divider type="vertical" v-if="hasPerm('codeGenerate:config') & hasPerm('codeGenerate:runLocal') || hasPerm('codeGenerate:runDown') "/> |
| | | <a v-if="hasPerm('codeGenerate:config')" @click="indexConfigOpen(record)">配置</a> |
| | | <a-divider type="vertical" v-if="hasPerm('codeGenerate:config') & hasPerm('codeGenerate:edit')"/> |
| | | <a v-if="hasPerm('codeGenerate:edit')" @click="$refs.editForm.edit(record)">编辑</a> |
| | | <a-divider type="vertical" v-if="hasPerm('codeGenerate:edit') & hasPerm('codeGenerate:delete')"/> |
| | | <a-popconfirm v-if="hasPerm('codeGenerate:delete')" placement="topRight" title="确认删除?" @confirm="() => codeGenerateDelete(record)"> |
| | | <a>删除</a> |
| | | </a-popconfirm> |
| | | </span> |
| | | </s-table> |
| | | <add-form ref="addForm" @ok="handleOk" v-if="hasPerm('codeGenerate:add')"/> |
| | | <edit-form ref="editForm" @ok="handleOk" v-if="hasPerm('codeGenerate:edit')"/> |
| | | </a-spin> |
| | | </a-card> |
| | | <index-config ref="indexConfig" @ok="handleResetOpen" v-if="hasPerm('codeGenerate:config')"/> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { STable, Ellipsis } from '@/components' |
| | | import { codeGeneratePage, codeGenerateDelete, codeGenerateRunDown, codeGenerateRunLocal } from '@/api/modular/gen/codeGenerateManage' |
| | | import addForm from './addForm' |
| | | import editForm from './editForm' |
| | | import indexConfig from './indexConfig' |
| | | |
| | | export default { |
| | | components: { |
| | | indexConfig, |
| | | STable, |
| | | Ellipsis, |
| | | addForm, |
| | | editForm |
| | | }, |
| | | data () { |
| | | return { |
| | | // 查询参数 |
| | | queryParam: {}, |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | | title: '表名称', |
| | | dataIndex: 'tableName', |
| | | scopedSlots: { customRender: 'tableName' } |
| | | }, |
| | | { |
| | | title: '代码包名', |
| | | dataIndex: 'packageName', |
| | | scopedSlots: { customRender: 'packageName' } |
| | | }, |
| | | { |
| | | title: '业务名', |
| | | dataIndex: 'busName', |
| | | scopedSlots: { customRender: 'busName' } |
| | | }, |
| | | { |
| | | title: '类名', |
| | | dataIndex: 'className', |
| | | scopedSlots: { customRender: 'className' } |
| | | }, |
| | | { |
| | | title: '功能名', |
| | | dataIndex: 'tableComment', |
| | | scopedSlots: { customRender: 'tableComment' } |
| | | }, |
| | | { |
| | | title: '作者姓名', |
| | | dataIndex: 'authorName' |
| | | }, |
| | | { |
| | | title: '表前缀移除', |
| | | dataIndex: 'tablePrefix', |
| | | scopedSlots: { customRender: 'tablePrefix' } |
| | | }, |
| | | { |
| | | title: '生成方式', |
| | | dataIndex: 'generateType', |
| | | scopedSlots: { customRender: 'generateType' } |
| | | } |
| | | ], |
| | | tstyle: { 'padding-bottom': '0px', 'margin-bottom': '10px' }, |
| | | loadData: parameter => { |
| | | return codeGeneratePage(Object.assign(parameter, this.queryParam)).then((res) => { |
| | | return res.data |
| | | }) |
| | | }, |
| | | selectedRowKeys: [], |
| | | selectedRows: [], |
| | | Loading: false, |
| | | jdbcDriverList: [], |
| | | indexOpenShow: true |
| | | } |
| | | }, |
| | | created () { |
| | | if (this.hasPerm('codeGenerate:edit') || this.hasPerm('codeGenerate:delete')) { |
| | | this.columns.push({ |
| | | title: '操作', |
| | | width: '230px', |
| | | dataIndex: 'action', |
| | | scopedSlots: { customRender: 'action' } |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 开始生成代码(生成压缩包) |
| | | */ |
| | | runDownCodeGenerate (record) { |
| | | this.Loading = true |
| | | codeGenerateRunDown({ id: record.id }).then((res) => { |
| | | this.Loading = false |
| | | this.downloadfile(res) |
| | | // eslint-disable-next-line handle-callback-err |
| | | }).catch((err) => { |
| | | this.Loading = false |
| | | this.$message.error('下载错误:获取文件流错误') |
| | | }) |
| | | }, |
| | | downloadfile (res) { |
| | | var blob = new Blob([res.data], { type: 'application/octet-stream;charset=UTF-8' }) |
| | | var contentDisposition = res.headers['content-disposition'] |
| | | var patt = new RegExp('filename=([^;]+\\.[^\\.;]+);*') |
| | | var result = patt.exec(contentDisposition) |
| | | var filename = result[1] |
| | | var downloadElement = document.createElement('a') |
| | | var href = window.URL.createObjectURL(blob) // 创建下载的链接 |
| | | var reg = /^["](.*)["]$/g |
| | | downloadElement.style.display = 'none' |
| | | downloadElement.href = href |
| | | downloadElement.download = decodeURI(filename.replace(reg, '$1')) // 下载后文件名 |
| | | document.body.appendChild(downloadElement) |
| | | downloadElement.click() // 点击下载 |
| | | document.body.removeChild(downloadElement) // 下载完成移除元素 |
| | | window.URL.revokeObjectURL(href) |
| | | }, |
| | | /** |
| | | * 开始生成代码(本地项目) |
| | | */ |
| | | runLocalCodeGenerate (record) { |
| | | codeGenerateRunLocal(record).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('生成成功') |
| | | this.$refs.table.refresh() |
| | | } else { |
| | | this.$message.error('生成失败:' + res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 删除 |
| | | */ |
| | | codeGenerateDelete (record) { |
| | | this.Loading = true |
| | | codeGenerateDelete([{ id: record.id }]).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.$refs.table.refresh() |
| | | } else { |
| | | this.$message.error('删除失败:' + res.message) |
| | | } |
| | | }).catch((err) => { |
| | | this.$message.error('删除错误:' + err.message) |
| | | }).finally((res) => { |
| | | this.Loading = false |
| | | }) |
| | | }, |
| | | /** |
| | | * 打开配置界面 |
| | | */ |
| | | indexConfigOpen (record) { |
| | | this.indexOpenShow = false |
| | | this.$refs.indexConfig.open(record) |
| | | }, |
| | | /** |
| | | * 详细配置界面返回 |
| | | */ |
| | | handleResetOpen () { |
| | | this.indexOpenShow = true |
| | | this.$refs.table.refresh() |
| | | }, |
| | | /** |
| | | * 其他界面返回 |
| | | */ |
| | | handleOk () { |
| | | this.$refs.table.refresh() |
| | | }, |
| | | onSelectChange (selectedRowKeys, selectedRows) { |
| | | this.selectedRowKeys = selectedRowKeys |
| | | this.selectedRows = selectedRows |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | .table-operator { |
| | | margin-bottom: 18px; |
| | | } |
| | | button { |
| | | margin-right: 8px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-card :bordered="false" v-show="indexConfigShow"> |
| | | <div class="table-operator"> |
| | | <a-button class="but_item" type="dashed" @click="handleCancel" icon="rollback">返回</a-button> |
| | | <a-button type="primary" icon="plus" @click="handleSubmit">保存</a-button> |
| | | </div> |
| | | <a-table |
| | | ref="table" |
| | | size="middle" |
| | | :columns="columns" |
| | | :dataSource="loadData" |
| | | :pagination="false" |
| | | :alert="true" |
| | | :loading="tableLoading" |
| | | :rowKey="(record) => record.id" |
| | | > |
| | | <template slot="columnComment" slot-scope="text, record"> |
| | | <a-input v-model="record.columnComment" /> |
| | | </template> |
| | | <template slot="javaType" slot-scope="text, record"> |
| | | <a-select style="width: 120px" v-model="record.javaType" :disabled="judgeColumns(record)"> |
| | | <a-select-option v-for="(item,index) in javaTypeData" :key="index" :value="item.code">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </template> |
| | | <template slot="effectType" slot-scope="text, record"> |
| | | <a-select style="width: 120px" v-model="record.effectType" :disabled="judgeColumns(record)"> |
| | | <a-select-option v-for="(item,index) in effectTypeData" :key="index" :value="item.code">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </template> |
| | | <template slot="dictTypeCode" slot-scope="text, record"> |
| | | <a-select |
| | | style="width: 120px" |
| | | v-model="record.dictTypeCode" |
| | | :disabled="record.effectType !== 'radio' && |
| | | record.effectType !== 'select' && record.effectType !== 'checkbox'"> |
| | | <a-select-option v-for="(item,index) in dictDataAll" :key="index" :value="item.code">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </template> |
| | | <template slot="whetherTable" slot-scope="text, record"> |
| | | <a-checkbox v-model="record.whetherTable"/> |
| | | </template> |
| | | <template slot="whetherRetract" slot-scope="text, record"> |
| | | <a-checkbox v-model="record.whetherRetract"/> |
| | | </template> |
| | | <template slot="whetherAddUpdate" slot-scope="text, record"> |
| | | <a-checkbox v-model="record.whetherAddUpdate" :disabled="judgeColumns(record)"/> |
| | | </template> |
| | | <template slot="whetherRequired" slot-scope="text, record"> |
| | | <a-checkbox v-model="record.whetherRequired" :disabled="judgeColumns(record)"/> |
| | | </template> |
| | | <template slot="queryWhether" slot-scope="text, record"> |
| | | <a-switch v-model="record.queryWhether"> |
| | | <a-icon slot="checkedChildren" type="check" /> |
| | | <a-icon slot="unCheckedChildren" type="close" /> |
| | | </a-switch> |
| | | </template> |
| | | <template slot="queryType" slot-scope="text, record"> |
| | | <a-select style="width: 100px" v-model="record.queryType" :disabled="!record.queryWhether"> |
| | | <a-select-option v-for="(item,index) in codeGenQueryTypeData" :key="index" :value="item.code">{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </template> |
| | | </a-table> |
| | | </a-card> |
| | | </template> |
| | | <script> |
| | | import { sysCodeGenerateConfigList, sysCodeGenerateConfigEdit } from '@/api/modular/gen/sysCodeGenerateConfigManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | | title: 'java字段', |
| | | dataIndex: 'javaName' |
| | | }, |
| | | { |
| | | title: '字段描述', |
| | | dataIndex: 'columnComment', |
| | | scopedSlots: { customRender: 'columnComment' } |
| | | }, |
| | | { |
| | | title: '物理类型', |
| | | dataIndex: 'dataType' |
| | | }, |
| | | { |
| | | title: 'java类型', |
| | | dataIndex: 'javaType', |
| | | scopedSlots: { customRender: 'javaType' } |
| | | }, |
| | | { |
| | | title: '作用类型', |
| | | dataIndex: 'effectType', |
| | | scopedSlots: { customRender: 'effectType' } |
| | | }, |
| | | { |
| | | title: '字典', |
| | | dataIndex: 'dictTypeCode', |
| | | scopedSlots: { customRender: 'dictTypeCode' } |
| | | }, |
| | | { |
| | | title: '列表显示', |
| | | align: 'center', |
| | | dataIndex: 'whetherTable', |
| | | scopedSlots: { customRender: 'whetherTable' } |
| | | }, |
| | | { |
| | | title: '列字段省略', |
| | | align: 'center', |
| | | dataIndex: 'whetherRetract', |
| | | scopedSlots: { customRender: 'whetherRetract' } |
| | | }, |
| | | { |
| | | title: '增改', |
| | | align: 'center', |
| | | dataIndex: 'whetherAddUpdate', |
| | | scopedSlots: { customRender: 'whetherAddUpdate' } |
| | | }, |
| | | { |
| | | title: '必填', |
| | | align: 'center', |
| | | dataIndex: 'whetherRequired', |
| | | scopedSlots: { customRender: 'whetherRequired' } |
| | | }, |
| | | { |
| | | title: '是否是查询', |
| | | align: 'center', |
| | | dataIndex: 'queryWhether', |
| | | scopedSlots: { customRender: 'queryWhether' } |
| | | }, |
| | | { |
| | | title: '查询方式', |
| | | dataIndex: 'queryType', |
| | | scopedSlots: { customRender: 'queryType' } |
| | | } |
| | | ], |
| | | indexConfigShow: false, |
| | | tableLoading: false, |
| | | visible: false, |
| | | loadData: [], |
| | | javaTypeData: [], |
| | | effectTypeData: [], |
| | | dictDataAll: [], |
| | | codeGenQueryTypeData: [], |
| | | yesOrNoData: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 打开界面 |
| | | */ |
| | | open (data) { |
| | | this.indexConfigShow = true |
| | | this.tableLoading = true |
| | | const dictOption = this.$options |
| | | this.javaTypeData = dictOption.filters['dictData']('code_gen_java_type') |
| | | this.effectTypeData = dictOption.filters['dictData']('code_gen_effect_type') |
| | | this.dictDataAll = dictOption.filters['dictDataAll']() |
| | | this.yesOrNoData = dictOption.filters['dictData']('yes_or_no') |
| | | this.codeGenQueryTypeData = dictOption.filters['dictData']('code_gen_query_type') |
| | | const params = { |
| | | codeGenId: data.id |
| | | } |
| | | sysCodeGenerateConfigList(params).then((res) => { |
| | | this.loadData = res.data |
| | | this.loadData.forEach(item => { |
| | | for (const key in item) { |
| | | if (item[key] === 'Y') { |
| | | item[key] = true |
| | | } |
| | | if (item[key] === 'N') { |
| | | item[key] = false |
| | | } |
| | | } |
| | | }) |
| | | this.tableLoading = false |
| | | }) |
| | | }, |
| | | /** |
| | | * 提交表单 |
| | | */ |
| | | handleSubmit () { |
| | | this.tableLoading = true |
| | | // 做数组属性转换, 咱先来一个切断双向绑定,学习的童鞋下回记下啊 |
| | | // eslint-disable-next-line prefer-const |
| | | let loadDatas = JSON.parse(JSON.stringify(this.loadData)) |
| | | loadDatas.forEach(item => { |
| | | // 必填那一项转换 |
| | | for (const key in item) { |
| | | if (item[key] === true) { |
| | | item[key] = 'Y' |
| | | } |
| | | if (item[key] === false) { |
| | | item[key] = 'N' |
| | | } |
| | | } |
| | | }) |
| | | const param = { |
| | | sysCodeGenerateConfigParamList: loadDatas |
| | | } |
| | | sysCodeGenerateConfigEdit(param).then((res) => { |
| | | this.tableLoading = false |
| | | if (res.success) { |
| | | this.$message.success('编辑成功') |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('编辑失败:' + res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 判断是否(用于是否能选择或输入等) |
| | | */ |
| | | judgeColumns (data) { |
| | | if (data.columnName.indexOf('create_user') > -1 || |
| | | data.columnName.indexOf('create_time') > -1 || |
| | | data.columnName.indexOf('update_user') > -1 || |
| | | data.columnName.indexOf('update_time') > -1 || |
| | | data.columnKey === 'PRI') { |
| | | return true |
| | | } |
| | | return false |
| | | }, |
| | | handleCancel () { |
| | | this.$emit('ok') |
| | | this.loadData = [] |
| | | this.indexConfigShow = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | /** 您的业务接口文件全写在此文件夹下面,升级底座直接迁移代码即可 **/ |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="新增blog文章主体" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item |
| | | label="文章标题" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章标题" v-decorator="['title', {rules: [{required: true, message: '请输入文章标题!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章文件id" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章文件id" v-decorator="['articleFileId', {rules: [{required: false, message: '请输入文章文件id!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文件类型 1:markdown 2:html" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文件类型 1:markdown 2:html" v-decorator="['articleFileType', {rules: [{required: true, message: '请输入文件类型 1:markdown 2:html!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章分类id 0:没有分类" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章分类id 0:没有分类" v-decorator="['articleTypeId', {rules: [{required: true, message: '请输入文章分类id 0:没有分类!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章引言" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章引言" v-decorator="['introduce', {rules: [{required: true, message: '请输入文章引言!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="封面文件地址(id)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入封面文件地址(id)" v-decorator="['coverFileId', {rules: [{required: true, message: '请输入封面文件地址(id)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="上次编辑时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择上次编辑时间" v-decorator="['lastEditorDate',{rules: [{ required: true, message: '请选择上次编辑时间!' }]}]" @change="lastEditorDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="发布时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择发布时间" v-decorator="['publishDate',{rules: [{ required: true, message: '请选择发布时间!' }]}]" @change="publishDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="是否置顶 0:否 1:是" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入是否置顶 0:否 1:是" v-decorator="['isTop', {rules: [{required: true, message: '请输入是否置顶 0:否 1:是!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="置顶值(越小越靠前)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入置顶值(越小越靠前)" v-decorator="['topValue', {rules: [{required: true, message: '请输入置顶值(越小越靠前)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="公开状态 1:公开 2:私密 3:密码授权" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入公开状态 1:公开 2:私密 3:密码授权" v-decorator="['authStatus', {rules: [{required: true, message: '请输入公开状态 1:公开 2:私密 3:密码授权!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="授权密码(在密码授权状态时)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入授权密码(在密码授权状态时)" v-decorator="['authPassword', {rules: [{required: true, message: '请输入授权密码(在密码授权状态时)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="编辑状态 0:草稿 1:发布" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入编辑状态 0:草稿 1:发布" v-decorator="['editorStatus', {rules: [{required: true, message: '请输入编辑状态 0:草稿 1:发布!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档年份(以初次发布时间为准)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档年份(以初次发布时间为准)" v-decorator="['separateYear', {rules: [{required: true, message: '请输入归档年份(以初次发布时间为准)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档月份" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档月份" v-decorator="['separateMonth', {rules: [{required: true, message: '请输入归档月份!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档日" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档日" v-decorator="['separateDay', {rules: [{required: true, message: '请输入归档日!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="是否启用 0:否 1:是" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入是否启用 0:否 1:是" v-decorator="['isEnable', {rules: [{required: true, message: '请输入是否启用 0:否 1:是!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="更新时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择更新时间" v-decorator="['updateDate',{rules: [{ required: true, message: '请选择更新时间!' }]}]" @change="updateDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="创建时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择创建时间" v-decorator="['createDate',{rules: [{ required: true, message: '请选择创建时间!' }]}]" @change="createDateOnChange"/> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | import { blogArticleAdd } from '@/api/modular/main/blogarticle/blogArticleManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | lastEditorDateDateString: '', |
| | | publishDateDateString: '', |
| | | updateDateDateString: '', |
| | | createDateDateString: '', |
| | | visible: false, |
| | | confirmLoading: false, |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | add (record) { |
| | | this.visible = true |
| | | }, |
| | | /** |
| | | * 提交表单 |
| | | */ |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | for (const key in values) { |
| | | if (typeof (values[key]) === 'object' && values[key] != null) { |
| | | values[key] = JSON.stringify(values[key]) |
| | | } |
| | | } |
| | | values.lastEditorDate = this.lastEditorDateDateString || null |
| | | values.publishDate = this.publishDateDateString || null |
| | | values.updateDate = this.updateDateDateString || null |
| | | values.createDate = this.createDateDateString || null |
| | | blogArticleAdd(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('新增成功') |
| | | this.confirmLoading = false |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('新增失败')// + res.message |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | lastEditorDateOnChange(date, dateString) { |
| | | this.lastEditorDateDateString = dateString |
| | | }, |
| | | publishDateOnChange(date, dateString) { |
| | | this.publishDateDateString = dateString |
| | | }, |
| | | updateDateOnChange(date, dateString) { |
| | | this.updateDateDateString = dateString |
| | | }, |
| | | createDateOnChange(date, dateString) { |
| | | this.createDateDateString = dateString |
| | | }, |
| | | handleCancel () { |
| | | this.lastEditorDateDateString ='' |
| | | this.form.getFieldDecorator('lastEditorDate', { initialValue: null }) |
| | | this.publishDateDateString ='' |
| | | this.form.getFieldDecorator('publishDate', { initialValue: null }) |
| | | this.updateDateDateString ='' |
| | | this.form.getFieldDecorator('updateDate', { initialValue: null }) |
| | | this.createDateDateString ='' |
| | | this.form.getFieldDecorator('createDate', { initialValue: null }) |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="编辑blog文章主体" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item v-show="false"><a-input v-decorator="['id']" /></a-form-item> |
| | | <a-form-item |
| | | label="文章标题" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章标题" v-decorator="['title', {rules: [{required: true, message: '请输入文章标题!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章文件id" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章文件id" v-decorator="['articleFileId', {rules: [{required: true, message: '请输入文章文件id!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文件类型 1:markdown 2:html" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文件类型 1:markdown 2:html" v-decorator="['articleFileType', {rules: [{required: true, message: '请输入文件类型 1:markdown 2:html!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章分类id 0:没有分类" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章分类id 0:没有分类" v-decorator="['articleTypeId', {rules: [{required: true, message: '请输入文章分类id 0:没有分类!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="文章引言" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入文章引言" v-decorator="['introduce', {rules: [{required: true, message: '请输入文章引言!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="封面文件地址(id)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入封面文件地址(id)" v-decorator="['coverFileId', {rules: [{required: true, message: '请输入封面文件地址(id)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="上次编辑时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择上次编辑时间" v-decorator="['lastEditorDate',{rules: [{ required: true, message: '请选择上次编辑时间!' }]}]" @change="lastEditorDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="发布时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择发布时间" v-decorator="['publishDate',{rules: [{ required: true, message: '请选择发布时间!' }]}]" @change="publishDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="是否置顶 0:否 1:是" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入是否置顶 0:否 1:是" v-decorator="['isTop', {rules: [{required: true, message: '请输入是否置顶 0:否 1:是!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="置顶值(越小越靠前)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入置顶值(越小越靠前)" v-decorator="['topValue', {rules: [{required: true, message: '请输入置顶值(越小越靠前)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="公开状态 1:公开 2:私密 3:密码授权" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入公开状态 1:公开 2:私密 3:密码授权" v-decorator="['authStatus', {rules: [{required: true, message: '请输入公开状态 1:公开 2:私密 3:密码授权!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="授权密码(在密码授权状态时)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入授权密码(在密码授权状态时)" v-decorator="['authPassword', {rules: [{required: true, message: '请输入授权密码(在密码授权状态时)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="编辑状态 0:草稿 1:发布" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入编辑状态 0:草稿 1:发布" v-decorator="['editorStatus', {rules: [{required: true, message: '请输入编辑状态 0:草稿 1:发布!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档年份(以初次发布时间为准)" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档年份(以初次发布时间为准)" v-decorator="['separateYear', {rules: [{required: true, message: '请输入归档年份(以初次发布时间为准)!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档月份" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档月份" v-decorator="['separateMonth', {rules: [{required: true, message: '请输入归档月份!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="归档日" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入归档日" v-decorator="['separateDay', {rules: [{required: true, message: '请输入归档日!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="是否启用 0:否 1:是" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入是否启用 0:否 1:是" v-decorator="['isEnable', {rules: [{required: true, message: '请输入是否启用 0:否 1:是!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="更新时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择更新时间" v-decorator="['updateDate',{rules: [{ required: true, message: '请选择更新时间!' }]}]" @change="updateDateOnChange"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="创建时间" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-date-picker style="width: 100%" placeholder="请选择创建时间" v-decorator="['createDate',{rules: [{ required: true, message: '请选择创建时间!' }]}]" @change="createDateOnChange"/> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | import moment from 'moment' |
| | | import { blogArticleEdit } from '@/api/modular/main/blogarticle/blogArticleManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | lastEditorDateDateString: '', |
| | | publishDateDateString: '', |
| | | updateDateDateString: '', |
| | | createDateDateString: '', |
| | | visible: false, |
| | | confirmLoading: false, |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | moment, |
| | | // 初始化方法 |
| | | edit (record) { |
| | | this.visible = true |
| | | setTimeout(() => { |
| | | this.form.setFieldsValue( |
| | | { |
| | | id: record.id, |
| | | title: record.title, |
| | | articleFileId: record.articleFileId, |
| | | articleFileType: record.articleFileType, |
| | | articleTypeId: record.articleTypeId, |
| | | introduce: record.introduce, |
| | | coverFileId: record.coverFileId, |
| | | isTop: record.isTop, |
| | | topValue: record.topValue, |
| | | authStatus: record.authStatus, |
| | | authPassword: record.authPassword, |
| | | editorStatus: record.editorStatus, |
| | | separateYear: record.separateYear, |
| | | separateMonth: record.separateMonth, |
| | | separateDay: record.separateDay, |
| | | isEnable: record.isEnable |
| | | } |
| | | ) |
| | | }, 100) |
| | | // 时间单独处理 |
| | | if (record.lastEditorDate) { |
| | | this.form.getFieldDecorator('lastEditorDate', { initialValue: moment(record.lastEditorDate, 'YYYY-MM-DD') }) |
| | | this.lastEditorDateDateString = moment(record.lastEditorDate).format('YYYY-MM-DD') |
| | | } |
| | | // 时间单独处理 |
| | | if (record.publishDate) { |
| | | this.form.getFieldDecorator('publishDate', { initialValue: moment(record.publishDate, 'YYYY-MM-DD') }) |
| | | this.publishDateDateString = moment(record.publishDate).format('YYYY-MM-DD') |
| | | } |
| | | // 时间单独处理 |
| | | if (record.updateDate) { |
| | | this.form.getFieldDecorator('updateDate', { initialValue: moment(record.updateDate, 'YYYY-MM-DD') }) |
| | | this.updateDateDateString = moment(record.updateDate).format('YYYY-MM-DD') |
| | | } |
| | | // 时间单独处理 |
| | | if (record.createDate) { |
| | | this.form.getFieldDecorator('createDate', { initialValue: moment(record.createDate, 'YYYY-MM-DD') }) |
| | | this.createDateDateString = moment(record.createDate).format('YYYY-MM-DD') |
| | | } |
| | | }, |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | for (const key in values) { |
| | | if (typeof (values[key]) === 'object' && values[key] != null) { |
| | | values[key] = JSON.stringify(values[key]) |
| | | } |
| | | } |
| | | values.lastEditorDate = this.lastEditorDateDateString || null |
| | | values.publishDate = this.publishDateDateString || null |
| | | values.updateDate = this.updateDateDateString || null |
| | | values.createDate = this.createDateDateString || null |
| | | blogArticleEdit(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('编辑成功') |
| | | this.confirmLoading = false |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('编辑失败')// + res.message |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | lastEditorDateOnChange(date, dateString) { |
| | | this.lastEditorDateDateString = dateString |
| | | }, |
| | | publishDateOnChange(date, dateString) { |
| | | this.publishDateDateString = dateString |
| | | }, |
| | | updateDateOnChange(date, dateString) { |
| | | this.updateDateDateString = dateString |
| | | }, |
| | | createDateOnChange(date, dateString) { |
| | | this.createDateDateString = dateString |
| | | }, |
| | | handleCancel () { |
| | | this.lastEditorDateDateString ='' |
| | | this.form.getFieldDecorator('lastEditorDate', { initialValue: null }) |
| | | this.publishDateDateString ='' |
| | | this.form.getFieldDecorator('publishDate', { initialValue: null }) |
| | | this.updateDateDateString ='' |
| | | this.form.getFieldDecorator('updateDate', { initialValue: null }) |
| | | this.createDateDateString ='' |
| | | this.form.getFieldDecorator('createDate', { initialValue: null }) |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <a-card :bordered="false" :bodyStyle="tstyle"> |
| | | <div class="table-page-search-wrapper" v-if="hasPerm('blogArticle:page')"> |
| | | <a-form layout="inline"> |
| | | <a-row :gutter="48"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="文章标题"> |
| | | <a-input v-model="queryParam.title" allow-clear placeholder="请输入文章标题"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="文章文件id"> |
| | | <a-input v-model="queryParam.articleFileId" allow-clear placeholder="请输入文章文件id"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <template v-if="advanced"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="文件类型 1:markdown 2:html"> |
| | | <a-input v-model="queryParam.articleFileType" allow-clear placeholder="请输入文件类型 1:markdown 2:html"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="文章分类id 0:没有分类"> |
| | | <a-input v-model="queryParam.articleTypeId" allow-clear placeholder="请输入文章分类id 0:没有分类"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="文章引言"> |
| | | <a-input v-model="queryParam.introduce" allow-clear placeholder="请输入文章引言"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="封面文件地址(id)"> |
| | | <a-input v-model="queryParam.coverFileId" allow-clear placeholder="请输入封面文件地址(id)"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="上次编辑时间"> |
| | | <a-date-picker style="width: 100%" placeholder="请选择上次编辑时间" v-model="queryParam.lastEditorDateDate" @change="onChangelastEditorDate"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="发布时间"> |
| | | <a-date-picker style="width: 100%" placeholder="请选择发布时间" v-model="queryParam.publishDateDate" @change="onChangepublishDate"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="是否置顶 0:否 1:是"> |
| | | <a-input v-model="queryParam.isTop" allow-clear placeholder="请输入是否置顶 0:否 1:是"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="置顶值(越小越靠前)"> |
| | | <a-input v-model="queryParam.topValue" allow-clear placeholder="请输入置顶值(越小越靠前)"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="公开状态 1:公开 2:私密 3:密码授权"> |
| | | <a-input v-model="queryParam.authStatus" allow-clear placeholder="请输入公开状态 1:公开 2:私密 3:密码授权"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="授权密码(在密码授权状态时)"> |
| | | <a-input v-model="queryParam.authPassword" allow-clear placeholder="请输入授权密码(在密码授权状态时)"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="编辑状态 0:草稿 1:发布"> |
| | | <a-input v-model="queryParam.editorStatus" allow-clear placeholder="请输入编辑状态 0:草稿 1:发布"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="归档年份(以初次发布时间为准)"> |
| | | <a-input v-model="queryParam.separateYear" allow-clear placeholder="请输入归档年份(以初次发布时间为准)"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="归档月份"> |
| | | <a-input v-model="queryParam.separateMonth" allow-clear placeholder="请输入归档月份"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="归档日"> |
| | | <a-input v-model="queryParam.separateDay" allow-clear placeholder="请输入归档日"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="是否启用 0:否 1:是"> |
| | | <a-input v-model="queryParam.isEnable" allow-clear placeholder="请输入是否启用 0:否 1:是"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="更新时间"> |
| | | <a-date-picker style="width: 100%" placeholder="请选择更新时间" v-model="queryParam.updateDateDate" @change="onChangeupdateDate"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="创建时间"> |
| | | <a-date-picker style="width: 100%" placeholder="请选择创建时间" v-model="queryParam.createDateDate" @change="onChangecreateDate"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | </template> |
| | | <a-col :md="8" :sm="24" > |
| | | <span class="table-page-search-submitButtons"> |
| | | <a-button type="primary" @click="$refs.table.refresh(true)" >查询</a-button> |
| | | <a-button style="margin-left: 8px" @click="() => queryParam = {}">重置</a-button> |
| | | <a @click="toggleAdvanced" style="margin-left: 8px"> |
| | | {{ advanced ? '收起' : '展开' }} |
| | | <a-icon :type="advanced ? 'up' : 'down'"/> |
| | | </a> |
| | | </span> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </div> |
| | | </a-card> |
| | | <a-card :bordered="false"> |
| | | <s-table |
| | | ref="table" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | :alert="options.alert" |
| | | :rowKey="(record) => record.id" |
| | | :rowSelection="options.rowSelection" |
| | | > |
| | | <template class="table-operator" slot="operator" v-if="hasPerm('blogArticle:add')" > |
| | | <a-button type="primary" v-if="hasPerm('blogArticle:add')" icon="plus" @click="$refs.addForm.add()">新增blog文章主体</a-button> |
| | | <a-button type="danger" :disabled="selectedRowKeys.length < 1" v-if="hasPerm('blogArticle:delete')" @click="batchDelete"><a-icon type="delete"/>批量删除</a-button> |
| | | <x-down |
| | | v-if="hasPerm('blogArticle:export')" |
| | | ref="batchExport" |
| | | @batchExport="batchExport" |
| | | /> |
| | | </template> |
| | | <span slot="action" slot-scope="text, record"> |
| | | <a v-if="hasPerm('blogArticle:edit')" @click="$refs.editForm.edit(record)">编辑</a> |
| | | <a-divider type="vertical" v-if="hasPerm('blogArticle:edit') & hasPerm('blogArticle:delete')"/> |
| | | <a-popconfirm v-if="hasPerm('blogArticle:delete')" placement="topRight" title="确认删除?" @confirm="() => singleDelete(record)"> |
| | | <a>删除</a> |
| | | </a-popconfirm> |
| | | </span> |
| | | </s-table> |
| | | <add-form ref="addForm" @ok="handleOk" /> |
| | | <edit-form ref="editForm" @ok="handleOk" /> |
| | | </a-card> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { STable, XDown } from '@/components' |
| | | import moment from 'moment' |
| | | import { blogArticlePage, blogArticleDelete, blogArticleExport } from '@/api/modular/main/blogarticle/blogArticleManage' |
| | | import addForm from './addForm.vue' |
| | | import editForm from './editForm.vue' |
| | | export default { |
| | | components: { |
| | | STable, |
| | | addForm, |
| | | editForm, |
| | | XDown |
| | | }, |
| | | data () { |
| | | return { |
| | | // 高级搜索 展开/关闭 |
| | | advanced: false, |
| | | // 查询参数 |
| | | queryParam: {}, |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | | title: '文章标题', |
| | | align: 'center', |
| | | dataIndex: 'title' |
| | | }, |
| | | { |
| | | title: '文章文件id', |
| | | align: 'center', |
| | | dataIndex: 'articleFileId' |
| | | }, |
| | | { |
| | | title: '文件类型 1:markdown 2:html', |
| | | align: 'center', |
| | | dataIndex: 'articleFileType' |
| | | }, |
| | | { |
| | | title: '文章分类id 0:没有分类', |
| | | align: 'center', |
| | | dataIndex: 'articleTypeId' |
| | | }, |
| | | { |
| | | title: '文章引言', |
| | | align: 'center', |
| | | dataIndex: 'introduce' |
| | | }, |
| | | { |
| | | title: '封面文件地址(id)', |
| | | align: 'center', |
| | | dataIndex: 'coverFileId' |
| | | }, |
| | | { |
| | | title: '上次编辑时间', |
| | | align: 'center', |
| | | dataIndex: 'lastEditorDate' |
| | | }, |
| | | { |
| | | title: '发布时间', |
| | | align: 'center', |
| | | dataIndex: 'publishDate' |
| | | }, |
| | | { |
| | | title: '是否置顶 0:否 1:是', |
| | | align: 'center', |
| | | dataIndex: 'isTop' |
| | | }, |
| | | { |
| | | title: '置顶值(越小越靠前)', |
| | | align: 'center', |
| | | dataIndex: 'topValue' |
| | | }, |
| | | { |
| | | title: '公开状态 1:公开 2:私密 3:密码授权', |
| | | align: 'center', |
| | | dataIndex: 'authStatus' |
| | | }, |
| | | { |
| | | title: '授权密码(在密码授权状态时)', |
| | | align: 'center', |
| | | dataIndex: 'authPassword' |
| | | }, |
| | | { |
| | | title: '编辑状态 0:草稿 1:发布', |
| | | align: 'center', |
| | | dataIndex: 'editorStatus' |
| | | }, |
| | | { |
| | | title: '归档年份(以初次发布时间为准)', |
| | | align: 'center', |
| | | dataIndex: 'separateYear' |
| | | }, |
| | | { |
| | | title: '归档月份', |
| | | align: 'center', |
| | | dataIndex: 'separateMonth' |
| | | }, |
| | | { |
| | | title: '归档日', |
| | | align: 'center', |
| | | dataIndex: 'separateDay' |
| | | }, |
| | | { |
| | | title: '是否启用 0:否 1:是', |
| | | align: 'center', |
| | | dataIndex: 'isEnable' |
| | | }, |
| | | { |
| | | title: '更新时间', |
| | | align: 'center', |
| | | dataIndex: 'updateDate' |
| | | }, |
| | | { |
| | | title: '创建时间', |
| | | align: 'center', |
| | | dataIndex: 'createDate' |
| | | } |
| | | ], |
| | | tstyle: { 'padding-bottom': '0px', 'margin-bottom': '10px' }, |
| | | // 加载数据方法 必须为 Promise 对象 |
| | | loadData: parameter => { |
| | | return blogArticlePage(Object.assign(parameter, this.switchingDate())).then((res) => { |
| | | return res.data |
| | | }) |
| | | }, |
| | | selectedRowKeys: [], |
| | | selectedRows: [], |
| | | options: { |
| | | alert: { show: true, clear: () => { this.selectedRowKeys = [] } }, |
| | | rowSelection: { |
| | | selectedRowKeys: this.selectedRowKeys, |
| | | onChange: this.onSelectChange |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | created () { |
| | | if (this.hasPerm('blogArticle:edit') || this.hasPerm('blogArticle:delete')) { |
| | | this.columns.push({ |
| | | title: '操作', |
| | | width: '150px', |
| | | dataIndex: 'action', |
| | | scopedSlots: { customRender: 'action' } |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | moment, |
| | | /** |
| | | * 查询参数组装 |
| | | */ |
| | | switchingDate () { |
| | | const queryParamlastEditorDate = this.queryParam.lastEditorDateDate |
| | | if (queryParamlastEditorDate != null) { |
| | | this.queryParam.lastEditorDate = moment(queryParamlastEditorDate).format('YYYY-MM-DD') |
| | | if (queryParamlastEditorDate.length < 1) { |
| | | delete this.queryParam.lastEditorDate |
| | | } |
| | | } |
| | | const queryParampublishDate = this.queryParam.publishDateDate |
| | | if (queryParampublishDate != null) { |
| | | this.queryParam.publishDate = moment(queryParampublishDate).format('YYYY-MM-DD') |
| | | if (queryParampublishDate.length < 1) { |
| | | delete this.queryParam.publishDate |
| | | } |
| | | } |
| | | const queryParamupdateDate = this.queryParam.updateDateDate |
| | | if (queryParamupdateDate != null) { |
| | | this.queryParam.updateDate = moment(queryParamupdateDate).format('YYYY-MM-DD') |
| | | if (queryParamupdateDate.length < 1) { |
| | | delete this.queryParam.updateDate |
| | | } |
| | | } |
| | | const queryParamcreateDate = this.queryParam.createDateDate |
| | | if (queryParamcreateDate != null) { |
| | | this.queryParam.createDate = moment(queryParamcreateDate).format('YYYY-MM-DD') |
| | | if (queryParamcreateDate.length < 1) { |
| | | delete this.queryParam.createDate |
| | | } |
| | | } |
| | | const obj = JSON.parse(JSON.stringify(this.queryParam)) |
| | | return obj |
| | | }, |
| | | /** |
| | | * 单个删除 |
| | | */ |
| | | singleDelete (record) { |
| | | const param = [{ 'id': record.id }] |
| | | this.blogArticleDelete(param) |
| | | }, |
| | | /** |
| | | * 批量删除 |
| | | */ |
| | | batchDelete () { |
| | | const paramIds = this.selectedRowKeys.map((d) => { |
| | | return { 'id': d } |
| | | }) |
| | | this.blogArticleDelete(paramIds) |
| | | }, |
| | | blogArticleDelete (record) { |
| | | blogArticleDelete(record).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.$refs.table.clearRefreshSelected() |
| | | } else { |
| | | this.$message.error('删除失败') // + res.message |
| | | } |
| | | }) |
| | | }, |
| | | toggleAdvanced () { |
| | | this.advanced = !this.advanced |
| | | }, |
| | | onChangelastEditorDate(date, dateString) { |
| | | this.lastEditorDateDateString = dateString |
| | | }, |
| | | onChangepublishDate(date, dateString) { |
| | | this.publishDateDateString = dateString |
| | | }, |
| | | onChangeupdateDate(date, dateString) { |
| | | this.updateDateDateString = dateString |
| | | }, |
| | | onChangecreateDate(date, dateString) { |
| | | this.createDateDateString = dateString |
| | | }, |
| | | /** |
| | | * 批量导出 |
| | | */ |
| | | batchExport () { |
| | | const paramIds = this.selectedRowKeys.map((d) => { |
| | | return { 'id': d } |
| | | }) |
| | | blogArticleExport(paramIds).then((res) => { |
| | | this.$refs.batchExport.downloadfile(res) |
| | | }) |
| | | }, |
| | | handleOk () { |
| | | this.$refs.table.refresh() |
| | | }, |
| | | onSelectChange (selectedRowKeys, selectedRows) { |
| | | this.selectedRowKeys = selectedRowKeys |
| | | this.selectedRows = selectedRows |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | .table-operator { |
| | | margin-bottom: 18px; |
| | | } |
| | | button { |
| | | margin-right: 8px; |
| | | } |
| | | </style> |
New file |
| | |
| | | /** 此文件夹下代码尽量不要动,底座升级直接覆盖替换 **/ |
New file |
| | |
| | | <template> |
| | | <div class="page-header-index-wide page-header-wrapper-grid-content-main"> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="24" :lg="7"> |
| | | <a-card :bordered="false"> |
| | | <div class="account-center-avatarHolder"> |
| | | <div class="avatar"> |
| | | <img :src="avatar()"> |
| | | </div> |
| | | <div class="username">{{ nickname() }}</div> |
| | | <div class="bio">海纳百川,有容乃大</div> |
| | | </div> |
| | | <div class="account-center-detail"> |
| | | <p> |
| | | <i class="title"></i>交互专家 |
| | | </p> |
| | | <p> |
| | | <i class="group"></i>蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED |
| | | </p> |
| | | <p> |
| | | <i class="address"></i> |
| | | <span>浙江省</span> |
| | | <span>杭州市</span> |
| | | </p> |
| | | </div> |
| | | <a-divider/> |
| | | |
| | | <div class="account-center-tags"> |
| | | <div class="tagsTitle">标签</div> |
| | | <div> |
| | | <template v-for="(tag, index) in tags"> |
| | | <a-tooltip v-if="tag.length > 20" :key="tag" :title="tag"> |
| | | <a-tag |
| | | :key="tag" |
| | | :closable="index !== 0" |
| | | >{{ `${tag.slice(0, 20)}...` }}</a-tag> |
| | | </a-tooltip> |
| | | <a-tag |
| | | v-else |
| | | :key="tag" |
| | | :closable="index !== 0" |
| | | >{{ tag }}</a-tag> |
| | | </template> |
| | | <a-input |
| | | v-if="tagInputVisible" |
| | | ref="tagInput" |
| | | type="text" |
| | | size="small" |
| | | :style="{ width: '78px' }" |
| | | :value="tagInputValue" |
| | | @change="handleInputChange" |
| | | @blur="handleTagInputConfirm" |
| | | @keyup.enter="handleTagInputConfirm" |
| | | /> |
| | | <a-tag v-else @click="showTagInput" style="background: #fff; borderStyle: dashed;"> |
| | | <a-icon type="plus"/>New Tag |
| | | </a-tag> |
| | | </div> |
| | | </div> |
| | | <a-divider :dashed="true"/> |
| | | |
| | | <div class="account-center-team"> |
| | | <div class="teamTitle">团队</div> |
| | | <a-spin :spinning="teamSpinning"> |
| | | <div class="members"> |
| | | <a-row> |
| | | <a-col :span="12" v-for="(item, index) in teams" :key="index"> |
| | | <a> |
| | | <a-avatar size="small" :src="item.avatar"/> |
| | | <span class="member">{{ item.name }}</span> |
| | | </a> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | </a-spin> |
| | | </div> |
| | | </a-card> |
| | | </a-col> |
| | | <a-col :md="24" :lg="17"> |
| | | <a-card |
| | | style="width:100%" |
| | | :bordered="false" |
| | | :tabList="tabListNoTitle" |
| | | :activeTabKey="noTitleKey" |
| | | @tabChange="key => handleTabChange(key, 'noTitleKey')" |
| | | > |
| | | <article-page v-if="noTitleKey === 'article'"></article-page> |
| | | <app-page v-else-if="noTitleKey === 'app'"></app-page> |
| | | <project-page v-else-if="noTitleKey === 'project'"></project-page> |
| | | </a-card> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { PageView, RouteView } from '@/layouts' |
| | | import { AppPage, ArticlePage, ProjectPage } from './page' |
| | | import { mapGetters } from 'vuex' |
| | | |
| | | export default { |
| | | components: { |
| | | RouteView, |
| | | PageView, |
| | | AppPage, |
| | | ArticlePage, |
| | | ProjectPage |
| | | }, |
| | | data () { |
| | | return { |
| | | tags: ['很有想法的', '专注设计', '辣~', '大长腿', '川妹子', '海纳百川'], |
| | | |
| | | tagInputVisible: false, |
| | | tagInputValue: '', |
| | | |
| | | teams: [], |
| | | teamSpinning: true, |
| | | |
| | | tabListNoTitle: [ |
| | | { |
| | | key: 'article', |
| | | tab: '文章(8)' |
| | | }, |
| | | { |
| | | key: 'app', |
| | | tab: '应用(8)' |
| | | }, |
| | | { |
| | | key: 'project', |
| | | tab: '项目(8)' |
| | | } |
| | | ], |
| | | noTitleKey: 'app' |
| | | } |
| | | }, |
| | | mounted () { |
| | | this.getTeams() |
| | | }, |
| | | methods: { |
| | | ...mapGetters(['nickname', 'avatar']), |
| | | |
| | | getTeams () { |
| | | this.teams = [{ |
| | | id: 1, |
| | | name: '科学搬砖组', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png' |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: '程序员日常', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '设计天团', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '中二少女团', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '骗你学计算机', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png' |
| | | } |
| | | ] |
| | | this.teamSpinning = false |
| | | }, |
| | | |
| | | handleTabChange (key, type) { |
| | | this[type] = key |
| | | }, |
| | | |
| | | handleTagClose (removeTag) { |
| | | const tags = this.tags.filter(tag => tag !== removeTag) |
| | | this.tags = tags |
| | | }, |
| | | |
| | | showTagInput () { |
| | | this.tagInputVisible = true |
| | | this.$nextTick(() => { |
| | | this.$refs.tagInput.focus() |
| | | }) |
| | | }, |
| | | |
| | | handleInputChange (e) { |
| | | this.tagInputValue = e.target.value |
| | | }, |
| | | |
| | | handleTagInputConfirm () { |
| | | const inputValue = this.tagInputValue |
| | | let tags = this.tags |
| | | if (inputValue && !tags.includes(inputValue)) { |
| | | tags = [...tags, inputValue] |
| | | } |
| | | |
| | | Object.assign(this, { |
| | | tags, |
| | | tagInputVisible: false, |
| | | tagInputValue: '' |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .page-header-wrapper-grid-content-main { |
| | | width: 100%; |
| | | height: 100%; |
| | | min-height: 100%; |
| | | transition: 0.3s; |
| | | |
| | | .account-center-avatarHolder { |
| | | text-align: center; |
| | | margin-bottom: 24px; |
| | | |
| | | & > .avatar { |
| | | margin: 0 auto; |
| | | width: 104px; |
| | | height: 104px; |
| | | margin-bottom: 20px; |
| | | border-radius: 50%; |
| | | overflow: hidden; |
| | | img { |
| | | height: 100%; |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .username { |
| | | color: rgba(0, 0, 0, 0.85); |
| | | font-size: 20px; |
| | | line-height: 28px; |
| | | font-weight: 500; |
| | | margin-bottom: 4px; |
| | | } |
| | | } |
| | | |
| | | .account-center-detail { |
| | | p { |
| | | margin-bottom: 8px; |
| | | padding-left: 26px; |
| | | position: relative; |
| | | } |
| | | |
| | | i { |
| | | position: absolute; |
| | | height: 14px; |
| | | width: 14px; |
| | | left: 0; |
| | | top: 4px; |
| | | background: url(https://gw.alipayobjects.com/zos/rmsportal/pBjWzVAHnOOtAUvZmZfy.svg); |
| | | } |
| | | |
| | | .title { |
| | | background-position: 0 0; |
| | | } |
| | | .group { |
| | | background-position: 0 -22px; |
| | | } |
| | | .address { |
| | | background-position: 0 -44px; |
| | | } |
| | | } |
| | | |
| | | .account-center-tags { |
| | | .ant-tag { |
| | | margin-bottom: 8px; |
| | | } |
| | | } |
| | | |
| | | .account-center-team { |
| | | .members { |
| | | a { |
| | | display: block; |
| | | margin: 12px 0; |
| | | line-height: 24px; |
| | | height: 24px; |
| | | .member { |
| | | font-size: 14px; |
| | | color: rgba(0, 0, 0, 0.65); |
| | | line-height: 24px; |
| | | max-width: 100px; |
| | | vertical-align: top; |
| | | margin-left: 12px; |
| | | transition: all 0.3s; |
| | | display: inline-block; |
| | | } |
| | | &:hover { |
| | | span { |
| | | color: #1890ff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .tagsTitle, |
| | | .teamTitle { |
| | | font-weight: 500; |
| | | color: rgba(0, 0, 0, 0.85); |
| | | margin-bottom: 12px; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="app-list"> |
| | | <a-list |
| | | :grid="{ gutter: 24, lg: 3, md: 2, sm: 1, xs: 1 }" |
| | | :dataSource="dataSource"> |
| | | <a-list-item slot="renderItem" slot-scope="item"> |
| | | <a-card :hoverable="true"> |
| | | <a-card-meta> |
| | | <div style="margin-bottom: 3px" slot="title">{{ item.title }}</div> |
| | | <a-avatar class="card-avatar" slot="avatar" :src="item.avatar" size="small"/> |
| | | <div class="meta-cardInfo" slot="description"> |
| | | <div> |
| | | <p>活跃用户</p> |
| | | <p> |
| | | <span>{{ item.activeUser }}<span>万</span></span> |
| | | </p> |
| | | </div> |
| | | <div> |
| | | <p>新增用户</p> |
| | | <p>{{ item.newUser | NumberFormat }}</p> |
| | | </div> |
| | | </div> |
| | | </a-card-meta> |
| | | <template class="ant-card-actions" slot="actions"> |
| | | <a> |
| | | <a-icon type="download"/> |
| | | </a> |
| | | <a> |
| | | <a-icon type="edit"/> |
| | | </a> |
| | | <a> |
| | | <a-icon type="share-alt"/> |
| | | </a> |
| | | <a> |
| | | <a-dropdown> |
| | | <a class="ant-dropdown-link" href="javascript:;"> |
| | | <a-icon type="ellipsis"/> |
| | | </a> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item> |
| | | <a href="javascript:;">1st menu item</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">2nd menu item</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">3rd menu item</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </a> |
| | | </template> |
| | | </a-card> |
| | | </a-list-item> |
| | | </a-list> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | const dataSource = [] |
| | | for (let i = 0; i < 11; i++) { |
| | | dataSource.push({ |
| | | title: 'Alipay', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', |
| | | activeUser: 17, |
| | | newUser: 1700 |
| | | }) |
| | | } |
| | | |
| | | export default { |
| | | name: 'Article', |
| | | components: {}, |
| | | data () { |
| | | return { |
| | | dataSource |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .app-list { |
| | | |
| | | .meta-cardInfo { |
| | | zoom: 1; |
| | | margin-top: 16px; |
| | | |
| | | > div { |
| | | position: relative; |
| | | text-align: left; |
| | | float: left; |
| | | width: 50%; |
| | | |
| | | p { |
| | | line-height: 32px; |
| | | font-size: 24px; |
| | | margin: 0; |
| | | |
| | | &:first-child { |
| | | color: rgba(0, 0, 0, .45); |
| | | font-size: 12px; |
| | | line-height: 20px; |
| | | margin-bottom: 4px; |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-list |
| | | size="large" |
| | | rowKey="id" |
| | | :loading="loading" |
| | | itemLayout="vertical" |
| | | :dataSource="data" |
| | | > |
| | | <a-list-item :key="item.id" slot="renderItem" slot-scope="item"> |
| | | <a-list-item-meta> |
| | | <a slot="title" href="https://vue.ant.design/">{{ item.title }}</a> |
| | | <template slot="description"> |
| | | <span> |
| | | <a-tag>Ant Design</a-tag> |
| | | <a-tag>设计语言</a-tag> |
| | | <a-tag>蚂蚁金服</a-tag> |
| | | </span> |
| | | </template> |
| | | </a-list-item-meta> |
| | | <article-list-content :description="item.description" :owner="item.owner" :avatar="item.avatar" :href="item.href" :updateAt="item.updatedAt" /> |
| | | </a-list-item> |
| | | <div slot="footer" v-if="data.length > 0" style="text-align: center; margin-top: 16px;"> |
| | | <a-button @click="loadMore" :loading="loadingMore">加载更多</a-button> |
| | | </div> |
| | | </a-list> |
| | | </template> |
| | | |
| | | <script> |
| | | import { ArticleListContent } from '@/components' |
| | | |
| | | export default { |
| | | name: 'Article', |
| | | components: { |
| | | ArticleListContent |
| | | }, |
| | | data () { |
| | | return { |
| | | loading: true, |
| | | loadingMore: false, |
| | | data: [] |
| | | } |
| | | }, |
| | | mounted () { |
| | | this.getList() |
| | | }, |
| | | methods: { |
| | | getList () { |
| | | this.data = [ |
| | | { |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | title: '小诺', |
| | | owner: '俞宝山', |
| | | description: 'snowy是小诺团队产品', |
| | | href: 'https://xiaonuo.vip', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', |
| | | name: '曲丽丽', |
| | | id: 'member1' |
| | | }, |
| | | { |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | title: '小诺', |
| | | owner: '徐玉祥', |
| | | description: 'snowy是小诺团队产品', |
| | | href: 'https://xiaonuo.vip', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', |
| | | name: '王昭君', |
| | | id: 'member2' |
| | | }, |
| | | { |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | title: '小诺', |
| | | owner: '董夏雨', |
| | | description: 'snowy是小诺团队产品', |
| | | href: 'https://xiaonuo.vip', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', |
| | | name: '董娜娜', |
| | | id: 'member3' |
| | | } |
| | | ] |
| | | this.loading = false |
| | | }, |
| | | loadMore () { |
| | | this.loadingMore = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="ant-pro-pages-account-projects-cardList"> |
| | | <a-list :loading="loading" :data-source="data" :grid="{ gutter: 24, xxl: 3, xl: 2, lg: 2, md: 2, sm: 2, xs: 1 }"> |
| | | <a-list-item slot="renderItem" slot-scope="item"> |
| | | <a-card class="ant-pro-pages-account-projects-card" hoverable> |
| | | <img slot="cover" :src="item.cover" :alt="item.title" /> |
| | | <a-card-meta :title="item.title"> |
| | | <template slot="description"> |
| | | <ellipsis :length="50">{{ item.description }}</ellipsis> |
| | | </template> |
| | | </a-card-meta> |
| | | <div class="cardItemContent"> |
| | | <span>{{ item.updatedAt | fromNow }}</span> |
| | | <div class="avatarList"> |
| | | <avatar-list size="mini"> |
| | | <avatar-list-item |
| | | v-for="(member, i) in item.members" |
| | | :key="`${item.id}-avatar-${i}`" |
| | | :src="member.avatar" |
| | | :tips="member.name" |
| | | /> |
| | | </avatar-list> |
| | | </div> |
| | | </div> |
| | | </a-card> |
| | | </a-list-item> |
| | | </a-list> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import moment from 'moment' |
| | | import { TagSelect, StandardFormRow, Ellipsis, AvatarList } from '@/components' |
| | | const TagSelectOption = TagSelect.Option |
| | | const AvatarListItem = AvatarList.AvatarItem |
| | | |
| | | export default { |
| | | name: 'Project', |
| | | components: { |
| | | AvatarList, |
| | | AvatarListItem, |
| | | Ellipsis, |
| | | TagSelect, |
| | | TagSelectOption, |
| | | StandardFormRow |
| | | }, |
| | | data () { |
| | | return { |
| | | data: [], |
| | | form: this.$form.createForm(this), |
| | | loading: true |
| | | } |
| | | }, |
| | | filters: { |
| | | fromNow (date) { |
| | | return moment(date).fromNow() |
| | | } |
| | | }, |
| | | mounted () { |
| | | this.getList() |
| | | }, |
| | | methods: { |
| | | handleChange (value) { |
| | | console.log(`selected ${value}`) |
| | | }, |
| | | getList () { |
| | | this.data = [ |
| | | { |
| | | id: '123', |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/uMfMFlvUuceEyPpotzlq.png', |
| | | content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', |
| | | message: '消息', |
| | | description: '小诺框架产品', |
| | | href: 'https://xiaonuo.vip', |
| | | title: '小诺', |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | members: [ |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', |
| | | name: '曲丽丽', |
| | | id: 'member1' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', |
| | | name: '王昭君', |
| | | id: 'member2' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', |
| | | name: '董娜娜', |
| | | id: 'member3' |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | id: '1234', |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/iZBVOIhGJiAnhplqjvZW.png', |
| | | content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', |
| | | message: '消息', |
| | | description: '小诺框架产品', |
| | | href: 'https://xiaonuo.vip', |
| | | title: '小诺', |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | members: [ |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', |
| | | name: '曲丽丽', |
| | | id: 'member1' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', |
| | | name: '王昭君', |
| | | id: 'member2' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', |
| | | name: '董娜娜', |
| | | id: 'member3' |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | id: '12345', |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/iXjVmWVHbCJAyqvDxdtx.png', |
| | | content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', |
| | | message: '消息', |
| | | description: '小诺框架产品', |
| | | href: 'https://xiaonuo.vip', |
| | | title: '小诺', |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | members: [ |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', |
| | | name: '曲丽丽', |
| | | id: 'member1' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', |
| | | name: '王昭君', |
| | | id: 'member2' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', |
| | | name: '董娜娜', |
| | | id: 'member3' |
| | | } |
| | | ] |
| | | }, |
| | | { |
| | | id: '1236', |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/gLaIAoVWTtLbBWZNYEMg.png', |
| | | content: '段落示意:蚂蚁金服设计平台 ant.design,用最小的工作量,无缝接入蚂蚁金服生态,提供跨越设计与开发的体验解决方案。', |
| | | message: '消息', |
| | | description: '小诺框架产品', |
| | | href: 'https://xiaonuo.vip', |
| | | title: '小诺', |
| | | updatedAt: '2021-05-01 12:00:00', |
| | | members: [ |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ZiESqWwCXBRQoaPONSJe.png', |
| | | name: '曲丽丽', |
| | | id: 'member1' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/tBOxZPlITHqwlGjsJWaF.png', |
| | | name: '王昭君', |
| | | id: 'member2' |
| | | }, |
| | | { |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/sBxjgqiuHMGRkIjqlQCd.png', |
| | | name: '董娜娜', |
| | | id: 'member3' |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | this.loading = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .ant-pro-pages-account-projects-cardList { |
| | | margin-top: 24px; |
| | | |
| | | /deep/ .ant-card-meta-title { |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | /deep/ .ant-card-meta-description { |
| | | height: 44px; |
| | | overflow: hidden; |
| | | line-height: 22px; |
| | | } |
| | | |
| | | .cardItemContent { |
| | | display: flex; |
| | | height: 20px; |
| | | margin-top: 16px; |
| | | margin-bottom: -4px; |
| | | line-height: 20px; |
| | | |
| | | > span { |
| | | flex: 1 1; |
| | | color: rgba(0,0,0,.45); |
| | | font-size: 12px; |
| | | } |
| | | |
| | | /deep/ .ant-pro-avatar-list { |
| | | flex: 0 1 auto; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | import AppPage from './App' |
| | | import ArticlePage from './Article' |
| | | import ProjectPage from './Project' |
| | | |
| | | export { AppPage, ArticlePage, ProjectPage } |
New file |
| | |
| | | <template> |
| | | |
| | | <a-modal |
| | | title="修改头像" |
| | | :visible="visible" |
| | | :maskClosable="false" |
| | | :confirmLoading="confirmLoading" |
| | | :width="800" |
| | | :footer="null" |
| | | @cancel="cancelHandel"> |
| | | <a-row> |
| | | <a-col :xs="24" :md="12" :style="{height: '350px'}"> |
| | | <vue-cropper |
| | | ref="cropper" |
| | | :img="options.img" |
| | | :info="true" |
| | | :autoCrop="options.autoCrop" |
| | | :autoCropWidth="options.autoCropWidth" |
| | | :autoCropHeight="options.autoCropHeight" |
| | | :fixedBox="options.fixedBox" |
| | | @realTime="realTime" |
| | | > |
| | | </vue-cropper> |
| | | </a-col> |
| | | <a-col :xs="24" :md="12" :style="{height: '350px'}"> |
| | | <div class="avatar-upload-preview"> |
| | | <img :src="previews.url" :style="previews.img"/> |
| | | </div> |
| | | </a-col> |
| | | </a-row> |
| | | <br> |
| | | <a-row> |
| | | <a-col :lg="2" :md="2"> |
| | | <a-upload name="file" :beforeUpload="beforeUpload" :showUploadList="false"> |
| | | <a-button icon="upload">选择图片</a-button> |
| | | </a-upload> |
| | | </a-col> |
| | | <a-col :lg="{span: 1, offset: 2}" :md="2"> |
| | | <a-button icon="plus" @click="changeScale(1)"/> |
| | | </a-col> |
| | | <a-col :lg="{span: 1, offset: 1}" :md="2"> |
| | | <a-button icon="minus" @click="changeScale(-1)"/> |
| | | </a-col> |
| | | <a-col :lg="{span: 1, offset: 1}" :md="2"> |
| | | <a-button icon="undo" @click="rotateLeft"/> |
| | | </a-col> |
| | | <a-col :lg="{span: 1, offset: 1}" :md="2"> |
| | | <a-button icon="redo" @click="rotateRight"/> |
| | | </a-col> |
| | | <a-col :lg="{span: 2, offset: 6}" :md="2"> |
| | | <a-button type="primary" @click="finish('blob')" :loading="uploading">保存</a-button> |
| | | </a-col> |
| | | </a-row> |
| | | </a-modal> |
| | | |
| | | </template> |
| | | <script> |
| | | import { sysFileInfoUpload } from '@/api/modular/system/fileManage' |
| | | import { sysUserUpdateAvatar } from '@/api/modular/system/userManage' |
| | | |
| | | export default { |
| | | data () { |
| | | return { |
| | | visible: false, |
| | | id: null, |
| | | confirmLoading: false, |
| | | fileList: [], |
| | | uploading: false, |
| | | options: { |
| | | img: '', |
| | | autoCrop: true, |
| | | autoCropWidth: 200, |
| | | autoCropHeight: 200, |
| | | fixedBox: true |
| | | }, |
| | | previews: {} |
| | | } |
| | | }, |
| | | methods: { |
| | | edit (id) { |
| | | this.visible = true |
| | | this.id = id |
| | | /* 获取原始头像 */ |
| | | }, |
| | | close () { |
| | | this.id = null |
| | | this.visible = false |
| | | }, |
| | | cancelHandel () { |
| | | this.close() |
| | | }, |
| | | changeScale (num) { |
| | | num = num || 1 |
| | | this.$refs.cropper.changeScale(num) |
| | | }, |
| | | rotateLeft () { |
| | | this.$refs.cropper.rotateLeft() |
| | | }, |
| | | rotateRight () { |
| | | this.$refs.cropper.rotateRight() |
| | | }, |
| | | beforeUpload (file) { |
| | | this.fileList = file |
| | | const reader = new FileReader() |
| | | // 把Array Buffer转化为blob 如果是base64不需要 |
| | | // 转化为base64 |
| | | reader.readAsDataURL(file) |
| | | reader.onload = () => { |
| | | this.options.img = reader.result |
| | | } |
| | | // 转化为blob |
| | | // reader.readAsArrayBuffer(file) |
| | | return false |
| | | }, |
| | | |
| | | // 上传图片(点击上传按钮) |
| | | finish (type) { |
| | | if (type === 'blob') { |
| | | this.uploading = true |
| | | this.$refs.cropper.getCropBlob((data) => { |
| | | const files = new window.File( |
| | | [data], |
| | | this.fileList.name, |
| | | { type: this.fileList.type } |
| | | ) |
| | | const formData = new FormData() |
| | | formData.append('file', files) |
| | | sysFileInfoUpload(formData).then((res) => { |
| | | if (res.success) { |
| | | this.updateAvatar(res.data) |
| | | this.$emit('ok', res.data) |
| | | } else { |
| | | this.uploading = false |
| | | this.$message.error(res.message) |
| | | } |
| | | }) |
| | | }) |
| | | } else { |
| | | this.$refs.cropper.getCropData((data) => { |
| | | console.log(data) |
| | | }) |
| | | } |
| | | }, |
| | | updateAvatar (data) { |
| | | const params = { |
| | | id: this.id, |
| | | avatar: data |
| | | } |
| | | sysUserUpdateAvatar(params).then((res) => { |
| | | this.uploading = false |
| | | if (res.success) { |
| | | this.visible = false |
| | | this.$message.success('头像上传修改成功') |
| | | } else { |
| | | this.$message.error(res.message) |
| | | } |
| | | }) |
| | | }, |
| | | realTime (data) { |
| | | this.previews = data |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .avatar-upload-preview { |
| | | position: absolute; |
| | | top: 50%; |
| | | transform: translate(50%, -50%); |
| | | width: 200px; |
| | | height: 200px; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 4px #ccc; |
| | | overflow: hidden; |
| | | |
| | | img { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="account-settings-info-view"> |
| | | <a-row :gutter="16"> |
| | | <a-col :md="24" :lg="16"> |
| | | |
| | | <a-form layout="vertical" :form="form"> |
| | | <a-form-item |
| | | label="昵称" |
| | | > |
| | | <a-input placeholder="给自己起个昵称吧" v-decorator="['nickName']"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="生日" |
| | | > |
| | | <a-date-picker placeholder="请选择生日" @change="onChange" style="width: 100%" v-decorator="['birthday', {rules: [{required: true, message: '请选择生日!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="性别" |
| | | > |
| | | <a-radio-group v-decorator="['sex',{rules: [{ required: true, message: '请选择性别!' }]}]" > |
| | | <a-radio v-for="(item,index) in sexData" :key="index" :value="item.code">{{ item.name }}</a-radio> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="手机" |
| | | > |
| | | <a-input placeholder="请输入手机号" v-decorator="['phone', {rules: [{required: true, message: '请输入手机号!'}]}]"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="电话" |
| | | > |
| | | <a-input placeholder="请输入电话" v-decorator="['tel', {rules: [{required: true, message: '请输入电话!'}]}]"/> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="电子邮件" |
| | | :required="false" |
| | | > |
| | | <a-input placeholder="请输入电子邮件地址" v-decorator="['email', {type: 'email',message: '请输入正确的邮箱号',rules: [{required: true, message: '请输入正确的邮箱号!'}]}]"/> |
| | | </a-form-item> |
| | | <a-form-item> |
| | | <a-button type="primary" @click="submitUserInfo">更新基本信息</a-button> |
| | | </a-form-item> |
| | | </a-form> |
| | | |
| | | </a-col> |
| | | <a-col :md="24" :lg="8" :style="{ minHeight: '180px' }"> |
| | | <div class="ant-upload-preview" @click="$refs.modal.edit(userInfo.id)" > |
| | | <a-icon type="cloud-upload-o" class="upload-icon"/> |
| | | <div class="mask"> |
| | | <a-icon type="plus" /> |
| | | </div> |
| | | <img :src="option.img"/> |
| | | </div> |
| | | </a-col> |
| | | |
| | | </a-row> |
| | | |
| | | <avatar-modal ref="modal" @ok="setavatar"/> |
| | | |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import store from '@/store' |
| | | import AvatarModal from './AvatarModal' |
| | | import { mapGetters } from 'vuex' |
| | | import moment from 'moment' |
| | | import { sysUserUpdateInfo } from '@/api/modular/system/userManage' |
| | | // mapActions |
| | | export default { |
| | | components: { |
| | | AvatarModal |
| | | }, |
| | | data () { |
| | | return { |
| | | // cropper |
| | | preview: {}, |
| | | form: this.$form.createForm(this), |
| | | sexData: [], |
| | | option: { |
| | | img: '/avatar2.jpg', |
| | | info: true, |
| | | size: 1, |
| | | outputType: 'jpeg', |
| | | canScale: false, |
| | | autoCrop: true, |
| | | // 只有自动截图开启 宽度高度才生效 |
| | | autoCropWidth: 180, |
| | | autoCropHeight: 180, |
| | | fixedBox: true, |
| | | // 开启宽度和高度比例 |
| | | fixed: true, |
| | | fixedNumber: [1, 1], |
| | | // userInfo |
| | | birthdayString: '' |
| | | } |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['userInfo']) |
| | | }, |
| | | mounted () { |
| | | this.initUserInfo() |
| | | }, |
| | | methods: { |
| | | // ...mapActions(['GetInfo']), |
| | | /** |
| | | * 初始化用户信息 |
| | | */ |
| | | initUserInfo () { |
| | | setTimeout(() => { |
| | | this.form.setFieldsValue( |
| | | { |
| | | birthday: moment(this.userInfo.birthday, 'YYYY-MM-DD'), |
| | | nickName: this.userInfo.nickName, |
| | | sex: this.userInfo.sex.toString(), |
| | | email: this.userInfo.email, |
| | | phone: this.userInfo.phone, |
| | | tel: this.userInfo.tel |
| | | } |
| | | ) |
| | | this.birthdayString = moment(this.userInfo.birthday).format('YYYY-MM-DD') |
| | | this.option.img = process.env.VUE_APP_API_BASE_URL + '/sysFileInfo/preview?id=' + this.userInfo.avatar |
| | | this.getSexData() |
| | | }, 100) |
| | | }, |
| | | /** |
| | | * 日期需单独转换 |
| | | */ |
| | | onChange (date, dateString) { |
| | | this.birthdayString = dateString |
| | | }, |
| | | submitUserInfo () { |
| | | const { form: { validateFields } } = this |
| | | validateFields((err, values) => { |
| | | if (!err) { |
| | | values.birthday = this.birthdayString |
| | | values.id = this.userInfo.id |
| | | sysUserUpdateInfo(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('个人信息更新成功') |
| | | store.dispatch('GetInfo').then(() => {}) |
| | | } else { |
| | | this.$message.error(res.message) |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | getSexData () { |
| | | this.sexData = this.$options.filters['dictData']('sex') |
| | | }, |
| | | setavatar (url) { |
| | | this.option.img = process.env.VUE_APP_API_BASE_URL + '/sysFileInfo/preview?id=' + url |
| | | store.dispatch('GetInfo').then(() => {}) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | |
| | | .avatar-upload-wrapper { |
| | | height: 200px; |
| | | width: 100%; |
| | | } |
| | | |
| | | .ant-upload-preview { |
| | | position: relative; |
| | | margin: 0 auto; |
| | | width: 100%; |
| | | max-width: 180px; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 4px #ccc; |
| | | |
| | | .upload-icon { |
| | | position: absolute; |
| | | top: 0; |
| | | right: 10px; |
| | | font-size: 1.4rem; |
| | | padding: 0.5rem; |
| | | background: rgba(222, 221, 221, 0.7); |
| | | border-radius: 50%; |
| | | border: 1px solid rgba(0, 0, 0, 0.2); |
| | | } |
| | | .mask { |
| | | opacity: 0; |
| | | position: absolute; |
| | | background: rgba(0,0,0,0.4); |
| | | cursor: pointer; |
| | | transition: opacity 0.4s; |
| | | |
| | | &:hover { |
| | | opacity: 1; |
| | | } |
| | | |
| | | i { |
| | | font-size: 2rem; |
| | | position: absolute; |
| | | top: 50%; |
| | | left: 50%; |
| | | margin-left: -1rem; |
| | | margin-top: -1rem; |
| | | color: #d6d6d6; |
| | | } |
| | | } |
| | | |
| | | img, .mask { |
| | | width: 100%; |
| | | max-width: 180px; |
| | | height: 100%; |
| | | border-radius: 50%; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-list |
| | | itemLayout="horizontal" |
| | | :dataSource="data" |
| | | > |
| | | |
| | | </a-list> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data () { |
| | | return { |
| | | data: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <script> |
| | | import { colorList } from '@/components/SettingDrawer/settingConfig' |
| | | import ASwitch from 'ant-design-vue/es/switch' |
| | | import AList from 'ant-design-vue/es/list' |
| | | import AListItem from 'ant-design-vue/es/list/Item' |
| | | import { mixin } from '@/utils/mixin' |
| | | |
| | | const Meta = AListItem.Meta |
| | | |
| | | export default { |
| | | components: { |
| | | AListItem, |
| | | AList, |
| | | ASwitch, |
| | | Meta |
| | | }, |
| | | mixins: [mixin], |
| | | data () { |
| | | return { |
| | | } |
| | | }, |
| | | filters: { |
| | | themeFilter (theme) { |
| | | const themeMap = { |
| | | 'dark': '暗色', |
| | | 'light': '白色' |
| | | } |
| | | return themeMap[theme] |
| | | } |
| | | }, |
| | | methods: { |
| | | colorFilter (color) { |
| | | const c = colorList.find(o => o.color === color) |
| | | return c && c.key |
| | | }, |
| | | |
| | | onChange (checked) { |
| | | if (checked) { |
| | | this.$store.dispatch('ToggleTheme', 'dark') |
| | | } else { |
| | | this.$store.dispatch('ToggleTheme', 'light') |
| | | } |
| | | } |
| | | }, |
| | | render () { |
| | | return ( |
| | | <AList itemLayout="horizontal"> |
| | | <AListItem> |
| | | <Meta> |
| | | <a slot="title">风格配色</a> |
| | | <span slot="description"> |
| | | 整体风格配色设置 |
| | | </span> |
| | | </Meta> |
| | | <div slot="actions"> |
| | | <ASwitch checkedChildren="暗色" unCheckedChildren="白色" defaultChecked={this.navTheme === 'dark' && true || false} onChange={this.onChange} /> |
| | | </div> |
| | | </AListItem> |
| | | <AListItem> |
| | | <Meta> |
| | | <a slot="title">主题色</a> |
| | | <span slot="description"> |
| | | 页面风格配色: <a domPropsInnerHTML={ this.colorFilter(this.primaryColor) }/> |
| | | </span> |
| | | </Meta> |
| | | </AListItem> |
| | | </AList> |
| | | ) |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="page-header-index-wide"> |
| | | <a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }"> |
| | | <div class="account-settings-info-main" :class="device"> |
| | | <div class="account-settings-info-left"> |
| | | <a-menu |
| | | :mode="device == 'mobile' ? 'horizontal' : 'inline'" |
| | | :style="{ border: '0', width: device == 'mobile' ? '560px' : 'auto'}" |
| | | :selectedKeys="selectedKeys" |
| | | type="inner" |
| | | @openChange="onOpenChange" |
| | | > |
| | | <a-menu-item key="/account/settings/base"> |
| | | <router-link :to="{ name: 'BaseSettings' }"> |
| | | 基本设置 |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-item key="/account/settings/security"> |
| | | <router-link :to="{ name: 'SecuritySettings' }"> |
| | | 安全设置 |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-item key="/account/settings/custom"> |
| | | <router-link :to="{ name: 'CustomSettings' }"> |
| | | 个性化 |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-item key="/account/settings/binding"> |
| | | <router-link :to="{ name: 'BindingSettings' }"> |
| | | 账户绑定 |
| | | </router-link> |
| | | </a-menu-item> |
| | | <a-menu-item key="/account/settings/notification"> |
| | | <router-link :to="{ name: 'NotificationSettings' }"> |
| | | 新消息通知 |
| | | </router-link> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </div> |
| | | <div class="account-settings-info-right"> |
| | | <div class="account-settings-info-title"> |
| | | <span>{{ $route.meta.title }}</span> |
| | | </div> |
| | | <route-view></route-view> |
| | | </div> |
| | | </div> |
| | | </a-card> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { PageView, RouteView } from '@/layouts' |
| | | import { mixinDevice } from '@/utils/mixin.js' |
| | | |
| | | export default { |
| | | components: { |
| | | RouteView, |
| | | PageView |
| | | }, |
| | | mixins: [mixinDevice], |
| | | data () { |
| | | return { |
| | | // horizontal inline |
| | | mode: 'inline', |
| | | |
| | | openKeys: [], |
| | | selectedKeys: [], |
| | | |
| | | // cropper |
| | | preview: {}, |
| | | option: { |
| | | img: '/avatar2.jpg', |
| | | info: true, |
| | | size: 1, |
| | | outputType: 'jpeg', |
| | | canScale: false, |
| | | autoCrop: true, |
| | | // 只有自动截图开启 宽度高度才生效 |
| | | autoCropWidth: 180, |
| | | autoCropHeight: 180, |
| | | fixedBox: true, |
| | | // 开启宽度和高度比例 |
| | | fixed: true, |
| | | fixedNumber: [1, 1] |
| | | }, |
| | | |
| | | pageTitle: '' |
| | | } |
| | | }, |
| | | mounted () { |
| | | this.updateMenu() |
| | | }, |
| | | methods: { |
| | | onOpenChange (openKeys) { |
| | | this.openKeys = openKeys |
| | | }, |
| | | updateMenu () { |
| | | const routes = this.$route.matched.concat() |
| | | this.selectedKeys = [ routes.pop().path ] |
| | | } |
| | | }, |
| | | watch: { |
| | | '$route' (val) { |
| | | this.updateMenu() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .account-settings-info-main { |
| | | width: 100%; |
| | | display: flex; |
| | | height: 100%; |
| | | overflow: auto; |
| | | |
| | | &.mobile { |
| | | display: block; |
| | | |
| | | .account-settings-info-left { |
| | | border-right: unset; |
| | | border-bottom: 1px solid #e8e8e8; |
| | | width: 100%; |
| | | height: 50px; |
| | | overflow-x: auto; |
| | | overflow-y: scroll; |
| | | } |
| | | .account-settings-info-right { |
| | | padding: 20px 40px; |
| | | } |
| | | } |
| | | |
| | | .account-settings-info-left { |
| | | border-right: 1px solid #e8e8e8; |
| | | width: 224px; |
| | | } |
| | | |
| | | .account-settings-info-right { |
| | | flex: 1 1; |
| | | padding: 8px 40px; |
| | | |
| | | .account-settings-info-title { |
| | | color: rgba(0,0,0,.85); |
| | | font-size: 20px; |
| | | font-weight: 500; |
| | | line-height: 28px; |
| | | margin-bottom: 12px; |
| | | } |
| | | .account-settings-info-view { |
| | | padding-top: 12px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-list |
| | | itemLayout="horizontal" |
| | | :dataSource="data" |
| | | > |
| | | |
| | | </a-list> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | data () { |
| | | return { |
| | | data: [] |
| | | } |
| | | }, |
| | | methods: { |
| | | |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <a-list |
| | | itemLayout="horizontal" |
| | | :dataSource="data" |
| | | > |
| | | <a-list-item slot="renderItem" slot-scope="item, MenuIndex" :key="MenuIndex"> |
| | | <a-list-item-meta> |
| | | <a slot="title">{{ item.title }}</a> |
| | | <span slot="description"> |
| | | <span class="security-list-description">{{ item.description }}</span> |
| | | <span v-if="item.value"> : </span> |
| | | <span class="security-list-value">{{ item.value }}</span> |
| | | </span> |
| | | </a-list-item-meta> |
| | | <template v-if="item.actions"> |
| | | <a slot="actions" @click="item.actions.callback">{{ item.actions.title }}</a> |
| | | </template> |
| | | </a-list-item> |
| | | </a-list> |
| | | <upd-pwd ref="updPwd"/> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { mapGetters } from 'vuex' |
| | | import UpdPwd from './securityItem/updPwd' |
| | | export default { |
| | | components: { |
| | | UpdPwd |
| | | }, |
| | | data () { |
| | | return { |
| | | data: [] |
| | | } |
| | | }, |
| | | created () { |
| | | if (this.hasPerm('sysUser:updatePwd')) { |
| | | const updPwdMenu = { |
| | | title: '账户密码', |
| | | description: '当前密码强度', |
| | | value: '强', |
| | | actions: { title: '修改', |
| | | callback: () => { |
| | | this.$refs.updPwd.open(this.userInfo.id) |
| | | } |
| | | } |
| | | } |
| | | this.data.push(updPwdMenu) |
| | | } |
| | | const encryptedPhone = { title: '密保手机', description: '已绑定手机', value: '138****8293', actions: { title: '修改', callback: () => { this.$message.success('This is a message of success') } } } |
| | | const encryptedProblem = { title: '密保问题', description: '未设置密保问题,密保问题可有效保护账户安全', value: '', actions: { title: '设置', callback: () => { this.$message.error('This is a message of error') } } } |
| | | const encryptedEmail = { title: '备用邮箱', description: '已绑定邮箱', value: 'ant***sign.com', actions: { title: '修改', callback: () => { this.$message.warning('This is message of warning') } } } |
| | | const encryptedMfa = { title: 'MFA 设备', description: '未绑定 MFA 设备,绑定后,可以进行二次确认', value: '', actions: { title: '绑定', callback: () => { this.$message.info('This is a normal message') } } } |
| | | this.data.push(encryptedPhone) |
| | | this.data.push(encryptedProblem) |
| | | this.data.push(encryptedEmail) |
| | | this.data.push(encryptedMfa) |
| | | }, |
| | | computed: { |
| | | ...mapGetters(['userInfo']) |
| | | }, |
| | | methods: { |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="修改密码" |
| | | :visible="visible_updPwd" |
| | | :confirm-loading="confirmLoading" |
| | | @ok="handleOkUpdPwd" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-form :form="formUpdPwd"> |
| | | <a-form-item |
| | | label="原密码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入原密码" type="password" v-decorator="['password', {rules: [{required: true, message: '请输入原密码!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="新密码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input |
| | | placeholder="请输入新密码" |
| | | type="password" |
| | | v-decorator="['newPassword', {rules: [{required: true, message: '请输入新密码!'},{ |
| | | validator: validateToNextPassword, |
| | | },]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="重复新密码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input |
| | | placeholder="请再次输入新密码" |
| | | type="password" |
| | | v-decorator="['confirm', {rules: [{required: true, message: '请再次输入新密码!'}, |
| | | { |
| | | validator: compareToFirstPassword, |
| | | }]}]" /> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-modal> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | import { sysUserUpdatePwd } from '@/api/modular/system/userManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 16 } |
| | | }, |
| | | confirmLoading: false, |
| | | visible_updPwd: false, |
| | | userId: '', |
| | | formUpdPwd: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | open (id) { |
| | | this.userId = id |
| | | this.visible_updPwd = true |
| | | }, |
| | | handleOkUpdPwd () { |
| | | const { formUpdPwd: { validateFields } } = this |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | this.confirmLoading = true |
| | | values.id = this.userId |
| | | sysUserUpdatePwd(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('修改成功') |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('修改失败:' + res.message) |
| | | } |
| | | // eslint-disable-next-line handle-callback-err |
| | | }).finally((err) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.visible_updPwd = false |
| | | }, |
| | | compareToFirstPassword (rule, value, callback) { |
| | | const formUpdPwd = this.formUpdPwd |
| | | if (value && value !== formUpdPwd.getFieldValue('newPassword')) { |
| | | // eslint-disable-next-line standard/no-callback-literal |
| | | callback('请确认两次输入密码的一致性!') |
| | | } else { |
| | | callback() |
| | | } |
| | | }, |
| | | validateToNextPassword (rule, value, callback) { |
| | | const formUpdPwd = this.formUpdPwd |
| | | if (value && this.confirmDirty) { |
| | | formUpdPwd.validateFields(['confirm'], { force: true }) |
| | | } |
| | | callback() |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="新增应用" |
| | | :width="500" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item |
| | | style="display: none;" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | > |
| | | <a-input v-decorator="['active']" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="应用名称" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入应用名称" v-decorator="['name', {rules: [{required: true, message: '请输入应用名称!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="唯一编码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入唯一编码" v-decorator="['code', {rules: [{required: true, message: '请输入唯一编码!'}]}]" /> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | <script> |
| | | import { sysAppAdd } from '@/api/modular/system/appManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 18 } |
| | | }, |
| | | visible: false, |
| | | confirmLoading: false, |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | add () { |
| | | this.visible = true |
| | | this.form.getFieldDecorator('active', { initialValue: 'N' }) |
| | | }, |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | sysAppAdd(values).then((res) => { |
| | | this.confirmLoading = false |
| | | if (res.success) { |
| | | this.$message.success('新增成功') |
| | | this.handleCancel() |
| | | this.$emit('ok', values) |
| | | } else { |
| | | this.$message.error('新增失败:' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="应用编辑" |
| | | :width="500" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form" > |
| | | <a-form-item |
| | | style="display: none;" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | > |
| | | <a-input v-decorator="['id']" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | style="display: none;" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | > |
| | | <a-input v-decorator="['active']" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="应用名称" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入应用名称" v-decorator="['name', {rules: [{required: true, message: '请输入应用名称!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="唯一编码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入唯一编码" v-decorator="['code', {rules: [{required: true, message: '请输入唯一编码!'}]}]" /> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | <script> |
| | | import { sysAppEdit } from '@/api/modular/system/appManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 18 } |
| | | }, |
| | | visible: false, |
| | | confirmLoading: false, |
| | | visibleDef: false, |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | edit (record) { |
| | | this.visible = true |
| | | setTimeout(() => { |
| | | this.form.setFieldsValue( |
| | | { |
| | | id: record.id, |
| | | name: record.name, |
| | | code: record.code, |
| | | active: record.active |
| | | } |
| | | ) |
| | | }, 100) |
| | | }, |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | sysAppEdit(values).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('编辑成功') |
| | | this.visible = false |
| | | this.confirmLoading = false |
| | | this.$emit('ok', values) |
| | | } else { |
| | | this.$message.error('编辑失败:' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | /* eslint-disable eqeqeq */ |
| | | <template> |
| | | <div> |
| | | <x-card v-if="hasPerm('sysApp:page')"> |
| | | <div slot="content" class="table-page-search-wrapper"> |
| | | <a-form layout="inline"> |
| | | <a-row :gutter="48"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="应用名称"> |
| | | <a-input v-model="queryParam.name" allow-clear placeholder="请输入应用名称"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="唯一编码"> |
| | | <a-input v-model="queryParam.code" allow-clear placeholder="请输入唯一编码"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <span class="table-page-search-submitButtons"> |
| | | <a-button type="primary" @click="$refs.table.refresh(true)">查询</a-button> |
| | | <a-button style="margin-left: 8px" @click="() => queryParam = {}">重置</a-button> |
| | | </span> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </div> |
| | | </x-card> |
| | | <a-card :bordered="false" > |
| | | <a-spin :spinning="loading"> |
| | | <s-table |
| | | ref="table" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | :alert="false" |
| | | :rowKey="(record) => record.id" |
| | | > |
| | | <template slot="operator" v-if="hasPerm('sysApp:add')"> |
| | | <a-button @click="$refs.addForm.add()" icon="plus" type="primary" v-if="hasPerm('sysApp:add')">新增应用</a-button> |
| | | </template> |
| | | <span slot="active" slot-scope="text"> |
| | | {{ activeFilter(text) }} |
| | | </span> |
| | | <span slot="status" slot-scope="text"> |
| | | {{ statusFilter(text) }} |
| | | </span> |
| | | <span slot="action" slot-scope="text, record"> |
| | | <a v-if="hasPerm('sysApp:edit')" @click="$refs.editForm.edit(record)">编辑</a> |
| | | <a-divider type="vertical" v-if="hasPerm('sysApp:edit') & hasPerm('sysApp:delete')" /> |
| | | <a-popconfirm v-if="hasPerm('sysApp:delete')" placement="topRight" title="确认删除?" @confirm="() => sysAppDelete(record)"> |
| | | <a>删除</a> |
| | | </a-popconfirm> |
| | | <a-divider type="vertical" v-if="hasPerm('sysApp:setAsDefault') & hasPerm('sysApp:delete') & record.active == 'N' || hasPerm('sysApp:edit') & hasPerm('sysApp:setAsDefault') & record.active == 'N'" /> |
| | | <a-popconfirm v-if="hasPerm('sysApp:setAsDefault') & record.active == 'N'" placement="topRight" title="设置为默认应用?" @confirm="() => sysDefault(record)"> |
| | | <a>设为默认</a> |
| | | </a-popconfirm> |
| | | </span> |
| | | </s-table> |
| | | <add-form ref="addForm" @ok="handleOk" /> |
| | | <edit-form ref="editForm" @ok="handleOk" /> |
| | | </a-spin> |
| | | </a-card> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { STable, XCard } from '@/components' |
| | | import { getAppPage, sysAppDelete, sysAppSetAsDefault } from '@/api/modular/system/appManage' |
| | | import { sysDictTypeDropDown } from '@/api/modular/system/dictManage' |
| | | import editForm from './editForm' |
| | | import addForm from './addForm' |
| | | export default { |
| | | components: { |
| | | XCard, |
| | | STable, |
| | | editForm, |
| | | addForm |
| | | }, |
| | | data () { |
| | | return { |
| | | // description: '面包屑说明', |
| | | // 查询参数 |
| | | queryParam: {}, |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | | title: '应用名称', |
| | | dataIndex: 'name' |
| | | }, |
| | | { |
| | | title: '唯一编码', |
| | | dataIndex: 'code' |
| | | }, |
| | | { |
| | | title: '是否默认', |
| | | dataIndex: 'active', |
| | | scopedSlots: { customRender: 'active' } |
| | | }, |
| | | { |
| | | title: '状态', |
| | | dataIndex: 'status', |
| | | scopedSlots: { customRender: 'status' } |
| | | } |
| | | ], |
| | | tstyle: { 'padding-bottom': '0px', 'margin-bottom': '10px' }, |
| | | // 加载数据方法 必须为 Promise 对象 |
| | | loadData: parameter => { |
| | | return getAppPage(Object.assign(parameter, this.queryParam)).then((res) => { |
| | | return res.data |
| | | }) |
| | | }, |
| | | loading: false, |
| | | statusDict: [], |
| | | activeDict: [] |
| | | } |
| | | }, |
| | | created () { |
| | | this.sysDictTypeDropDown() |
| | | if (this.hasPerm('sysApp:edit') || this.hasPerm('sysApp:delete') || this.hasPerm('sysApp:setAsDefault')) { |
| | | this.columns.push({ |
| | | title: '操作', |
| | | width: '200px', |
| | | dataIndex: 'action', |
| | | scopedSlots: { customRender: 'action' } |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | activeFilter (active) { |
| | | // eslint-disable-next-line eqeqeq |
| | | const values = this.activeDict.filter(item => item.code == active) |
| | | if (values.length > 0) { |
| | | return values[0].value |
| | | } |
| | | }, |
| | | statusFilter (status) { |
| | | // eslint-disable-next-line eqeqeq |
| | | const values = this.statusDict.filter(item => item.code == status) |
| | | if (values.length > 0) { |
| | | return values[0].value |
| | | } |
| | | }, |
| | | /** |
| | | * 获取字典数据 |
| | | */ |
| | | sysDictTypeDropDown () { |
| | | sysDictTypeDropDown({ code: 'yes_or_no' }).then((res) => { |
| | | this.activeDict = res.data |
| | | }) |
| | | sysDictTypeDropDown({ code: 'common_status' }).then((res) => { |
| | | this.statusDict = res.data |
| | | }) |
| | | }, |
| | | handleOk () { |
| | | this.$refs.table.refresh() |
| | | }, |
| | | sysDefault (record) { |
| | | this.loading = true |
| | | sysAppSetAsDefault({ id: record.id }).then((res) => { |
| | | this.loading = false |
| | | if (res.success) { |
| | | this.$message.success('设置成功') |
| | | this.$refs.table.refresh() |
| | | } else { |
| | | this.$message.error('设置失败:' + res.message) |
| | | } |
| | | }) |
| | | }, |
| | | /** |
| | | * 删除应用 |
| | | */ |
| | | sysAppDelete (record) { |
| | | this.loading = true |
| | | sysAppDelete(record).then((res) => { |
| | | this.loading = false |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.$refs.table.refresh() |
| | | } else { |
| | | this.$message.error('删除失败:' + res.message) |
| | | } |
| | | }).catch((err) => { |
| | | this.$message.error('删除错误:' + err.message) |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style scoped> |
| | | .table-operator { |
| | | margin-bottom: 18px; |
| | | } |
| | | button { |
| | | margin-right: 8px; |
| | | } |
| | | </style> |
New file |
| | |
| | | /* eslint-disable */ |
| | | <template> |
| | | |
| | | <a-card :bordered="false"> |
| | | |
| | | <a-table |
| | | ref="table" |
| | | size="middle" |
| | | :rowKey="(record) => record.id" |
| | | :pagination="false" |
| | | :defaultExpandAllRows="true" |
| | | :columns="columns" |
| | | :dataSource="data" |
| | | :loading="loading" |
| | | showPagination="auto" |
| | | @expand="onExpand"> |
| | | </a-table> |
| | | |
| | | </a-card> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | import { getAreaList } from '@/api/modular/system/areaManage' |
| | | |
| | | export default { |
| | | |
| | | data () { |
| | | return { |
| | | queryParam: {}, |
| | | data: [], |
| | | loading: true, |
| | | // 定义展开过的节点的id数组 |
| | | expandedData: [], |
| | | columns: [ |
| | | { |
| | | title: '名称', |
| | | dataIndex: 'name' |
| | | }, |
| | | { |
| | | title: '层级', |
| | | dataIndex: 'levelCode' |
| | | }, |
| | | { |
| | | title: '简称', |
| | | dataIndex: 'shortName' |
| | | }, |
| | | { |
| | | title: '组合名', |
| | | dataIndex: 'mergerName' |
| | | }, |
| | | { |
| | | title: '拼音', |
| | | dataIndex: 'pinyin' |
| | | }, |
| | | { |
| | | title: '邮编', |
| | | dataIndex: 'zipCode' |
| | | }, |
| | | { |
| | | title: '经度', |
| | | dataIndex: 'lng' |
| | | }, |
| | | { |
| | | title: '纬度', |
| | | dataIndex: 'lat' |
| | | } |
| | | ], |
| | | selectedRowKeys: [] |
| | | } |
| | | }, |
| | | |
| | | created () { |
| | | this.loadData() |
| | | }, |
| | | |
| | | methods: { |
| | | loadData () { |
| | | this.loading = true |
| | | getAreaList(this.queryParam).then((res) => { |
| | | if (res.success) { |
| | | this.data = res.data |
| | | this.removeEmptyChildren(this.data) |
| | | } |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | |
| | | removeEmptyChildren(data) { |
| | | if (data == null || data.length === 0) return |
| | | for (let i = 0; i < data.length; i++) { |
| | | const item = data[i] |
| | | // 如果为最终子节点或其为“市辖区”,则其没有子节点 |
| | | if (item.levelCode === 4 || (item.levelCode === 2 && item.name === '市辖区')) { |
| | | item.children = null |
| | | } |
| | | } |
| | | }, |
| | | onSelectChange (selectedRowKeys) { |
| | | this.selectedRowKeys = selectedRowKeys |
| | | }, |
| | | onExpand(expanded, record) { |
| | | if (expanded) { |
| | | // 判断其是否已经展开过 |
| | | const index = this.expandedData.indexOf(record.id) |
| | | // 如果没展开过,则请求接口 |
| | | if (index === -1) { |
| | | this.queryParam.parentCode = record.areaCode |
| | | getAreaList(this.queryParam).then((res) => { |
| | | if (res.success) { |
| | | // 设置为其子节点 |
| | | record.children = res.data |
| | | this.removeEmptyChildren(record.children) |
| | | // 将其放入展开过的id集合 |
| | | this.expandedData.push(record.id) |
| | | } |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | </script> |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="新增参数" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="formLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item |
| | | label="参数名称" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入参数名称" v-decorator="['name', {rules: [{required: true, message: '请输入参数名称!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="唯一编码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入唯一编码" v-decorator="['code', {rules: [{required: true, message: '请输入唯一编码!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="系统参数" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | > |
| | | <a-radio-group v-decorator="['sysFlag',{rules: [{ required: true, message: '请选择是否为系统参数!' }]}]" > |
| | | <a-radio-button value="Y" > 是 </a-radio-button> |
| | | <a-radio-button value="N" > 否 </a-radio-button> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="所属分类" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-select style="width: 100%" placeholder="请选择所属分类" v-decorator="['groupCode', {rules: [{ required: true, message: '请选择取所属分类!' }]}]" > |
| | | <a-select-option v-for="(item,index) in groupCodeList" :key="index" :value="item.code" >{{ item.value }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="参数值" |
| | | > |
| | | <a-input placeholder="请输入参数值" v-decorator="['value', {rules: [{required: true, message: '请输入参数值!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="备注" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-textarea :rows="4" placeholder="请输入备注" v-decorator="['remark']"></a-textarea> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | <script> |
| | | import { sysDictTypeDropDown, sysConfigAdd } from '@/api/modular/system/configManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | visible: false, |
| | | confirmLoading: false, |
| | | formLoading: true, |
| | | groupCodeList: [], |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | add () { |
| | | this.visible = true |
| | | this.sysDictTypeDropDown() |
| | | }, |
| | | /** |
| | | * 获取所属分类 |
| | | */ |
| | | sysDictTypeDropDown () { |
| | | sysDictTypeDropDown({ code: 'consts_type' }).then((res) => { |
| | | this.groupCodeList = res.data |
| | | this.formLoading = false |
| | | }) |
| | | }, |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | sysConfigAdd(values).then((res) => { |
| | | this.confirmLoading = false |
| | | if (res.success) { |
| | | this.$message.success('新增成功') |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('新增失败:' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <a-modal |
| | | title="参数编辑" |
| | | :width="900" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleSubmit" |
| | | @cancel="handleCancel" |
| | | > |
| | | <a-spin :spinning="formLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item |
| | | style="display: none;" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input v-decorator="['id']" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="参数名称" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入参数名称" v-decorator="['name', {rules: [{required: true, message: '请输入参数名称!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="唯一编码" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入唯一编码" :disabled="editDisabled" v-decorator="['code', {rules: [{required: true, message: '请输入唯一编码!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="系统参数" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | > |
| | | <a-radio-group :disabled="editDisabled" v-decorator="['sysFlag',{rules: [{ required: true, message: '请选择是否为系统参数!' }]}]" > |
| | | <a-radio-button value="Y" > 是 </a-radio-button> |
| | | <a-radio-button value="N" > 否 </a-radio-button> |
| | | </a-radio-group> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="所属分类" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-select :disabled="editDisabled" style="width: 100%" placeholder="请选择所属分类" v-decorator="['groupCode', {rules: [{ required: true, message: '请选择取所属分类!' }]}]" > |
| | | <a-select-option v-for="(item,index) in groupCodeList" :key="index" :value="item.code" >{{ item.value }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="参数值" |
| | | has-feedback |
| | | > |
| | | <a-input placeholder="请输入参数值" v-decorator="['value', {rules: [{required: true, message: '请输入参数值!'}]}]" /> |
| | | </a-form-item> |
| | | <a-form-item |
| | | label="备注" |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | has-feedback |
| | | > |
| | | <a-textarea :rows="4" placeholder="请输入备注" v-decorator="['remark']"></a-textarea> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | <script> |
| | | import { sysDictTypeDropDown, sysConfigEdit } from '@/api/modular/system/configManage' |
| | | export default { |
| | | data () { |
| | | return { |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 15 } |
| | | }, |
| | | visible: false, |
| | | confirmLoading: false, |
| | | formLoading: true, |
| | | groupCodeList: [], |
| | | editDisabled: false, |
| | | form: this.$form.createForm(this) |
| | | } |
| | | }, |
| | | methods: { |
| | | // 初始化方法 |
| | | edit (record) { |
| | | this.visible = true |
| | | setTimeout(() => { |
| | | this.form.setFieldsValue( |
| | | { |
| | | id: record.id, |
| | | name: record.name, |
| | | code: record.code, |
| | | groupCode: record.groupCode, |
| | | sysFlag: record.sysFlag, |
| | | value: record.value, |
| | | remark: record.remark |
| | | } |
| | | ) |
| | | }, 100) |
| | | // eslint-disable-next-line eqeqeq |
| | | if (record.sysFlag == 'Y') { |
| | | this.editDisabled = true |
| | | } |
| | | this.sysDictTypeDropDown() |
| | | }, |
| | | /** |
| | | * 获取所属分类 |
| | | */ |
| | | sysDictTypeDropDown () { |
| | | sysDictTypeDropDown({ code: 'consts_type' }).then((res) => { |
| | | this.groupCodeList = res.data |
| | | this.formLoading = false |
| | | }) |
| | | }, |
| | | handleSubmit () { |
| | | const { form: { validateFields } } = this |
| | | this.confirmLoading = true |
| | | validateFields((errors, values) => { |
| | | if (!errors) { |
| | | sysConfigEdit(values).then((res) => { |
| | | this.confirmLoading = false |
| | | if (res.success) { |
| | | this.$message.success('编辑成功') |
| | | this.$emit('ok', values) |
| | | this.handleCancel() |
| | | } else { |
| | | this.$message.error('编辑失败:' + res.message) |
| | | } |
| | | }).finally((res) => { |
| | | this.confirmLoading = false |
| | | }) |
| | | } else { |
| | | this.confirmLoading = false |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel () { |
| | | this.form.resetFields() |
| | | this.visible = false |
| | | this.editDisabled = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <x-card v-if="hasPerm('sysConfig:page')"> |
| | | <div slot="content" class="table-page-search-wrapper" > |
| | | <a-form layout="inline"> |
| | | <a-row :gutter="48"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="参数名称"> |
| | | <a-input v-model="queryParam.name" allow-clear placeholder="请输入参数名称"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="唯一编码"> |
| | | <a-input v-model="queryParam.code" allow-clear placeholder="请输入唯一编码"/> |
| | | </a-form-item> |
| | | </a-col> |
| | | <template v-if="advanced"> |
| | | <a-col :md="8" :sm="24"> |
| | | <a-form-item label="所属分类"> |
| | | <a-select v-model="queryParam.groupCode" placeholder="请选择所属分类" allow-clear> |
| | | <a-select-option v-for="(item,index) in groupCodeDictTypeDropDown" :key="index" :value="item.code" >{{ item.name }}</a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | </template> |
| | | <a-col :md="!advanced && 8 || 24" :sm="24" > |
| | | <span class="table-page-search-submitButtons" :style="advanced && { float: 'right', overflow: 'hidden' } || {} "> |
| | | <a-button type="primary" @click="$refs.table.refresh(true)" >查询</a-button> |
| | | <a-button style="margin-left: 8px" @click="() => queryParam = {}">重置</a-button> |
| | | <a @click="toggleAdvanced" style="margin-left: 8px"> |
| | | {{ advanced ? '收起' : '展开' }} |
| | | <a-icon :type="advanced ? 'up' : 'down'"/> |
| | | </a> |
| | | </span> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </div> |
| | | </x-card> |
| | | <a-card :bordered="false"> |
| | | <s-table |
| | | ref="table" |
| | | :columns="columns" |
| | | :data="loadData" |
| | | :alert="false" |
| | | :rowKey="(record) => record.code" |
| | | > |
| | | <template slot="operator" v-if="hasPerm('sysConfig:add')"> |
| | | <a-button @click="$refs.addForm.add()" icon="plus" type="primary" v-if="hasPerm('sysConfig:add')">新增配置</a-button> |
| | | </template> |
| | | <span slot="name" slot-scope="text"> |
| | | <ellipsis :length="20" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="code" slot-scope="text"> |
| | | <ellipsis :length="10" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="value" slot-scope="text"> |
| | | <ellipsis :length="16" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="remark" slot-scope="text"> |
| | | <ellipsis :length="16" tooltip>{{ text }}</ellipsis> |
| | | </span> |
| | | <span slot="groupCode" slot-scope="text"> |
| | | {{ 'consts_type' | dictType(text) }} |
| | | </span> |
| | | <span slot="action" slot-scope="text, record"> |
| | | <a v-if="hasPerm('sysConfig:edit')" @click="$refs.editForm.edit(record)">编辑</a> |
| | | <a-divider type="vertical" v-if="hasPerm('sysConfig:edit') & hasPerm('sysConfig:delete')"/> |
| | | <a-popconfirm v-if="hasPerm('sysConfig:delete')" placement="topRight" title="确认删除?" @confirm="() => sysConfigDelete(record)"> |
| | | <a>删除</a> |
| | | </a-popconfirm> |
| | | </span> |
| | | </s-table> |
| | | <add-form ref="addForm" @ok="handleOk" v-if="hasPerm('sysConfig:add')"/> |
| | | <edit-form ref="editForm" @ok="handleOk" v-if="hasPerm('sysConfig:edit')"/> |
| | | </a-card> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import { STable, Ellipsis, XCard } from '@/components' |
| | | import { sysConfigPage, sysConfigDelete } from '@/api/modular/system/configManage' |
| | | import addForm from './addForm' |
| | | import editForm from './editForm' |
| | | export default { |
| | | components: { |
| | | XCard, |
| | | STable, |
| | | Ellipsis, |
| | | addForm, |
| | | editForm |
| | | }, |
| | | data () { |
| | | return { |
| | | // 高级搜索 展开/关闭 |
| | | advanced: false, |
| | | // 查询参数 |
| | | queryParam: {}, |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | | title: '参数名称', |
| | | dataIndex: 'name', |
| | | scopedSlots: { customRender: 'name' } |
| | | }, |
| | | { |
| | | title: '唯一编码', |
| | | dataIndex: 'code', |
| | | scopedSlots: { customRender: 'code' } |
| | | }, |
| | | { |
| | | title: '参数值', |
| | | dataIndex: 'value', |
| | | scopedSlots: { customRender: 'value' } |
| | | }, |
| | | { |
| | | title: '所属分类', |
| | | dataIndex: 'groupCode', |
| | | scopedSlots: { customRender: 'groupCode' } |
| | | }, |
| | | { |
| | | title: '备注', |
| | | dataIndex: 'remark', |
| | | scopedSlots: { customRender: 'remark' } |
| | | } |
| | | ], |
| | | // 加载数据方法 必须为 Promise 对象 |
| | | loadData: parameter => { |
| | | return sysConfigPage(Object.assign(parameter, this.queryParam)).then((res) => { |
| | | return res.data |
| | | }) |
| | | }, |
| | | groupCodeDictTypeDropDown: [] |
| | | } |
| | | }, |
| | | /** |
| | | * 初始化判断按钮权限是否拥有,没有则不现实列 |
| | | */ |
| | | created () { |
| | | this.sysDictTypeDropDown() |
| | | if (this.hasPerm('sysConfig:edit') || this.hasPerm('sysConfig:delete')) { |
| | | this.columns.push({ |
| | | title: '操作', |
| | | width: '150px', |
| | | dataIndex: 'action', |
| | | scopedSlots: { customRender: 'action' } |
| | | }) |
| | | } |
| | | }, |
| | | methods: { |
| | | /** |
| | | * 获取字典数据 |
| | | */ |
| | | sysDictTypeDropDown () { |
| | | this.groupCodeDictTypeDropDown = this.$options.filters['dictData']('consts_type') |
| | | }, |
| | | sysConfigDelete (record) { |
| | | sysConfigDelete(record).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.$refs.table.refresh() |
| | | } else { |
| | | this.$message.error('删除失败:' + res.message) |
| | | } |
| | | }).catch((err) => { |
| | | this.$message.error('删除错误:' + err.message) |
| | | }) |
| | | }, |
| | | toggleAdvanced () { |
| | | this.advanced = !this.advanced |
| | | }, |
| | | handleOk () { |
| | | this.$refs.table.refresh() |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less"> |
| | | .table-operator { |
| | | margin-bottom: 18px; |
| | | } |
| | | button { |
| | | margin-right: 8px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="page-header-index-wide"> |
| | | <a-row :gutter="24"> |
| | | <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }"> |
| | | <chart-card :loading="loading" title="总销售额" total="¥126,560"> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" /> |
| | | </a-tooltip> |
| | | <div> |
| | | <trend flag="up" style="margin-right: 16px;"> |
| | | <span slot="term">周同比</span> |
| | | 12% |
| | | </trend> |
| | | <trend flag="down"> |
| | | <span slot="term">日同比</span> |
| | | 11% |
| | | </trend> |
| | | </div> |
| | | <template slot="footer">日均销售额<span>¥ 234.56</span></template> |
| | | </chart-card> |
| | | </a-col> |
| | | <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }"> |
| | | <chart-card :loading="loading" title="访问量" :total="8846 | NumberFormat"> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" /> |
| | | </a-tooltip> |
| | | <div> |
| | | <mini-area /> |
| | | </div> |
| | | <template slot="footer">日访问量<span> {{ '1234' | NumberFormat }}</span></template> |
| | | </chart-card> |
| | | </a-col> |
| | | <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }"> |
| | | <chart-card :loading="loading" title="支付笔数" :total="6560 | NumberFormat"> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" /> |
| | | </a-tooltip> |
| | | <div> |
| | | <mini-bar /> |
| | | </div> |
| | | <template slot="footer">转化率 <span>60%</span></template> |
| | | </chart-card> |
| | | </a-col> |
| | | <a-col :sm="24" :md="12" :xl="6" :style="{ marginBottom: '24px' }"> |
| | | <chart-card :loading="loading" title="运营活动效果" total="78%"> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" /> |
| | | </a-tooltip> |
| | | <div> |
| | | <mini-progress color="rgb(19, 194, 194)" :target="80" :percentage="78" height="8px" /> |
| | | </div> |
| | | <template slot="footer"> |
| | | <trend flag="down" style="margin-right: 16px;"> |
| | | <span slot="term">同周比</span> |
| | | 12% |
| | | </trend> |
| | | <trend flag="up"> |
| | | <span slot="term">日环比</span> |
| | | 80% |
| | | </trend> |
| | | </template> |
| | | </chart-card> |
| | | </a-col> |
| | | </a-row> |
| | | <a-card :loading="loading" :bordered="false" :body-style="{padding: '0'}"> |
| | | <div class="salesCard"> |
| | | <a-tabs default-active-key="1" size="large" :tab-bar-style="{marginBottom: '24px', paddingLeft: '16px'}"> |
| | | <div class="extra-wrapper" slot="tabBarExtraContent"> |
| | | <div class="extra-item"> |
| | | <a>今日</a> |
| | | <a>本周</a> |
| | | <a>本月</a> |
| | | <a>本年</a> |
| | | </div> |
| | | <a-range-picker :style="{width: '230px'}" /> |
| | | </div> |
| | | <a-tab-pane loading="true" tab="销售额" key="1"> |
| | | <a-row> |
| | | <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24"> |
| | | <bar :data="barData" title="销售额排行" /> |
| | | </a-col> |
| | | <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24"> |
| | | <rank-list title="门店销售排行榜" :list="rankList"/> |
| | | </a-col> |
| | | </a-row> |
| | | </a-tab-pane> |
| | | <a-tab-pane tab="访问量" key="2"> |
| | | <a-row> |
| | | <a-col :xl="16" :lg="12" :md="12" :sm="24" :xs="24"> |
| | | <bar :data="barData2" title="销售额趋势" /> |
| | | </a-col> |
| | | <a-col :xl="8" :lg="12" :md="12" :sm="24" :xs="24"> |
| | | <rank-list title="门店销售排行榜" :list="rankList"/> |
| | | </a-col> |
| | | </a-row> |
| | | </a-tab-pane> |
| | | </a-tabs> |
| | | </div> |
| | | </a-card> |
| | | <div class="antd-pro-pages-dashboard-analysis-twoColLayout" :class="isDesktop() ? 'desktop' : ''"> |
| | | <a-row :gutter="24" type="flex" :style="{ marginTop: '24px' }"> |
| | | <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24"> |
| | | <a-card :loading="loading" :bordered="false" title="线上热门搜索" :style="{ height: '100%' }"> |
| | | <a-dropdown :trigger="['click']" placement="bottomLeft" slot="extra"> |
| | | <a class="ant-dropdown-link" href="#"> |
| | | <a-icon type="ellipsis" /> |
| | | </a> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item> |
| | | <a href="javascript:;">操作一</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">操作二</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | <a-row :gutter="68"> |
| | | <a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}"> |
| | | <number-info :total="12321" :sub-total="17.1"> |
| | | <span slot="subtitle"> |
| | | <span>搜索用户数</span> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" /> |
| | | </a-tooltip> |
| | | </span> |
| | | </number-info> |
| | | <!-- miniChart --> |
| | | <div> |
| | | <mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" /> |
| | | </div> |
| | | </a-col> |
| | | <a-col :xs="24" :sm="12" :style="{ marginBottom: ' 24px'}"> |
| | | <number-info :total="2.7" :sub-total="26.2" status="down"> |
| | | <span slot="subtitle"> |
| | | <span>人均搜索次数</span> |
| | | <a-tooltip title="指标说明" slot="action"> |
| | | <a-icon type="info-circle-o" :style="{ marginLeft: '8px' }" /> |
| | | </a-tooltip> |
| | | </span> |
| | | </number-info> |
| | | <!-- miniChart --> |
| | | <div> |
| | | <mini-smooth-area :style="{ height: '45px' }" :dataSource="searchUserData" :scale="searchUserScale" /> |
| | | </div> |
| | | </a-col> |
| | | </a-row> |
| | | <div class="ant-table-wrapper"> |
| | | <a-table |
| | | row-key="index" |
| | | size="small" |
| | | :columns="searchTableColumns" |
| | | :dataSource="searchData" |
| | | :pagination="{ pageSize: 5 }" |
| | | > |
| | | <span slot="range" slot-scope="text, record"> |
| | | <trend :flag="record.status === 0 ? 'up' : 'down'"> |
| | | {{ text }}% |
| | | </trend> |
| | | </span> |
| | | </a-table> |
| | | </div> |
| | | </a-card> |
| | | </a-col> |
| | | <a-col :xl="12" :lg="24" :md="24" :sm="24" :xs="24"> |
| | | <a-card class="antd-pro-pages-dashboard-analysis-salesCard" :loading="loading" :bordered="false" title="销售额类别占比" :style="{ height: '100%' }"> |
| | | <div slot="extra" style="height: inherit;"> |
| | | <!-- style="bottom: 12px;display: inline-block;" --> |
| | | <span class="dashboard-analysis-iconGroup"> |
| | | <a-dropdown :trigger="['click']" placement="bottomLeft"> |
| | | <a-icon type="ellipsis" class="ant-dropdown-link" /> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item> |
| | | <a href="javascript:;">操作一</a> |
| | | </a-menu-item> |
| | | <a-menu-item> |
| | | <a href="javascript:;">操作二</a> |
| | | </a-menu-item> |
| | | </a-menu> |
| | | </a-dropdown> |
| | | </span> |
| | | <div class="analysis-salesTypeRadio"> |
| | | <a-radio-group defaultValue="a"> |
| | | <a-radio-button value="a">全部渠道</a-radio-button> |
| | | <a-radio-button value="b">线上</a-radio-button> |
| | | <a-radio-button value="c">门店</a-radio-button> |
| | | </a-radio-group> |
| | | </div> |
| | | </div> |
| | | <h4>销售额</h4> |
| | | <div> |
| | | <!-- style="width: calc(100% - 240px);" --> |
| | | <div> |
| | | <v-chart :force-fit="true" :height="405" :data="pieData" :scale="pieScale"> |
| | | <v-tooltip :showTitle="false" dataKey="item*percent" /> |
| | | <v-axis /> |
| | | <!-- position="right" :offsetX="-140" --> |
| | | <v-legend dataKey="item"/> |
| | | <v-pie position="percent" color="item" :vStyle="pieStyle" /> |
| | | <v-coord type="theta" :radius="0.75" :innerRadius="0.6" /> |
| | | </v-chart> |
| | | </div> |
| | | </div> |
| | | </a-card> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script> |
| | | import moment from 'moment' |
| | | import { ChartCard, MiniArea, MiniBar, MiniProgress, RankList, Bar, Trend, NumberInfo, MiniSmoothArea } from '@/components' |
| | | import { mixinDevice } from '@/utils/mixin' |
| | | const barData = [] |
| | | const barData2 = [] |
| | | for (let i = 0; i < 12; i += 1) { |
| | | barData.push({ |
| | | x: `${i + 1}月`, |
| | | y: Math.floor(Math.random() * 1000) + 200 |
| | | }) |
| | | barData2.push({ |
| | | x: `${i + 1}月`, |
| | | y: Math.floor(Math.random() * 1000) + 200 |
| | | }) |
| | | } |
| | | const rankList = [] |
| | | for (let i = 0; i < 7; i++) { |
| | | rankList.push({ |
| | | name: '白鹭岛 ' + (i + 1) + ' 号店', |
| | | total: 1234.56 - i * 100 |
| | | }) |
| | | } |
| | | const searchUserData = [] |
| | | for (let i = 0; i < 7; i++) { |
| | | searchUserData.push({ |
| | | x: moment().add(i, 'days').format('YYYY-MM-DD'), |
| | | y: Math.ceil(Math.random() * 10) |
| | | }) |
| | | } |
| | | const searchUserScale = [ |
| | | { |
| | | dataKey: 'x', |
| | | alias: '时间' |
| | | }, |
| | | { |
| | | dataKey: 'y', |
| | | alias: '用户数', |
| | | min: 0, |
| | | max: 10 |
| | | }] |
| | | const searchTableColumns = [ |
| | | { |
| | | dataIndex: 'MenuIndex.vue', |
| | | title: '排名', |
| | | width: 90 |
| | | }, |
| | | { |
| | | dataIndex: 'keyword', |
| | | title: '搜索关键词' |
| | | }, |
| | | { |
| | | dataIndex: 'count', |
| | | title: '用户数' |
| | | }, |
| | | { |
| | | dataIndex: 'range', |
| | | title: '周涨幅', |
| | | align: 'right', |
| | | sorter: (a, b) => a.range - b.range, |
| | | scopedSlots: { customRender: 'range' } |
| | | } |
| | | ] |
| | | const searchData = [] |
| | | for (let i = 0; i < 50; i += 1) { |
| | | searchData.push({ |
| | | index: i + 1, |
| | | keyword: `搜索关键词-${i}`, |
| | | count: Math.floor(Math.random() * 1000), |
| | | range: Math.floor(Math.random() * 100), |
| | | status: Math.floor((Math.random() * 10) % 2) |
| | | }) |
| | | } |
| | | const DataSet = require('@antv/data-set') |
| | | const sourceData = [ |
| | | { item: '家用电器', count: 32.2 }, |
| | | { item: '食用酒水', count: 21 }, |
| | | { item: '个护健康', count: 17 }, |
| | | { item: '服饰箱包', count: 13 }, |
| | | { item: '母婴产品', count: 9 }, |
| | | { item: '其他', count: 7.8 } |
| | | ] |
| | | const pieScale = [{ |
| | | dataKey: 'percent', |
| | | min: 0, |
| | | formatter: '.0%' |
| | | }] |
| | | const dv = new DataSet.View().source(sourceData) |
| | | dv.transform({ |
| | | type: 'percent', |
| | | field: 'count', |
| | | dimension: 'item', |
| | | as: 'percent' |
| | | }) |
| | | const pieData = dv.rows |
| | | export default { |
| | | name: 'Analysis', |
| | | mixins: [mixinDevice], |
| | | components: { |
| | | ChartCard, |
| | | MiniArea, |
| | | MiniBar, |
| | | MiniProgress, |
| | | RankList, |
| | | Bar, |
| | | Trend, |
| | | NumberInfo, |
| | | MiniSmoothArea |
| | | }, |
| | | data () { |
| | | return { |
| | | loading: true, |
| | | rankList, |
| | | // 搜索用户数 |
| | | searchUserData, |
| | | searchUserScale, |
| | | searchTableColumns, |
| | | searchData, |
| | | barData, |
| | | barData2, |
| | | // |
| | | pieScale, |
| | | pieData, |
| | | sourceData, |
| | | pieStyle: { |
| | | stroke: '#fff', |
| | | lineWidth: 1 |
| | | } |
| | | } |
| | | }, |
| | | created () { |
| | | setTimeout(() => { |
| | | this.loading = !this.loading |
| | | }, 1000) |
| | | } |
| | | } |
| | | </script> |
| | | <style lang="less" scoped> |
| | | .extra-wrapper { |
| | | line-height: 55px; |
| | | padding-right: 24px; |
| | | .extra-item { |
| | | display: inline-block; |
| | | margin-right: 24px; |
| | | |
| | | a { |
| | | margin-left: 24px; |
| | | } |
| | | } |
| | | } |
| | | .antd-pro-pages-dashboard-analysis-twoColLayout { |
| | | position: relative; |
| | | display: flex; |
| | | display: block; |
| | | flex-flow: row wrap; |
| | | } |
| | | .antd-pro-pages-dashboard-analysis-salesCard { |
| | | height: calc(100% - 24px); |
| | | /deep/ .ant-card-head { |
| | | position: relative; |
| | | } |
| | | } |
| | | .dashboard-analysis-iconGroup { |
| | | i { |
| | | margin-left: 16px; |
| | | color: rgba(0,0,0,.45); |
| | | cursor: pointer; |
| | | transition: color .32s; |
| | | color: black; |
| | | } |
| | | } |
| | | .analysis-salesTypeRadio { |
| | | position: absolute; |
| | | right: 54px; |
| | | bottom: 12px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | Monitor |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'Monitor' |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <h2>本页面内容均为测试功能,暂不提供稳定性保证</h2> |
| | | <a-divider /> |
| | | <div class="multi-tab-test"> |
| | | <h4>多标签组件测试功能</h4> |
| | | <a-button @click="handleCloseCurrentTab" style="margin-right: 16px;">关闭当前页</a-button> |
| | | <a-button @click="handleOpenTab" style="margin-right: 16px;">打开 任务列表</a-button> |
| | | <a-popconfirm :visible="visible" @confirm="confirm" @cancel="cancel" okText="确定" cancelText="取消"> |
| | | <template v-slot:title> |
| | | <div> |
| | | <a-form :form="form" layout="inline"> |
| | | <a-form-item label="自定义名称"> |
| | | <a-input v-decorator="['tabName', {rules: [{required: true, message: '请输入新的 Tab 名称'}]}]"/> |
| | | </a-form-item> |
| | | </a-form> |
| | | </div> |
| | | </template> |
| | | <a-button @click="() => visible = !visible" style="margin-right: 16px;">修改当前 Tab 名称</a-button> |
| | | </a-popconfirm> |
| | | |
| | | <a-popconfirm :visible="visible2" @confirm="confirm2" @cancel="() => visible2 = false" okText="确定" cancelText="取消"> |
| | | <template v-slot:title> |
| | | <div> |
| | | <p>页面 KEY 是由页面的路由 <code>path</code> 决定的</p> |
| | | <p>如果要修改某一个页面标题,该页面必须已经被打开在 Tab 栏</p> |
| | | <p>后期可以考虑优化到编程式 Tab 栏,就可以没有这种限制</p> |
| | | <a-form :form="form2" layout="inline"> |
| | | <a-form-item label="页面KEY"> |
| | | <a-input v-decorator="['tabKey', { initialValue: '/dashboard/workplace' }]" /> |
| | | </a-form-item> |
| | | <a-form-item label="自定义名称"> |
| | | <a-input v-decorator="['tabName', {rules: [{required: true, message: '请输入新的 Tab 名称'}]}]"/> |
| | | </a-form-item> |
| | | </a-form> |
| | | </div> |
| | | </template> |
| | | <a-button @click="() => visible2 = !visible2">修改某一个 Tab 名称</a-button> |
| | | </a-popconfirm> |
| | | </div> |
| | | <a-divider /> |
| | | <div class="page-loading-test"> |
| | | <h4>全局遮罩测试</h4> |
| | | <a-button @click="handleOpenLoading" style="margin-right: 16px;">打开遮罩(5s 自动关闭)</a-button> |
| | | <a-button @click="handleOpenLoadingCustomTip">打开遮罩(自定义提示语)</a-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: 'TestWork', |
| | | data () { |
| | | return { |
| | | visible: false, |
| | | visible2: false |
| | | } |
| | | }, |
| | | created () { |
| | | this.form = this.$form.createForm(this) |
| | | this.form2 = this.$form.createForm(this) |
| | | }, |
| | | methods: { |
| | | handleCloseCurrentTab () { |
| | | this.$multiTab.closeCurrentPage() // or this.$multiTab.close() |
| | | }, |
| | | handleOpenTab () { |
| | | this.$multiTab.open('/features/task') |
| | | }, |
| | | |
| | | handleOpenLoading () { |
| | | this.$nextTick(function () { |
| | | console.log('this', this) |
| | | console.log('this.$refs.tInput', this.$refs.tInput) |
| | | }) |
| | | this.$loading.show() |
| | | setTimeout(() => { |
| | | this.$loading.hide() |
| | | }, 5000) |
| | | }, |
| | | handleOpenLoadingCustomTip () { |
| | | this.$loading.show({ tip: '自定义提示语' }) |
| | | setTimeout(() => { |
| | | this.$loading.hide() |
| | | }, 5000) |
| | | }, |
| | | |
| | | // confirm |
| | | confirm (e) { |
| | | e.stopPropagation() |
| | | const { path } = this.$route |
| | | this.form.validateFields((err, values) => { |
| | | if (!err) { |
| | | this.$multiTab.rename(path, values.tabName) |
| | | this.visible = false |
| | | } |
| | | }) |
| | | }, |
| | | cancel () { |
| | | this.visible = false |
| | | }, |
| | | confirm2 (e) { |
| | | e.stopPropagation() |
| | | this.form2.validateFields((err, values) => { |
| | | if (!err) { |
| | | this.$multiTab.rename(values.tabKey, values.tabName) |
| | | this.visible2 = false |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <page-view :avatar="avatar" :title="false"> |
| | | <div slot="headerContent"> |
| | | <div class="title">{{ timeFix }},{{ user.name }}<span class="welcome-text">,{{ welcome }}</span></div> |
| | | <div style="margin-bottom:10px">前端工程师 | 蚂蚁金服 - 某某某事业群 - VUE平台</div> |
| | | </div> |
| | | <div slot="extra"> |
| | | <a-row class="more-info"> |
| | | <a-col :span="8"> |
| | | <head-info title="项目" content="56" :center="false" :bordered="false"/> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <head-info title="团队排名" content="8/24" :center="false" :bordered="false"/> |
| | | </a-col> |
| | | <a-col :span="8"> |
| | | <head-info title="项目数" content="2,223" :center="false" /> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | |
| | | <div> |
| | | <a-row :gutter="24"> |
| | | <a-col :xl="16" :lg="24" :md="24" :sm="24" :xs="24"> |
| | | <a-card |
| | | class="project-list" |
| | | :loading="loading" |
| | | style="margin-bottom: 24px;" |
| | | :bordered="false" |
| | | title="进行中的项目" |
| | | :body-style="{ padding: 0 }"> |
| | | <a slot="extra">全部项目</a> |
| | | <div> |
| | | <a-card-grid class="project-card-grid" :key="i" v-for="(item, i) in projects"> |
| | | <a-card :bordered="false" :body-style="{ padding: 0 }"> |
| | | <a-card-meta> |
| | | <div slot="title" class="card-title"> |
| | | <a-avatar size="small" :src="item.cover"/> |
| | | <a>{{ item.title }}</a> |
| | | </div> |
| | | <div slot="description" class="card-description"> |
| | | {{ item.description }} |
| | | </div> |
| | | </a-card-meta> |
| | | <div class="project-item"> |
| | | <a href="/#/">科学搬砖组</a> |
| | | <span class="datetime">9小时前</span> |
| | | </div> |
| | | </a-card> |
| | | </a-card-grid> |
| | | </div> |
| | | </a-card> |
| | | |
| | | <a-card :loading="loading" title="动态" :bordered="false"> |
| | | <a-list> |
| | | <a-list-item :key="index" v-for="(item, index) in activities"> |
| | | <a-list-item-meta> |
| | | <a-avatar slot="avatar" :src="item.user.avatar" /> |
| | | <div slot="title"> |
| | | <span>{{ item.user.nickname }}</span> |
| | | 在 <a href="#">{{ item.project.name }}</a> |
| | | <span>{{ item.project.action }}</span> |
| | | <a href="#">{{ item.project.event }}</a> |
| | | </div> |
| | | <div slot="description">{{ item.time }}</div> |
| | | </a-list-item-meta> |
| | | </a-list-item> |
| | | </a-list> |
| | | </a-card> |
| | | </a-col> |
| | | <a-col |
| | | style="padding: 0 12px" |
| | | :xl="8" |
| | | :lg="24" |
| | | :md="24" |
| | | :sm="24" |
| | | :xs="24"> |
| | | <a-card title="快速开始 / 便捷导航" style="margin-bottom: 24px" :bordered="false" :body-style="{padding: 0}"> |
| | | <div class="item-group"> |
| | | <a>操作一</a> |
| | | <a>操作二</a> |
| | | <a>操作三</a> |
| | | <a>操作四</a> |
| | | <a>操作五</a> |
| | | <a>操作六</a> |
| | | <a-button size="small" type="primary" ghost icon="plus">添加</a-button> |
| | | </div> |
| | | </a-card> |
| | | <a-card title="XX 指数" style="margin-bottom: 24px" :loading="radarLoading" :bordered="false" :body-style="{ padding: 0 }"> |
| | | <div style="min-height: 400px;"> |
| | | <!-- :scale="scale" :axis1Opts="axis1Opts" :axis2Opts="axis2Opts" --> |
| | | <radar :data="radarData" /> |
| | | </div> |
| | | </a-card> |
| | | <a-card :loading="loading" title="团队" :bordered="false"> |
| | | <div class="members"> |
| | | <a-row> |
| | | <a-col :span="12" v-for="(item, index) in teams" :key="index"> |
| | | <a> |
| | | <a-avatar size="small" :src="item.avatar" /> |
| | | <span class="member">{{ item.name }}</span> |
| | | </a> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | </a-card> |
| | | </a-col> |
| | | </a-row> |
| | | </div> |
| | | </page-view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { timeFix } from '@/utils/util' |
| | | import { mapState } from 'vuex' |
| | | import { PageView } from '@/layouts' |
| | | import HeadInfo from '@/components/tools/HeadInfo' |
| | | import { Radar } from '@/components' |
| | | const DataSet = require('@antv/data-set') |
| | | |
| | | export default { |
| | | name: 'Workplace', |
| | | components: { |
| | | PageView, |
| | | HeadInfo, |
| | | Radar |
| | | }, |
| | | data () { |
| | | return { |
| | | timeFix: timeFix(), |
| | | avatar: '', |
| | | user: {}, |
| | | |
| | | projects: [], |
| | | loading: true, |
| | | radarLoading: true, |
| | | activities: [], |
| | | teams: [], |
| | | |
| | | // data |
| | | axis1Opts: { |
| | | dataKey: 'item', |
| | | line: null, |
| | | tickLine: null, |
| | | grid: { |
| | | lineStyle: { |
| | | lineDash: null |
| | | }, |
| | | hideFirstLine: false |
| | | } |
| | | }, |
| | | axis2Opts: { |
| | | dataKey: 'score', |
| | | line: null, |
| | | tickLine: null, |
| | | grid: { |
| | | type: 'polygon', |
| | | lineStyle: { |
| | | lineDash: null |
| | | } |
| | | } |
| | | }, |
| | | scale: [{ |
| | | dataKey: 'score', |
| | | min: 0, |
| | | max: 80 |
| | | }], |
| | | axisData: [ |
| | | { item: '引用', a: 70, b: 30, c: 40 }, |
| | | { item: '口碑', a: 60, b: 70, c: 40 }, |
| | | { item: '产量', a: 50, b: 60, c: 40 }, |
| | | { item: '贡献', a: 40, b: 50, c: 40 }, |
| | | { item: '热度', a: 60, b: 70, c: 40 }, |
| | | { item: '引用', a: 70, b: 50, c: 40 } |
| | | ], |
| | | radarData: [] |
| | | } |
| | | }, |
| | | computed: { |
| | | ...mapState({ |
| | | nickname: (state) => state.user.nickname, |
| | | welcome: (state) => state.user.welcome |
| | | }), |
| | | userInfo () { |
| | | return this.$store.getters.userInfo |
| | | } |
| | | }, |
| | | created () { |
| | | this.user = this.userInfo |
| | | this.avatar = process.env.VUE_APP_API_BASE_URL + '/sysFileInfo/preview?id=' + this.userInfo.avatar |
| | | }, |
| | | mounted () { |
| | | this.getProjects() |
| | | this.getActivity() |
| | | this.getTeams() |
| | | this.initRadar() |
| | | }, |
| | | methods: { |
| | | getProjects () { |
| | | this.projects = [{ |
| | | id: 1, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png', |
| | | title: 'Alipay', |
| | | description: '那是一种内在的东西, 他们到达不了,也无法触及的', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 2, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png', |
| | | title: 'Angular', |
| | | description: '希望是一个好东西,也许是最好的,好东西是不会消亡的', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 3, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/dURIMkkrRFpPgTuzkwnB.png', |
| | | title: 'Ant Design', |
| | | description: '城镇中有那么多的酒馆,她却偏偏走进了我的酒馆', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 4, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png', |
| | | title: 'Snowy', |
| | | description: '那时候我只会想自己想要什么,从不想自己拥有什么', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 5, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/siCrBXXhmvTQGWPNLBow.png', |
| | | title: 'Bootstrap', |
| | | description: '凛冬将至', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | }, |
| | | { |
| | | id: 6, |
| | | cover: 'https://gw.alipayobjects.com/zos/rmsportal/ComBAopevLwENQdKWiIn.png', |
| | | title: 'Vue', |
| | | description: '生命就像一盒巧克力,结果往往出人意料', |
| | | status: 1, |
| | | updatedAt: '2018-07-26 00:00:00' |
| | | } |
| | | ] |
| | | this.loading = false |
| | | }, |
| | | getActivity () { |
| | | this.activities = [{ |
| | | id: 1, |
| | | user: { |
| | | nickname: '@name', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png' |
| | | }, |
| | | project: { |
| | | name: '白鹭酱油开发组', |
| | | action: '更新', |
| | | event: '番组计划' |
| | | }, |
| | | time: '2018-08-23 14:47:00' |
| | | }, |
| | | { |
| | | id: 1, |
| | | user: { |
| | | nickname: '蓝莓酱', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/jZUIxmJycoymBprLOUbT.png' |
| | | }, |
| | | project: { |
| | | name: '白鹭酱油开发组', |
| | | action: '更新', |
| | | event: '番组计划' |
| | | }, |
| | | time: '2018-08-23 09:35:37' |
| | | }, |
| | | { |
| | | id: 1, |
| | | user: { |
| | | nickname: '@name', |
| | | avatar: '@image(64x64)' |
| | | }, |
| | | project: { |
| | | name: '白鹭酱油开发组', |
| | | action: '创建', |
| | | event: '番组计划' |
| | | }, |
| | | time: '2017-05-27 00:00:00' |
| | | }, |
| | | { |
| | | id: 1, |
| | | user: { |
| | | nickname: '曲丽丽', |
| | | avatar: '@image(64x64)' |
| | | }, |
| | | project: { |
| | | name: '高逼格设计天团', |
| | | action: '更新', |
| | | event: '六月迭代' |
| | | }, |
| | | time: '2018-08-23 14:47:00' |
| | | }, |
| | | { |
| | | id: 1, |
| | | user: { |
| | | nickname: '@name', |
| | | avatar: '@image(64x64)' |
| | | }, |
| | | project: { |
| | | name: '高逼格设计天团', |
| | | action: 'created', |
| | | event: '六月迭代' |
| | | }, |
| | | time: '2018-08-23 14:47:00' |
| | | }, |
| | | { |
| | | id: 1, |
| | | user: { |
| | | nickname: '曲丽丽', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png' |
| | | }, |
| | | project: { |
| | | name: '高逼格设计天团', |
| | | action: 'created', |
| | | event: '六月迭代' |
| | | }, |
| | | time: '2018-08-23 14:47:00' |
| | | } |
| | | ] |
| | | }, |
| | | getTeams () { |
| | | this.teams = [{ |
| | | id: 1, |
| | | name: '科学搬砖组', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png' |
| | | }, |
| | | { |
| | | id: 2, |
| | | name: '程序员日常', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/cnrhVkzwxjPwAaCfPbdc.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '设计天团', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/gaOngJwsRYRaVAuXXcmB.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '中二少女团', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/ubnKSIfAJTxIgXOKlciN.png' |
| | | }, |
| | | { |
| | | id: 1, |
| | | name: '骗你学计算机', |
| | | avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WhxKECPNujWoWEFNdnJE.png' |
| | | } |
| | | ] |
| | | }, |
| | | initRadar () { |
| | | this.radarLoading = true |
| | | const dv = new DataSet.View().source( |
| | | [{ |
| | | item: '引用', |
| | | '个人': 70, |
| | | '团队': 30, |
| | | '部门': 40 |
| | | }, |
| | | { |
| | | item: '口碑', |
| | | '个人': 60, |
| | | '团队': 70, |
| | | '部门': 40 |
| | | }, |
| | | { |
| | | item: '产量', |
| | | '个人': 50, |
| | | '团队': 60, |
| | | '部门': 40 |
| | | }, |
| | | { |
| | | item: '贡献', |
| | | '个人': 40, |
| | | '团队': 50, |
| | | '部门': 40 |
| | | }, |
| | | { |
| | | item: '热度', |
| | | '个人': 60, |
| | | '团队': 70, |
| | | '部门': 40 |
| | | }, |
| | | { |
| | | item: '引用', |
| | | '个人': 70, |
| | | '团队': 50, |
| | | '部门': 40 |
| | | } |
| | | ] |
| | | ) |
| | | dv.transform({ |
| | | type: 'fold', |
| | | fields: ['个人', '团队', '部门'], |
| | | key: 'user', |
| | | value: 'score' |
| | | }) |
| | | |
| | | this.radarData = dv.rows |
| | | this.radarLoading = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | .project-list { |
| | | |
| | | .card-title { |
| | | font-size: 0; |
| | | |
| | | a { |
| | | color: rgba(0, 0, 0, 0.85); |
| | | margin-left: 12px; |
| | | line-height: 24px; |
| | | height: 24px; |
| | | display: inline-block; |
| | | vertical-align: top; |
| | | font-size: 14px; |
| | | |
| | | &:hover { |
| | | color: #1890ff; |
| | | } |
| | | } |
| | | } |
| | | .card-description { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | height: 44px; |
| | | line-height: 22px; |
| | | overflow: hidden; |
| | | } |
| | | .project-item { |
| | | display: flex; |
| | | margin-top: 8px; |
| | | overflow: hidden; |
| | | font-size: 12px; |
| | | height: 20px; |
| | | line-height: 20px; |
| | | a { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | display: inline-block; |
| | | flex: 1 1 0; |
| | | |
| | | &:hover { |
| | | color: #1890ff; |
| | | } |
| | | } |
| | | .datetime { |
| | | color: rgba(0, 0, 0, 0.25); |
| | | flex: 0 0 auto; |
| | | float: right; |
| | | } |
| | | } |
| | | .ant-card-meta-description { |
| | | color: rgba(0, 0, 0, 0.45); |
| | | height: 44px; |
| | | line-height: 22px; |
| | | overflow: hidden; |
| | | } |
| | | } |
| | | |
| | | .item-group { |
| | | padding: 20px 0 8px 24px; |
| | | font-size: 0; |
| | | a { |
| | | color: rgba(0, 0, 0, 0.65); |
| | | display: inline-block; |
| | | font-size: 14px; |
| | | margin-bottom: 13px; |
| | | width: 25%; |
| | | } |
| | | } |
| | | |
| | | .members { |
| | | a { |
| | | display: block; |
| | | margin: 12px 0; |
| | | line-height: 24px; |
| | | height: 24px; |
| | | .member { |
| | | font-size: 14px; |
| | | color: rgba(0, 0, 0, .65); |
| | | line-height: 24px; |
| | | max-width: 100px; |
| | | vertical-align: top; |
| | | margin-left: 12px; |
| | | transition: all 0.3s; |
| | | display: inline-block; |
| | | } |
| | | &:hover { |
| | | span { |
| | | color: #1890ff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .mobile { |
| | | |
| | | .project-list { |
| | | |
| | | .project-card-grid { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | .more-info { |
| | | border: 0; |
| | | padding-top: 16px; |
| | | margin: 16px 0 16px; |
| | | } |
| | | |
| | | .headerContent .title .welcome-text { |
| | | display: none; |
| | | } |
| | | } |
| | | |
| | | </style> |
_web/src/views/system/dict/addForm.vue
_web/src/views/system/dict/dictdata/addForm.vue
_web/src/views/system/dict/dictdata/editForm.vue
_web/src/views/system/dict/dictdata/index.vue
_web/src/views/system/dict/editForm.vue
_web/src/views/system/dict/index.vue
_web/src/views/system/email/index.vue
_web/src/views/system/exception/403.vue
_web/src/views/system/exception/404.vue
_web/src/views/system/exception/500.vue
_web/src/views/system/file/detailForm.vue
_web/src/views/system/file/index.vue
_web/src/views/system/file/previewForm.vue
_web/src/views/system/fileOnline/detailForm.vue
_web/src/views/system/fileOnline/index.vue
_web/src/views/system/fileOnline/onlineEditForm.vue
_web/src/views/system/fileOnline/previewForm.vue
_web/src/views/system/index/welcome.vue
_web/src/views/system/log/oplog/details.vue
_web/src/views/system/log/oplog/index.vue
_web/src/views/system/log/vislog/details.vue
_web/src/views/system/log/vislog/index.vue
_web/src/views/system/machine/index.vue
_web/src/views/system/menu/addForm.vue
_web/src/views/system/menu/editForm.vue
_web/src/views/system/menu/index.vue
_web/src/views/system/notice/addForm.vue
_web/src/views/system/notice/detailForm.vue
_web/src/views/system/notice/editForm.vue
_web/src/views/system/notice/index.vue
_web/src/views/system/noticeReceived/detailForm.vue
_web/src/views/system/noticeReceived/index.vue
_web/src/views/system/onlineUser/index.vue
_web/src/views/system/org/addForm.vue
_web/src/views/system/org/editForm.vue
_web/src/views/system/org/index.vue
_web/src/views/system/pos/addForm.vue
_web/src/views/system/pos/editForm.vue
_web/src/views/system/pos/index.vue
_web/src/views/system/role/addForm.vue
_web/src/views/system/role/editForm.vue
_web/src/views/system/role/index.vue
_web/src/views/system/role/roleMenuForm.vue
_web/src/views/system/role/roleOrgForm.vue
_web/src/views/system/sms/index.vue
_web/src/views/system/timers/addForm.vue
_web/src/views/system/timers/editForm.vue
_web/src/views/system/timers/index.vue
_web/src/views/system/user/addForm.vue
_web/src/views/system/user/editForm.vue
_web/src/views/system/user/index.vue
_web/src/views/system/user/userOrgForm.vue
_web/src/views/system/user/userRoleForm.vue
_web/src/views/userLoginReg/Login.vue
_web/src/views/userLoginReg/Register.vue
_web/src/views/userLoginReg/RegisterResult.vue
_web/tests/unit/.eslintrc.js
_web/vue.config.js
_web/webstorm.config.js
pom.xml
snowy-base/README.md
snowy-base/pom.xml
snowy-base/snowy-core/README.md
snowy-base/snowy-core/pom.xml
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/annotion/BusinessLog.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/annotion/DataScope.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/annotion/ExpEnumType.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/annotion/Permission.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/annotion/Wrapper.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/cache/CacheOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/cache/base/AbstractMemoryCacheOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/cache/base/AbstractRedisCacheOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/AopSortConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/CommonConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/ExpEnumConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/MediaTypeConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/SpringSecurityConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/consts/SymbolConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/constant/ConstantContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/constant/ConstantContextHolder.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/group/RequestGroupContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/group/RequestParamIdContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/login/LoginContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/login/LoginContextHolder.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/param/RequestParamContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/requestno/RequestNoContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/resources/ApiResourceContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/system/SystemContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/context/system/SystemContextHolder.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/cryptogram/keypair.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/dbs/CurrentDataSourceContext.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/email/MailSender.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/email/modular/SimpleMailSender.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/email/modular/exception/MailSendException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/email/modular/model/SendMailParam.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/CommonStatusEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/DbIdEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/DocumentFormatEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/LogAnnotionOpTypeEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/LogicTypeEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/enums/YesOrNotEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/AuthException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/DemoException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/LibreOfficeException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/PermissionException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/RequestMethodException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/ServiceException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/AuthExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/ParamExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/PermissionExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/RequestMethodExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/RequestTypeExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/ServerExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/StatusExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/WrapperExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/exception/enums/abs/AbstractBaseExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/factory/ExpEnumCodeFactory.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/factory/PageFactory.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/factory/TreeBuildFactory.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/FileOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/common/enums/BucketAuthEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/common/exp/FileServiceException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/aliyun/AliyunFileOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/aliyun/exp/AliyunFileServiceException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/aliyun/prop/AliyunOssProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/local/LocalFileOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/local/prop/LocalFileProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/tencent/TenFileOperator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/tencent/exp/TencentFileServiceException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/file/modular/tencent/prop/TenCosProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/base/entity/BaseEntity.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/base/node/BaseTreeNode.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/base/param/BaseParam.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/base/validate/UniqueValidateParam.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/base/wrapper/BaseWrapper.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/cryptogram/CryptogramConfigs.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/druid/DruidProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/email/EmailConfigs.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/login/LoginEmpInfo.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/login/SnowyAuthority.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/login/SysLoginUser.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/node/AntdBaseTreeNode.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/node/CommonBaseTreeNode.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/node/LoginMenuTreeNode.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/oauth/OauthConfigs.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/page/PageResult.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/response/ErrorResponseData.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/response/ResponseData.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/response/SuccessResponseData.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/sms/AliyunSmsConfigs.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/pojo/sms/TencentSmsConfigs.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/SmsSender.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/AliyunSmsSender.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/enums/AliyunSmsResultEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/exp/AliyunSmsException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/msign/MultiSignManager.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/msign/impl/MapBasedMultiSignManager.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/aliyun/prop/AliyunSmsProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/tencent/TencentSmsSender.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/tencent/exp/TencentSmsException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/sms/modular/tencent/prop/TencentSmsProperties.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/consts/TenantConstants.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/consts/TenantExpEnumConstant.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/context/TenantCodeHolder.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/context/TenantDbNameHolder.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/entity/TenantInfo.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/exception/TenantException.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/exception/enums/TenantExceptionEnum.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/params/TenantInfoParam.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/tenant/service/TenantInfoService.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/timer/TimerTaskRunner.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/AopTargetUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/CryptogramUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/HttpServletUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/IpAddressUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/JoinPointUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/LibreOfficeUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/PageUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/PastTimeFormatUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/PoiUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/ResponseUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/util/UaUtil.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/date/DateValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/date/DateValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateordatetime/DateOrDateTimeValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateordatetime/DateOrDateTimeValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateormonth/DateOrMonthValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateormonth/DateOrMonthValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateortime/DateOrTimeValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dateortime/DateOrTimeValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/datetime/DateTimeValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/datetime/DateTimeValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dict/DictValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/dict/DictValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/flag/FlagValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/flag/FlagValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/month/MonthValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/month/MonthValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/mothordatetime/MonthOrDateTimeValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/mothordatetime/MonthOrDateTimeValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/time/TimeValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/time/TimeValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/unique/TableUniqueValue.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/validation/unique/TableUniqueValueValidator.java
snowy-base/snowy-core/src/main/java/vip/xiaonuo/core/web/SnowyRequestResponseBodyMethodProcessor.java
snowy-base/snowy-gen/README.md
snowy-base/snowy-gen/pom.xml
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/consts/GenConstant.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/consts/GenExpEnumConstant.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/context/XnVelocityContext.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/enums/QueryTypeEnum.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/enums/TableFilteredFieldsEnum.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/param/TableField.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/param/XnCodeGenParam.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/tool/JavaEffTool.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/tool/JavaSqlTool.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/tool/NamingConTool.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/tool/StringDateTool.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/core/util/Util.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/controller/CodeGenerateController.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/controller/SysCodeGenerateConfigController.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/entity/CodeGenerate.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/entity/SysCodeGenerateConfig.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/enums/CodeGenerateExceptionEnum.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/enums/SysCodeGenerateConfigExceptionEnum.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/mapper/CodeGenerateMapper.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/mapper/SysCodeGenerateConfigMapper.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/mapper/mapping/CodeGenerateMapper.xml
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/mapper/mapping/SysCodeGenerateConfigMapper.xml
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/param/CodeGenerateParam.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/param/SysCodeGenerateConfigParam.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/result/InforMationColumnsResult.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/result/InformationResult.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/service/CodeGenerateService.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/service/SysCodeGenerateConfigService.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/service/impl/CodeGenerateServiceImpl.java
snowy-base/snowy-gen/src/main/java/vip/xiaonuo/generate/modular/service/impl/SysCodeGenerateConfigServiceImpl.java
snowy-base/snowy-gen/src/main/resources/template/Controller.java.vm
snowy-base/snowy-gen/src/main/resources/template/ExceptionEnum.java.vm
snowy-base/snowy-gen/src/main/resources/template/Manage.js.vm
snowy-base/snowy-gen/src/main/resources/template/Mapper.java.vm
snowy-base/snowy-gen/src/main/resources/template/Mapper.xml.vm
snowy-base/snowy-gen/src/main/resources/template/Param.java.vm
snowy-base/snowy-gen/src/main/resources/template/Service.java.vm
snowy-base/snowy-gen/src/main/resources/template/ServiceImpl.java.vm
snowy-base/snowy-gen/src/main/resources/template/XnMysql.sql.vm
snowy-base/snowy-gen/src/main/resources/template/XnOracle.sql.vm
snowy-base/snowy-gen/src/main/resources/template/addForm.vue.vm
snowy-base/snowy-gen/src/main/resources/template/editForm.vue.vm
snowy-base/snowy-gen/src/main/resources/template/entity.java.vm
snowy-base/snowy-gen/src/main/resources/template/index.vue.vm
snowy-base/snowy-system/README.md
snowy-base/snowy-system/pom.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/AopConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/CacheConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/DataSourceConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/FileConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/MailSenderConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/MybatisConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/SmsSenderConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/SpringSecurityConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/SwaggerConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/config/WebMvcConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/aop/BusinessLogAop.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/aop/DataScopeAop.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/aop/PermissionAop.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/aop/WrapperAop.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/cache/MappingCache.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/cache/OauthCache.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/cache/ResourceCache.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/cache/UserCache.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/consts/SysExpEnumConstant.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/context/SystemContextImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/AdminTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/DataScopeTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/LogSuccessStatusEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/MenuOpenTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/MenuTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/MenuWeightEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/NoticeStatusEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/NoticeUserStatusEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/OauthPlatformEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/OauthSexEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/SexEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/enums/VisLogTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/error/GlobalExceptionHandler.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/error/SnowyErrorAttributes.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/filter/RequestNoFilter.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/filter/security/JwtAuthenticationTokenFilter.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/filter/security/entrypoint/JwtAuthenticationEntryPoint.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/filter/xss/XssFilter.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/filter/xss/XssHttpServletRequestWrapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/jwt/JwtPayLoad.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/jwt/JwtTokenUtil.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/listener/ConstantsInitListener.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/listener/RemoveRequestParamListener.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/listener/ResourceCollectListener.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/listener/TimerTaskRunListener.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/log/LogManager.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/log/factory/LogFactory.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/log/factory/LogTaskFactory.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/mybatis/dbid/SnowyDatabaseIdProvider.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/mybatis/fieldfill/CustomMetaObjectHandler.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/mybatis/sqlfilter/DemoProfileSqlInterceptor.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/provider/CaptchaCacheServiceProvider.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/redis/FastJson2JsonRedisSerializer.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/scanner/ApiResourceScanner.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/core/validator/SnowyValidator.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/IndexController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/controller/SysAppController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/entity/SysApp.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/enums/SysAppExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/mapper/SysAppMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/mapper/mapping/SysAppMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/param/SysAppParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/service/SysAppService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/app/service/impl/SysAppServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/controller/SysAreaController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/entity/SysArea.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/mapper/SysAreaMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/mapper/mapping/SysAreaMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/param/SysAreaParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/service/SysAreaService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/area/service/impl/SysAreaServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/auth/context/LoginContextSpringSecurityImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/auth/controller/SysLoginController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/auth/factory/LoginUserFactory.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/auth/service/AuthService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/auth/service/impl/AuthServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/controller/SysConfigController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/entity/SysConfig.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/enums/SysConfigExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/mapper/SysConfigMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/mapper/mapping/SysConfigMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/param/SysConfigParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/service/SysConfigService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/consts/service/impl/SysConfigServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/controller/SysDictDataController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/controller/SysDictTypeController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/entity/SysDictData.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/entity/SysDictType.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/enums/SysDictDataExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/enums/SysDictTypeExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/mapper/SysDictDataMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/mapper/SysDictTypeMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/mapper/mapping/SysDictDataMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/mapper/mapping/SysDictTypeMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/param/SysDictDataParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/param/SysDictTypeParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/result/SysDictTreeNode.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/service/SysDictDataService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/service/SysDictTypeService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/service/impl/SysDictDataServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/dict/service/impl/SysDictTypeServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/email/controler/EmailController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/email/enums/SysEmailExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/entity/SysEmp.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/entity/SysEmpExtOrgPos.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/entity/SysEmpPos.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/SysEmpExtOrgPosMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/SysEmpMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/SysEmpPosMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/mapping/SysEmpExtOrgPosMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/mapping/SysEmpMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/mapper/mapping/SysEmpPosMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/param/SysEmpParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/result/SysEmpInfo.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/SysEmpExtOrgPosService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/SysEmpPosService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/SysEmpService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/impl/SysEmpExtOrgPosPosServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/impl/SysEmpPosServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/emp/service/impl/SysEmpServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/controller/SysFileInfoController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/entity/SysFileInfo.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/enums/SysFileInfoExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/enums/SysFileLocationEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/mapper/SysFileInfoMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/mapper/mapping/SysFileInfoMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/param/SysFileInfoParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/result/SysFileInfoResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/result/SysOnlineFileInfoResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/service/SysFileInfoService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/service/impl/SysFileInfoServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/util/DownloadUtil.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/file/util/OnlineDocumentUtil.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/controller/SysLogController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/entity/SysOpLog.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/entity/SysVisLog.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/mapper/SysOpLogMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/mapper/SysVisLogMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/mapper/mapping/SysOpLogMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/mapper/mapping/SysVisLogMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/param/SysOpLogParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/param/SysVisLogParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/service/SysOpLogService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/service/SysVisLogService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/service/impl/SysOpLogServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/log/service/impl/SysVisLogServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/controller/SysMenuController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/entity/SysMenu.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/enums/SysMenuExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/mapper/SysMenuMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/mapper/mapping/SysMenuMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/node/MenuBaseTreeNode.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/param/SysMenuParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/service/SysMenuService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/menu/service/impl/SysMenuServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/controller/SysMachineController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/controller/SysOnlineUserController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/param/SysOnlineUserParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/result/SysMachineResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/result/SysOnlineUserResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/service/SysMachineService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/service/SysOnlineUserService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/service/impl/SysMachineServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/monitor/service/impl/SysOnlineUserServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/controller/SysNoticeController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/entity/SysNotice.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/entity/SysNoticeUser.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/enums/SysNoticeExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/mapper/SysNoticeMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/mapper/SysNoticeUserMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/mapper/mapping/SysNoticeMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/mapper/mapping/SysNoticeUserMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/param/SysNoticeParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/result/SysNoticeDetailResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/result/SysNoticeReceiveResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/service/SysNoticeService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/service/SysNoticeUserService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/service/impl/SysNoticeServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/notice/service/impl/SysNoticeUserServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/controller/SysOauthController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/entity/SysOauthUser.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/enums/SysOauthExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/mapper/SysOauthMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/mapper/mapping/SysOauthMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/service/SysOauthService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/oauth/service/impl/SysOauthServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/controller/SysOrgController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/entity/SysOrg.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/enums/SysOrgExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/mapper/SysOrgMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/mapper/mapping/SysOrgMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/param/SysOrgParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/service/SysOrgService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/org/service/impl/SysOrgServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/controller/SysPosController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/entity/SysPos.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/enums/SysPosExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/mapper/SysPosMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/mapper/mapping/SysPosMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/param/SysPosParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/service/SysPosService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/pos/service/impl/SysPosServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/controller/SysRoleController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/entity/SysRole.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/entity/SysRoleDataScope.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/entity/SysRoleMenu.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/enums/SysRoleExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/SysRoleDataScopeMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/SysRoleMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/SysRoleMenuMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/mapping/SysRoleDataScopeMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/mapping/SysRoleMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/mapper/mapping/SysRoleMenuMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/param/SysRoleParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleDataScopeService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleMenuService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/SysRoleService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleDataScopeServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleMenuServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/role/service/impl/SysRoleServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/controller/SmsSenderController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/entity/SysSms.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/enums/SmsSendExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/enums/SmsSendSourceEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/enums/SmsSendStatusEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/enums/SmsTypeEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/enums/SmsVerifyEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/mapper/SysSmsMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/mapper/mapping/SysSmsMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/param/SysSmsInfoParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/param/SysSmsSendParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/param/SysSmsVerifyParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/service/SmsSenderService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/service/SysSmsInfoService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/service/impl/SmsSenderServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/sms/service/impl/SysSmsInfoServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/controller/SysTimersController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/entity/SysTimers.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/enums/TimerJobStatusEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/enums/exp/SysTimersExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/mapper/SysTimersMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/mapper/mapping/SysTimersMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/param/SysTimersParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/service/SysTimersService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/service/TimerExeService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/service/impl/HutoolTimerExeServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/service/impl/SysTimersServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/tasks/RefreshConstantsTaskRunner.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/timer/tasks/SystemOutTaskRunner.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/controller/SysUserController.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/entity/SysUser.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/entity/SysUserDataScope.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/entity/SysUserRole.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/enums/SysUserExceptionEnum.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/factory/SysUserFactory.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/SysUserDataScopeMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/SysUserMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/SysUserRoleMapper.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/mapping/SysUserDataScopeMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/mapping/SysUserMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/mapper/mapping/SysUserRoleMapper.xml
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/param/SysUserParam.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/result/SysUserResult.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserDataScopeService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserRoleService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserService.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserDataScopeServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserRoleServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java
snowy-base/snowy-system/src/main/java/vip/xiaonuo/sys/modular/user/wrapper/SysUserWrapper.java
snowy-base/snowy-system/src/main/resources/META-INF/spring.factories
snowy-main/README.md
snowy-main/pom.xml
snowy-main/src/main/docker/docker-assembly.xml
snowy-main/src/main/java/vip/xiaonuo/SnowyApplication.java
snowy-main/src/main/java/vip/xiaonuo/SnowyServletInitializer.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/controller/BlogArticleController.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/entity/BlogArticle.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/enums/BlogArticleExceptionEnum.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/mapper/BlogArticleMapper.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/mapper/mapping/BlogArticleMapper.xml
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/param/BlogArticleParam.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/service/BlogArticleService.java
snowy-main/src/main/java/vip/xiaonuo/modular/blogarticle/service/impl/BlogArticleServiceImpl.java
snowy-main/src/main/java/vip/xiaonuo/modular/controller/DatasourceExampleController.java
snowy-main/src/main/java/vip/xiaonuo/modular/model/AbModel.java
snowy-main/src/main/java/vip/xiaonuo/modular/service/DatasourceExampleService.java
snowy-main/src/main/resources/application-dev.yml
snowy-main/src/main/resources/application-local.yml
snowy-main/src/main/resources/application-prod.yml
snowy-main/src/main/resources/application.yml
snowy-main/src/main/resources/banner.txt
snowy-main/src/main/resources/logback-spring.xml
snowy-main/src/test/java/vip/xiaonuo/core/BaseJunit.java
snowy-main/src/test/java/vip/xiaonuo/core/Test.java
snowy-main/src/test/java/vip/xiaonuo/core/Test2.java
snowy-main/src/test/sql/test.sql |