瀏覽代碼

初始提交

LLL 9 月之前
當前提交
8ece85a293
共有 100 個文件被更改,包括 41751 次插入0 次删除
  1. 23 0
      .dockerignore
  2. 39 0
      .editorconfig
  3. 6 0
      .env
  4. 7 0
      .env.development
  5. 4 0
      .env.production
  6. 4 0
      .env.test
  7. 1 0
      .eslintignore
  8. 1 0
      .gitattributes
  9. 16 0
      .github/ISSUE_TEMPLATE.md
  10. 17 0
      .gitignore
  11. 5 0
      .prettierrc
  12. 29 0
      Dockerfile
  13. 213 0
      LICENSE
  14. 238 0
      README.md
  15. 6 0
      babel.config.js
  16. 24 0
      idea.config.js
  17. 18282 0
      package-lock.json
  18. 117 0
      package.json
  19. 二進制
      public/avatar2.jpg
  20. 6953 0
      public/cdn/babel-polyfill/polyfill_7_2_5.js
  21. 1 0
      public/cdn/font-icon/font_2316098_umqusozousr.js
  22. 7720 0
      public/color.less
  23. 二進制
      public/goright.png
  24. 269 0
      public/index.html
  25. 二進制
      public/logo.png
  26. 11 0
      public/static/config.js
  27. 261 0
      public/tinymce/langs/zh_CN.js
  28. 554 0
      public/tinymce/skins/lightgray/content.min.css
  29. 2159 0
      public/tinymce/skins/lightgray/skin.min.css
  30. 1 0
      public/v2.js
  31. 44 0
      src/App.vue
  32. 30 0
      src/api/GroupRequest.js
  33. 161 0
      src/api/api.js
  34. 10 0
      src/api/index.js
  35. 87 0
      src/api/login.js
  36. 213 0
      src/api/manage.js
  37. 69 0
      src/assets/background.svg
  38. 二進制
      src/assets/checkcode.png
  39. 二進制
      src/assets/daiban.png
  40. 二進制
      src/assets/duban.png
  41. 二進制
      src/assets/guaz.png
  42. 33 0
      src/assets/less/AppIcon.less
  43. 259 0
      src/assets/less/JAreaLinkage.less
  44. 15 0
      src/assets/less/TableExpand.less
  45. 1 0
      src/assets/less/codemirror_idea.css
  46. 61 0
      src/assets/less/common.less
  47. 28 0
      src/assets/less/index.less
  48. 二進制
      src/assets/logo-white.png
  49. 二進制
      src/assets/logo.png
  50. 172 0
      src/assets/logo.svg
  51. 二進制
      src/assets/nodata.png
  52. 二進制
      src/assets/pdf4.jpg
  53. 二進制
      src/assets/zaiban.png
  54. 77 0
      src/cas/sso.js
  55. 46 0
      src/components/AvatarList/Item.vue
  56. 100 0
      src/components/AvatarList/List.vue
  57. 4 0
      src/components/AvatarList/index.js
  58. 60 0
      src/components/AvatarList/index.less
  59. 111 0
      src/components/ChartCard.vue
  60. 103 0
      src/components/CountDown/CountDown.vue
  61. 3 0
      src/components/CountDown/index.js
  62. 49 0
      src/components/Ellipsis/Ellipsis.vue
  63. 3 0
      src/components/Ellipsis/index.js
  64. 228 0
      src/components/JVxeCells/JVxeFileCell.vue
  65. 242 0
      src/components/JVxeCells/JVxeImageCell.vue
  66. 67 0
      src/components/JVxeCells/JVxePopupCell.vue
  67. 60 0
      src/components/JVxeCells/JVxeRadioCell.vue
  68. 262 0
      src/components/JVxeCells/JVxeSelectDictSearchCell.js
  69. 36 0
      src/components/JVxeCells/install.js
  70. 54 0
      src/components/NumberInfo/NumberInfo.vue
  71. 3 0
      src/components/NumberInfo/index.js
  72. 55 0
      src/components/NumberInfo/index.less
  73. 43 0
      src/components/README.md
  74. 41 0
      src/components/Trend/Trend.vue
  75. 3 0
      src/components/Trend/index.js
  76. 42 0
      src/components/Trend/index.less
  77. 85 0
      src/components/_util/Area.js
  78. 46 0
      src/components/_util/StringUtil.js
  79. 12 0
      src/components/_util/util.js
  80. 88 0
      src/components/chart/AreaChartTy.vue
  81. 50 0
      src/components/chart/Bar.vue
  82. 60 0
      src/components/chart/BarAndLine.vue
  83. 96 0
      src/components/chart/BarMultid.vue
  84. 187 0
      src/components/chart/DashChartDemo.vue
  85. 61 0
      src/components/chart/IndexBar.vue
  86. 95 0
      src/components/chart/LineChartMultid.vue
  87. 80 0
      src/components/chart/Liquid.vue
  88. 69 0
      src/components/chart/MiniArea.vue
  89. 76 0
      src/components/chart/MiniBar.vue
  90. 75 0
      src/components/chart/MiniProgress.vue
  91. 70 0
      src/components/chart/Pie.vue
  92. 367 0
      src/components/chart/README.md
  93. 90 0
      src/components/chart/Radar.vue
  94. 81 0
      src/components/chart/RankList.vue
  95. 54 0
      src/components/chart/StackBar.vue
  96. 66 0
      src/components/chart/TransferBar.vue
  97. 84 0
      src/components/chart/Trend.vue
  98. 13 0
      src/components/chart/chart.less
  99. 10 0
      src/components/chart/mixins/ChartMixins.js
  100. 0 0
      src/components/dict/JDictSelectTag.vue

+ 23 - 0
.dockerignore

@@ -0,0 +1,23 @@
+docs
+public
+src
+.dockerignore
+.editorconfig
+.eslintignore
+.gitattributes
+.gitignore
+.prettierrc
+babel.config.js
+Dockerfile
+idea.config.js
+LICENSE
+package.json
+package-lock.json
+README.md
+vue.config.js
+yarn
+yarn.lock
+yarn-error.log
+.idea
+.svn
+node_modules

+ 39 - 0
.editorconfig

@@ -0,0 +1,39 @@
+[*]
+charset=utf-8
+end_of_line=crlf
+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
+

+ 6 - 0
.env

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

+ 7 - 0
.env.development

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

+ 4 - 0
.env.production

@@ -0,0 +1,4 @@
+NODE_ENV=production
+VUE_APP_API_BASE_URL=http://localhost:8520/jeecg-boot
+VUE_APP_CAS_BASE_URL=http://localhost:8888/cas
+VUE_APP_ONLINE_BASE_URL=http://fileview.jeecg.com/onlinePreview

+ 4 - 0
.env.test

@@ -0,0 +1,4 @@
+NODE_ENV=production
+VUE_APP_API_BASE_URL=http://boot.jeecg.com:8520/jeecg-boot
+VUE_APP_CAS_BASE_URL=http://cas.example.org:8443/cas
+VUE_APP_ONLINE_BASE_URL=http://fileview.jeecg.com/onlinePreview

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+/src

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+public/* linguist-vendored

+ 16 - 0
.github/ISSUE_TEMPLATE.md

@@ -0,0 +1,16 @@
+##### 版本号:
+
+
+##### 问题描述:
+
+
+##### 截图&代码:
+
+
+
+
+#### 友情提示(为了提高issue处理效率):
+  - 未按格式要求发帖,会被直接删掉;
+  - 请自己初判问题描述是否清楚,是否方便我们调查处理;
+  - 描述过于简单或模糊,导致无法处理的,会被直接删掉;
+  

+ 17 - 0
.gitignore

@@ -0,0 +1,17 @@
+node_modules
+.DS_Store
+dist
+.npmrc
+.cache
+
+## ide
+**/.idea
+*.iml
+
+## backend
+**/target
+**/logs
+**/dist
+
+## front
+/yarn-error.log

+ 5 - 0
.prettierrc

@@ -0,0 +1,5 @@
+{
+  "printWidth": 120,
+  "semi": false,
+  "singleQuote": true
+}

+ 29 - 0
Dockerfile

@@ -0,0 +1,29 @@
+FROM nginx
+MAINTAINER jeecgos@163.com
+VOLUME /tmp
+ENV LANG en_US.UTF-8
+RUN echo "server {  \
+                      listen       80; \
+                      location ^~ /jeecg-boot { \
+                      proxy_pass              http://jeecg-boot-system:8080/jeecg-boot/; \
+                      proxy_set_header        Host jeecg-boot-system; \
+                      proxy_set_header        X-Real-IP \$remote_addr; \
+                      proxy_set_header        X-Forwarded-For \$proxy_add_x_forwarded_for; \
+                  } \
+                  #解决Router(mode: 'history')模式下,刷新路由地址不能找到页面的问题 \
+                  location / { \
+                     root   /var/www/html/; \
+                      index  index.html index.htm; \
+                      if (!-e \$request_filename) { \
+                          rewrite ^(.*)\$ /index.html?s=\$1 last; \
+                          break; \
+                      } \
+                  } \
+                  access_log  /var/log/nginx/access.log ; \
+              } " > /etc/nginx/conf.d/default.conf \
+    &&  mkdir  -p  /var/www \
+    &&  mkdir -p /var/www/html
+
+ADD dist/ /var/www/html/
+EXPOSE 80
+EXPOSE 443

+ 213 - 0
LICENSE

@@ -0,0 +1,213 @@
+    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:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright (c) 2019 <a href="http://www.jeecg.com">Jeecg Boot</a> All rights reserved.
+ 
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   
+   In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
+
+  开源协议补充
+    JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京,地址:中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱:jeecgos@163.com
+    本软件受适用的国家软件著作权法(包括国际条约)和双重保护许可。
+  
+   1.允许基于本平台软件开展业务系统开发。
+   2.不得基于该平台软件的基础,修改包装成一个与JeecgBoot平台软件功能类似的产品进行发布、销售,或与JeecgBoot参与同类软件产品市场的竞争。
+	 违反此条款属于侵权行为,须赔偿侵权经济损失,同时立即停止著作权侵权行为。
+	 解释权归:http://www.jeecg.com
+	 

+ 238 - 0
README.md

@@ -0,0 +1,238 @@
+Ant Design Jeecg Vue(JeecgBoot 低代码平台)
+====
+
+当前最新版本: 3.4.3(发布日期:20221107)
+
+[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
+[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://www.jeecg.com)
+[![](https://img.shields.io/badge/Blog-官方博客-blue.svg)](https://jeecg.blog.csdn.net)
+[![](https://img.shields.io/badge/version-3.4.3-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
+[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
+[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
+
+
+
+Overview
+----
+
+基于 [Ant Design of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 实现的 Ant Design Pro  Vue 版
+Jeecg-boot 的前端UI框架,采用前后端分离方案,提供强大代码生成器的低代码平台。前端页面代码和后端功能代码一键生成,不需要写任何代码,保持jeecg一贯的强大!!
+ 
+> 强大的代码生成器让前后端代码一键生成! JeecgBoot引领低代码开发模式(OnlineCoding-> 代码生成-> 手工MERGE), 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省成本,同时又不失灵活性
+
+
+## 项目介绍 
+ 重要说明:JeecgBoot前端提供两套解决方案,一套VUE2和一套VUE3版本,目前vue2版本最新代码只支持到jeecgboot `3.4.3版本`,一定注意。
+
+
+## Vue2与Vue3版本区别
+> - VUE3版本彻底抛弃IE兼容,不兼容IE和低版本浏览器,只适配高版本谷歌和Edge
+ (政府、事业类单位项目需要谨慎选择——国产化迁移是一个漫长的过程,万一过程中要求IE兼容,这个不可逆)
+> - 所以如果对浏览器有要求的项目,请选择VUE2版本。
+> - VUE3版是全新的技术栈,紧跟主流(前端重写),各个功能都做了优化,拥有更好的体验效果
+
+
+
+
+##  项目源码
+
+| 仓库  | 前端源码Vue2版 | 后端源码                                                                     |
+|-|-|--------------------------------------------------------------------------|
+| Github   | [ant-design-vue-jeecg](https://github.com/jeecgboot/ant-design-vue-jeecg) | [jeecg-boot (v3.4.3)](https://gitee.com/jeecg/jeecg-boot/tree/v3.4.3last)      |
+| 码云  | [ant-design-vue-jeecg](https://gitee.com/jeecg/ant-design-vue-jeecg)  | [jeecg-boot (v3.4.3)](https://github.com/jeecgboot/jeecg-boot/tree/v3.4.3) |
+> 目前vue2版本最新代码只支持到jeecgboot 3.4.3版本,一定注意。
+
+## 项目说明
+
+| 项目名                | 说明                     | 
+|--------------------|------------------------|
+| `jeecg-boot`    | JAVA后台(支持微服务)        | 
+| `ant-design-vue-jeecg`  |Vue2版前端代码   |   
+
+
+## 技术支持
+
+
+本项目关闭issue,使用中遇到问题或者BUG可以在 [JeecgBoot主项目上提Issues](https://github.com/jeecgboot/jeecg-boot/issues/new)
+
+官方支持: http://jeecg.com/doc/help
+
+技术文档: http://doc.jeecg.com
+
+
+
+
+## 前端技术栈
+ 
+  > 此处是Vue2版的技术栈介绍
+
+- 基础框架:[ant-design-vue](https://github.com/vueComponent/ant-design-vue) - Ant Design Of Vue 实现
+- JavaScript框架:Vue
+- node
+- yarn
+- @vue/cli 3.2.1
+- [vue-cropper](https://github.com/xyxiao001/vue-cropper) - 头像裁剪组件
+- [@antv/g2](https://antv.alipay.com/zh-cn/index.html) - Alipay AntV 数据可视化图表
+- [Viser-vue](https://viserjs.github.io/docs.html#/viser/guide/installation)  - antv/g2 封装实现
+- [Vue 2.6.10](https://cn.vuejs.org/),[Vuex](https://vuex.vuejs.org/zh/),[Vue Router](https://router.vuejs.org/zh/)
+- [Axios](https://github.com/axios/axios)
+- [webpack](https://www.webpackjs.com/),[yarn](https://yarnpkg.com/zh-Hans/)
+- eslint,[@vue/cli 3.2.1](https://cli.vuejs.org/zh/guide)
+- vue-print-nb-jeecg - 打印
+
+
+
+
+
+## 项目下载和运行
+
+
+- 拉取项目代码
+```bash
+git clone https://github.com/zhangdaiscott/jeecg-boot.git
+cd  jeecg-boot/ant-design-vue-jeecg
+```
+
+- 安装依赖
+```
+yarn install
+```
+
+- 开发模式运行
+```
+yarn run serve
+```
+
+- 编译项目
+```
+yarn run build
+```
+
+- Lints and fixes files
+```
+yarn run lint
+```
+
+Docker镜像启动前端(单体模式)
+----
+
+ ``` 
+# 1.配置host
+
+    127.0.0.1   jeecg-boot-system
+
+# 2.修改前端项目的后台域名
+    .env.development
+    域名改成: http://jeecg-boot-system:8080/jeecg-boot
+   
+# 3.进入项目根目录,执行打包命令
+  yarn run build
+
+# 4.构建镜像
+  docker build -t jeecgboot-ui2 .
+
+# 5.启动镜像
+  docker run --name jeecgboot-ui-vue2 -p 80:80 -d jeecgboot-ui2
+
+# 6.访问前台项目
+  http://localhost
+``` 
+
+
+
+其他说明
+----
+
+- 项目使用的 [vue-cli3](https://cli.vuejs.org/guide/), 请更新您的 cli
+
+- 关闭 Eslint (不推荐) 移除 `package.json` 中 `eslintConfig` 整个节点代码
+
+- 修改 Ant Design 配色,在文件 `vue.config.js` 中,其他 less 变量覆盖参考 [ant design](https://ant.design/docs/react/customize-theme-cn) 官方说明
+```ecmascript 6
+  css: {
+    loaderOptions: {
+      less: {
+        modifyVars: {
+          /* less 变量覆盖,用于自定义 ant design 主题 */
+
+          'primary-color': '#F5222D',
+          'link-color': '#F5222D',
+          'border-radius-base': '4px',
+        },
+        javascriptEnabled: true,
+      }
+    }
+  }
+```
+
+
+
+附属文档
+----
+- [Ant Design Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn)
+
+- [报表 viser-vue](https://viserjs.github.io/demo.html#/viser/bar/basic-bar)
+
+- [Vue](https://cn.vuejs.org/v2/guide)
+
+- [路由/菜单说明](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/router/README.md)
+
+- [ANTD 默认配置项](https://github.com/zhangdaiscott/jeecg-boot/tree/master/ant-design-vue-jeecg/src/defaultSettings.js)
+
+- 其他待补充...
+
+
+备注
+----
+
+> @vue/cli 升级后,eslint 规则更新了。由于影响到全部 .vue 文件,需要逐个验证。既暂时关闭部分原本不验证的规则,后期维护时,在逐步修正这些 rules
+
+
+
+系统效果
+----
+##### 大屏模板
+![输入图片说明](https://static.oschina.net/uploads/img/201912/25133248_Ag1C.jpg "在这里输入图片标题")
+
+![输入图片说明](https://static.oschina.net/uploads/img/201912/25133301_k9Kc.jpg "在这里输入图片标题")
+
+##### PC端
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160657_cHwb.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14161004_bxQ4.png "在这里输入图片标题")
+
+
+##### 在线接口文档
+![输入图片说明](https://static.oschina.net/uploads/img/201908/27095258_M2Xq.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题")
+
+
+##### 报表
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160828_pkFr.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160834_Lo23.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160842_QK7B.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160849_GBm5.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160858_6RAM.png "在这里输入图片标题")
+
+##### 流程
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160623_8fwk.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
+![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
+
+
+##### 手机端
+![](https://oscimg.oschina.net/oscnet/da543c5d0d57baab0cecaa4670c8b68c521.jpg)
+![](https://oscimg.oschina.net/oscnet/fda4bd82cab9d682de1c1fbf2060bf14fa6.jpg)
+
+##### PAD端
+![](https://oscimg.oschina.net/oscnet/e90fef970a8c33790ab03ffd6c4c7cec225.jpg)
+![](https://oscimg.oschina.net/oscnet/d78218803a9e856a0aa82b45efc49849a0c.jpg)
+![](https://oscimg.oschina.net/oscnet/0404054d9a12647ef6f82cf9cfb80a5ac02.jpg)
+![](https://oscimg.oschina.net/oscnet/59c23b230f52384e588ee16309b44fa20de.jpg)
+
+
+
+

+ 6 - 0
babel.config.js

@@ -0,0 +1,6 @@
+module.exports = {
+  presets: [
+    ['@vue/app',
+     { useBuiltIns: 'entry' }]
+  ]
+}

+ 24 - 0
idea.config.js

@@ -0,0 +1,24 @@
+'use strict'
+const path = require('path')
+
+function resolve (dir) {
+  return path.join(__dirname, '.', dir)
+}
+
+module.exports = {
+    context: path.resolve(__dirname, './'),
+    resolve: {
+        extensions: ['.js', '.vue', '.json'],
+        alias: {
+            'config': resolve('config'),
+            '@': resolve('src'),
+            '@views': resolve('src/views'),
+            '@comp': resolve('src/components'),
+            '@core': resolve('src/core'),
+            '@utils': resolve('src/utils'),
+            '@entry': resolve('src/entry'),
+            '@router': resolve('src/router'),
+            '@store': resolve('src/store')
+        }
+    },
+}

File diff suppressed because it is too large
+ 18282 - 0
package-lock.json


+ 117 - 0
package.json

@@ -0,0 +1,117 @@
+{
+  "name": "vue-antd-jeecg",
+  "version": "3.4.3",
+  "private": true,
+  "scripts": {
+    "pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
+    "serve": "vue-cli-service serve",
+    "build:test": "vue-cli-service build --mode test",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@jeecg/antd-online-mini": "3.4.3-beta2",
+    "ant-design-vue": "^1.7.2",
+    "@antv/data-set": "^0.11.4",
+    "viser-vue": "^2.4.8",
+    "axios": "^0.18.0",
+    "dayjs": "^1.8.0",
+    "enquire.js": "^2.1.6",
+    "js-cookie": "^2.2.0",
+    "lodash.get": "^4.4.2",
+    "lodash.pick": "^4.4.0",
+    "md5": "^2.2.1",
+    "nprogress": "^0.2.0",
+    "vue": "^2.6.10",
+    "vue-cropper": "^0.5.4",
+    "vue-i18n": "^8.7.0",
+    "vue-loader": "^15.7.0",
+    "vue-ls": "^3.2.0",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.1.0",
+    "vue-print-nb-jeecg": "^1.0.12",
+    "clipboard": "^2.0.4",
+    "vue-photo-preview": "^1.1.3",
+    "vue-splitpane": "^1.0.4",
+    "vuedraggable": "^2.20.0",
+    "codemirror": "^5.46.0",
+    "@tinymce/tinymce-vue": "2.1.0",
+    "tinymce": "5.4.1",
+    "@toast-ui/editor": "^2.1.2",
+    "vue-area-linkage": "^5.1.0",
+    "china-area-data": "^5.0.1",
+    "dom-align": "1.12.0",
+    "xe-utils": "2.4.8",
+    "vxe-table": "2.9.13",
+    "vxe-table-plugin-antd": "1.8.10",
+    "cron-parser": "^2.10.0",
+    "qiankun": "^2.5.1",
+    "xss": "^1.0.13"
+  },
+  "devDependencies": {
+    "@babel/polyfill": "^7.2.5",
+    "@vue/cli-plugin-babel": "^3.3.0",
+    "@vue/cli-plugin-eslint": "^3.3.0",
+    "@vue/cli-service": "^3.3.0",
+    "@vue/eslint-config-standard": "^4.0.0",
+    "babel-eslint": "7.2.3",
+    "eslint": "^5.16.0",
+    "eslint-plugin-vue": "^5.1.0",
+    "less": "^3.9.0",
+    "less-loader": "^4.1.0",
+    "vue-template-compiler": "^2.6.10",
+    "html-webpack-plugin": "^4.2.0",
+    "compression-webpack-plugin": "^3.1.0"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/strongly-recommended",
+      "@vue/standard"
+    ],
+    "parserOptions": {
+      "parser": "babel-eslint"
+    },
+    "rules": {
+      "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,
+      "indent": ["off", 2],
+      "no-console": 0,
+      "space-before-function-paren": 0
+    }
+  },
+  "postcss": {
+    "plugins": {
+      "autoprefixer": {}
+    }
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 10"
+  ]
+}

二進制
public/avatar2.jpg


File diff suppressed because it is too large
+ 6953 - 0
public/cdn/babel-polyfill/polyfill_7_2_5.js


File diff suppressed because it is too large
+ 1 - 0
public/cdn/font-icon/font_2316098_umqusozousr.js


File diff suppressed because it is too large
+ 7720 - 0
public/color.less


二進制
public/goright.png


+ 269 - 0
public/index.html

@@ -0,0 +1,269 @@
+<!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">
+  <title>JeecgBoot 企业级低代码平台</title>
+  <link rel="icon" href="<%= BASE_URL %>logo.png">
+  <script src="<%= BASE_URL %>cdn/babel-polyfill/polyfill_7_2_5.js"></script>
+  <style>
+    html,
+    body,
+    #app {
+      height: 100%;
+      margin: 0px;
+      padding: 0px;
+    }
+    .chromeframe {
+      margin: 0.2em 0;
+      background: #ccc;
+      color: #000;
+      padding: 0.2em 0;
+    }
+    #loader-wrapper {
+      position: fixed;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 100%;
+      z-index: 999999;
+    }
+    #loader {
+      display: block;
+      position: relative;
+      left: 50%;
+      top: 50%;
+      width: 120px;
+      height: 120px;
+      margin: -75px 0 0 -75px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      /* COLOR 1 */
+      border-top-color: #FFF;
+      -webkit-animation: spin 2s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -ms-animation: spin 2s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -moz-animation: spin 2s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -o-animation: spin 2s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      animation: spin 2s linear infinite;
+      /* Chrome, Firefox 16+, IE 10+, Opera */
+      z-index: 1001;
+    }
+    #loader:before {
+      content: "";
+      position: absolute;
+      top: 5px;
+      left: 5px;
+      right: 5px;
+      bottom: 5px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      /* COLOR 2 */
+      border-top-color: #FFF;
+      -webkit-animation: spin 3s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -moz-animation: spin 3s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -o-animation: spin 3s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -ms-animation: spin 3s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      animation: spin 3s linear infinite;
+      /* Chrome, Firefox 16+, IE 10+, Opera */
+    }
+    #loader:after {
+      content: "";
+      position: absolute;
+      top: 15px;
+      left: 15px;
+      right: 15px;
+      bottom: 15px;
+      border-radius: 50%;
+      border: 3px solid transparent;
+      border-top-color: #FFF;
+      /* COLOR 3 */
+      -moz-animation: spin 1.5s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -o-animation: spin 1.5s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -ms-animation: spin 1.5s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      -webkit-animation: spin 1.5s linear infinite;
+      /* Chrome, Opera 15+, Safari 5+ */
+      animation: spin 1.5s linear infinite;
+      /* Chrome, Firefox 16+, IE 10+, Opera */
+    }
+    @-webkit-keyframes spin {
+      0% {
+        -webkit-transform: rotate(0deg);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: rotate(0deg);
+        /* IE 9 */
+        transform: rotate(0deg);
+        /* Firefox 16+, IE 10+, Opera */
+      }
+      100% {
+        -webkit-transform: rotate(360deg);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: rotate(360deg);
+        /* IE 9 */
+        transform: rotate(360deg);
+        /* Firefox 16+, IE 10+, Opera */
+      }
+    }
+    @keyframes spin {
+      0% {
+        -webkit-transform: rotate(0deg);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: rotate(0deg);
+        /* IE 9 */
+        transform: rotate(0deg);
+        /* Firefox 16+, IE 10+, Opera */
+      }
+      100% {
+        -webkit-transform: rotate(360deg);
+        /* Chrome, Opera 15+, Safari 3.1+ */
+        -ms-transform: rotate(360deg);
+        /* IE 9 */
+        transform: rotate(360deg);
+        /* Firefox 16+, IE 10+, Opera */
+      }
+    }
+    #loader-wrapper .loader-section {
+      position: fixed;
+      top: 0;
+      width: 51%;
+      height: 100%;
+      background: #49a9ee;
+      /* Old browsers */
+      z-index: 1000;
+      -webkit-transform: translateX(0);
+      /* Chrome, Opera 15+, Safari 3.1+ */
+      -ms-transform: translateX(0);
+      /* IE 9 */
+      transform: translateX(0);
+      /* Firefox 16+, IE 10+, Opera */
+    }
+    #loader-wrapper .loader-section.section-left {
+      left: 0;
+    }
+    #loader-wrapper .loader-section.section-right {
+      right: 0;
+    }
+    /* Loaded */
+    .loaded #loader-wrapper .loader-section.section-left {
+      -webkit-transform: translateX(-100%);
+      /* Chrome, Opera 15+, Safari 3.1+ */
+      -ms-transform: translateX(-100%);
+      /* IE 9 */
+      transform: translateX(-100%);
+      /* Firefox 16+, IE 10+, Opera */
+      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    }
+    .loaded #loader-wrapper .loader-section.section-right {
+      -webkit-transform: translateX(100%);
+      /* Chrome, Opera 15+, Safari 3.1+ */
+      -ms-transform: translateX(100%);
+      /* IE 9 */
+      transform: translateX(100%);
+      /* Firefox 16+, IE 10+, Opera */
+      -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+      transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+    }
+    .loaded #loader {
+      opacity: 0;
+      -webkit-transition: all 0.3s ease-out;
+      transition: all 0.3s ease-out;
+    }
+    .loaded #loader-wrapper {
+      visibility: hidden;
+      -webkit-transform: translateY(-100%);
+      /* Chrome, Opera 15+, Safari 3.1+ */
+      -ms-transform: translateY(-100%);
+      /* IE 9 */
+      transform: translateY(-100%);
+      /* Firefox 16+, IE 10+, Opera */
+      -webkit-transition: all 0.3s 1s ease-out;
+      transition: all 0.3s 1s ease-out;
+    }
+    /* JavaScript Turned Off */
+    .no-js #loader-wrapper {
+      display: none;
+    }
+    .no-js h1 {
+      color: #222222;
+    }
+    #loader-wrapper .load_title {
+      font-family: 'Open Sans';
+      color: #FFF;
+      font-size: 14px;
+      width: 100%;
+      text-align: center;
+      z-index: 9999999999999;
+      position: absolute;
+      top: 60%;
+      opacity: 1;
+      line-height: 30px;
+    }
+    #loader-wrapper .load_title span {
+      font-weight: normal;
+      font-style: italic;
+      font-size: 14px;
+      color: #FFF;
+      opacity: 0.5;
+    }
+    /* 滚动条优化 start */
+    ::-webkit-scrollbar{
+      width:8px;
+      height:8px;
+    }
+    ::-webkit-scrollbar-track{
+      background: #f6f6f6;
+      border-radius:2px;
+    }
+    ::-webkit-scrollbar-thumb{
+      background: #cdcdcd;
+      border-radius:2px;
+    }
+    ::-webkit-scrollbar-thumb:hover{
+      background: #747474;
+    }
+    ::-webkit-scrollbar-corner {
+      background: #f6f6f6;
+    }
+    /* 滚动条优化 end */
+  </style>
+  <!-- 全局配置 -->
+  <script src="<%= BASE_URL %>static/config.js"></script>
+</head>
+
+<body>
+<div id="app">
+  <div id="loader-wrapper">
+    <div id="loader"></div>
+    <div class="loader-section section-left"></div>
+    <div class="loader-section section-right"></div>
+    <div class="load_title">正在加载 JeecgBoot 低代码平台,请耐心等待
+
+    </div>
+  </div>
+</div>
+
+<script>
+  var _hmt = _hmt || [];
+  (function() {
+    var hm = document.createElement("script");
+    hm.src = "https://hm.baidu.com/hm.js?0febd9e3cacb3f627ddac64d52caac39";
+    var s = document.getElementsByTagName("script")[0];
+    s.parentNode.insertBefore(hm, s);
+  })();
+</script>
+</body>
+
+</html>

二進制
public/logo.png


+ 11 - 0
public/static/config.js

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

+ 261 - 0
public/tinymce/langs/zh_CN.js

@@ -0,0 +1,261 @@
+tinymce.addI18n('zh_CN',{
+"Redo": "\u91cd\u590d",
+"Undo": "\u64a4\u6d88",
+"Cut": "\u526a\u5207",
+"Copy": "\u590d\u5236",
+"Paste": "\u7c98\u8d34",
+"Select all": "\u5168\u9009",
+"New document": "\u65b0\u6587\u6863",
+"Ok": "\u786e\u5b9a",
+"Cancel": "\u53d6\u6d88",
+"Visual aids": "\u7f51\u683c\u7ebf",
+"Bold": "\u7c97\u4f53",
+"Italic": "\u659c\u4f53",
+"Underline": "\u4e0b\u5212\u7ebf",
+"Strikethrough": "\u5220\u9664\u7ebf",
+"Superscript": "\u4e0a\u6807",
+"Subscript": "\u4e0b\u6807",
+"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
+"Align left": "\u5de6\u5bf9\u9f50",
+"Align center": "\u5c45\u4e2d",
+"Align right": "\u53f3\u5bf9\u9f50",
+"Justify": "\u4e24\u7aef\u5bf9\u9f50",
+"Bullet list": "\u9879\u76ee\u7b26\u53f7",
+"Numbered list": "\u7f16\u53f7\u5217\u8868",
+"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
+"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
+"Close": "\u5173\u95ed",
+"Formats": "\u683c\u5f0f",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u5bf9\u526a\u8d34\u677f\u7684\u8bbf\u95ee\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u952e\u8fdb\u884c\u590d\u5236\u7c98\u8d34\u3002",
+"Headers": "\u6807\u9898",
+"Header 1": "\u6807\u98981",
+"Header 2": "\u6807\u98982",
+"Header 3": "\u6807\u98983",
+"Header 4": "\u6807\u98984",
+"Header 5": "\u6807\u98985",
+"Header 6": "\u6807\u98986",
+"Headings": "\u6807\u9898",
+"Heading 1": "\u6807\u98981",
+"Heading 2": "\u6807\u98982",
+"Heading 3": "\u6807\u98983",
+"Heading 4": "\u6807\u98984",
+"Heading 5": "\u6807\u98985",
+"Heading 6": "\u6807\u98986",
+"Preformatted": "\u9884\u683c\u5f0f\u5316",
+"Div": "Div\u533a\u5757",
+"Pre": "\u9884\u683c\u5f0f\u6587\u672c",
+"Code": "\u4ee3\u7801",
+"Paragraph": "\u6bb5\u843d",
+"Blockquote": "\u5f15\u7528",
+"Inline": "\u6587\u672c",
+"Blocks": "\u533a\u5757",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
+"Font Family": "\u5b57\u4f53",
+"Font Sizes": "\u5b57\u53f7",
+"Class": "Class",
+"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
+"OR": "\u6216",
+"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
+"Upload": "\u4e0a\u4f20",
+"Block": "\u5757",
+"Align": "\u5bf9\u9f50",
+"Default": "\u9ed8\u8ba4",
+"Circle": "\u7a7a\u5fc3\u5706",
+"Disc": "\u5b9e\u5fc3\u5706",
+"Square": "\u65b9\u5757",
+"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
+"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
+"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
+"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Anchor": "\u951a\u70b9",
+"Name": "\u540d\u79f0",
+"Id": "\u6807\u8bc6\u7b26",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
+"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
+"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
+"Special character": "\u7279\u6b8a\u7b26\u53f7",
+"Source code": "\u6e90\u4ee3\u7801",
+"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
+"Language": "\u8bed\u8a00",
+"Code sample": "\u4ee3\u7801\u793a\u4f8b",
+"Color": "\u989c\u8272",
+"R": "R",
+"G": "G",
+"B": "B",
+"Left to right": "\u4ece\u5de6\u5230\u53f3",
+"Right to left": "\u4ece\u53f3\u5230\u5de6",
+"Emoticons": "\u8868\u60c5",
+"Document properties": "\u6587\u6863\u5c5e\u6027",
+"Title": "\u6807\u9898",
+"Keywords": "\u5173\u952e\u8bcd",
+"Description": "\u63cf\u8ff0",
+"Robots": "\u673a\u5668\u4eba",
+"Author": "\u4f5c\u8005",
+"Encoding": "\u7f16\u7801",
+"Fullscreen": "\u5168\u5c4f",
+"Action": "\u64cd\u4f5c",
+"Shortcut": "\u5feb\u6377\u952e",
+"Help": "\u5e2e\u52a9",
+"Address": "\u5730\u5740",
+"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
+"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
+"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
+"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
+"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
+"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
+"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
+"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
+"Plugins": "\u63d2\u4ef6",
+"Handy Shortcuts": "\u5feb\u6377\u952e",
+"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
+"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
+"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Source": "\u5730\u5740",
+"Dimensions": "\u5927\u5c0f",
+"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
+"General": "\u666e\u901a",
+"Advanced": "\u9ad8\u7ea7",
+"Style": "\u6837\u5f0f",
+"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
+"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
+"Border": "\u8fb9\u6846",
+"Insert image": "\u63d2\u5165\u56fe\u7247",
+"Image": "\u56fe\u7247",
+"Image list": "\u56fe\u7247\u5217\u8868",
+"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
+"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
+"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
+"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
+"Edit image": "\u7f16\u8f91\u56fe\u7247",
+"Image options": "\u56fe\u7247\u9009\u9879",
+"Zoom in": "\u653e\u5927",
+"Zoom out": "\u7f29\u5c0f",
+"Crop": "\u88c1\u526a",
+"Resize": "\u8c03\u6574\u5927\u5c0f",
+"Orientation": "\u65b9\u5411",
+"Brightness": "\u4eae\u5ea6",
+"Sharpen": "\u9510\u5316",
+"Contrast": "\u5bf9\u6bd4\u5ea6",
+"Color levels": "\u989c\u8272\u5c42\u6b21",
+"Gamma": "\u4f3d\u9a6c\u503c",
+"Invert": "\u53cd\u8f6c",
+"Apply": "\u5e94\u7528",
+"Back": "\u540e\u9000",
+"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
+"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
+"Insert link": "\u63d2\u5165\u94fe\u63a5",
+"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Text to display": "\u663e\u793a\u6587\u5b57",
+"Url": "\u5730\u5740",
+"Target": "\u6253\u5f00\u65b9\u5f0f",
+"None": "\u65e0",
+"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
+"Remove link": "\u5220\u9664\u94fe\u63a5",
+"Anchors": "\u951a\u70b9",
+"Link": "\u94fe\u63a5",
+"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
+"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
+"Link list": "\u94fe\u63a5\u5217\u8868",
+"Insert video": "\u63d2\u5165\u89c6\u9891",
+"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
+"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
+"Alternative source": "\u955c\u50cf",
+"Poster": "\u5c01\u9762",
+"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
+"Embed": "\u5185\u5d4c",
+"Media": "\u5a92\u4f53",
+"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
+"Page break": "\u5206\u9875\u7b26",
+"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
+"Preview": "\u9884\u89c8",
+"Print": "\u6253\u5370",
+"Save": "\u4fdd\u5b58",
+"Find": "\u67e5\u627e",
+"Replace with": "\u66ff\u6362\u4e3a",
+"Replace": "\u66ff\u6362",
+"Replace all": "\u5168\u90e8\u66ff\u6362",
+"Prev": "\u4e0a\u4e00\u4e2a",
+"Next": "\u4e0b\u4e00\u4e2a",
+"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
+"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
+"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
+"Whole words": "\u5168\u5b57\u5339\u914d",
+"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Ignore": "\u5ffd\u7565",
+"Ignore all": "\u5168\u90e8\u5ffd\u7565",
+"Finish": "\u5b8c\u6210",
+"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
+"Insert table": "\u63d2\u5165\u8868\u683c",
+"Table properties": "\u8868\u683c\u5c5e\u6027",
+"Delete table": "\u5220\u9664\u8868\u683c",
+"Cell": "\u5355\u5143\u683c",
+"Row": "\u884c",
+"Column": "\u5217",
+"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
+"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
+"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
+"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
+"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
+"Delete row": "\u5220\u9664\u884c",
+"Row properties": "\u884c\u5c5e\u6027",
+"Cut row": "\u526a\u5207\u884c",
+"Copy row": "\u590d\u5236\u884c",
+"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
+"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
+"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
+"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Delete column": "\u5220\u9664\u5217",
+"Cols": "\u5217",
+"Rows": "\u884c",
+"Width": "\u5bbd",
+"Height": "\u9ad8",
+"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
+"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
+"Caption": "\u6807\u9898",
+"Left": "\u5de6\u5bf9\u9f50",
+"Center": "\u5c45\u4e2d",
+"Right": "\u53f3\u5bf9\u9f50",
+"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
+"Scope": "\u8303\u56f4",
+"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
+"H Align": "\u6c34\u5e73\u5bf9\u9f50",
+"V Align": "\u5782\u76f4\u5bf9\u9f50",
+"Top": "\u9876\u90e8\u5bf9\u9f50",
+"Middle": "\u5782\u76f4\u5c45\u4e2d",
+"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683c",
+"Row group": "\u884c\u7ec4",
+"Column group": "\u5217\u7ec4",
+"Row type": "\u884c\u7c7b\u578b",
+"Header": "\u8868\u5934",
+"Body": "\u8868\u4f53",
+"Footer": "\u8868\u5c3e",
+"Border color": "\u8fb9\u6846\u989c\u8272",
+"Insert template": "\u63d2\u5165\u6a21\u677f",
+"Templates": "\u6a21\u677f",
+"Template": "\u6a21\u677f",
+"Text color": "\u6587\u5b57\u989c\u8272",
+"Background color": "\u80cc\u666f\u8272",
+"Custom...": "\u81ea\u5b9a\u4e49...",
+"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
+"No color": "\u65e0",
+"Table of Contents": "\u5185\u5bb9\u5217\u8868",
+"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
+"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
+"Words: {0}": "\u5b57\u6570\uff1a{0}",
+"{0} words": "{0} \u5b57",
+"File": "\u6587\u4ef6",
+"Edit": "\u7f16\u8f91",
+"Insert": "\u63d2\u5165",
+"View": "\u89c6\u56fe",
+"Format": "\u683c\u5f0f",
+"Table": "\u8868\u683c",
+"Tools": "\u5de5\u5177",
+"Powered by {0}": "\u7531{0}\u9a71\u52a8",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9"
+});

File diff suppressed because it is too large
+ 554 - 0
public/tinymce/skins/lightgray/content.min.css


File diff suppressed because it is too large
+ 2159 - 0
public/tinymce/skins/lightgray/skin.min.css


File diff suppressed because it is too large
+ 1 - 0
public/v2.js


+ 44 - 0
src/App.vue

@@ -0,0 +1,44 @@
+<template>
+  <a-config-provider :locale="locale">
+    <div id="app">
+      <router-view/>
+    </div>
+  </a-config-provider>
+</template>
+<script>
+  import zhCN from 'ant-design-vue/lib/locale-provider/zh_CN'
+  import enquireScreen from '@/utils/device'
+
+  export default {
+    data () {
+      return {
+        locale: zhCN,
+      }
+    },
+    created () {
+      let that = this
+      enquireScreen(deviceType => {
+        // tablet
+        if (deviceType === 0) {
+          that.$store.commit('TOGGLE_DEVICE', 'mobile')
+          that.$store.dispatch('setSidebar', false)
+        }
+        // mobile
+        else if (deviceType === 1) {
+          that.$store.commit('TOGGLE_DEVICE', 'mobile')
+          that.$store.dispatch('setSidebar', false)
+        }
+        else {
+          that.$store.commit('TOGGLE_DEVICE', 'desktop')
+          that.$store.dispatch('setSidebar', true)
+        }
+
+      })
+    }
+  }
+</script>
+<style>
+  #app {
+    height: 100%;
+  }
+</style>

+ 30 - 0
src/api/GroupRequest.js

@@ -0,0 +1,30 @@
+import Vue from 'vue'
+
+/**
+ * 将一个请求分组
+ *
+ * @param getPromise 传入一个可以获取到Promise对象的方法
+ * @param groupId 分组ID,如果不传或者为空则不分组
+ * @param expire 过期时间,默认 半分钟
+ */
+export function httpGroupRequest(getPromise, groupId, expire = 1000 * 30) {
+  if (groupId == null || groupId === '') {
+    console.log("--------popup----------getFrom  DB-------with---no--groupId ")
+    return getPromise()
+  }
+
+  if (Vue.ls.get(groupId)) {
+    console.log("---------popup--------getFrom  Cache--------groupId = " + groupId)
+    return Promise.resolve(Vue.ls.get(groupId));
+  } else {
+    console.log("--------popup----------getFrom  DB---------groupId = " + groupId)
+  }
+
+  // 还没有发出请求,就发出第一次的请求
+  return getPromise().then(res => {
+    Vue.ls.set(groupId, res, expire);
+    return Promise.resolve(res);
+  })
+}
+
+

+ 161 - 0
src/api/api.js

@@ -0,0 +1,161 @@
+import { getAction, deleteAction, putAction, postAction, httpAction } from '@/api/manage'
+import Vue from 'vue'
+import {UI_CACHE_DB_DICT_DATA } from "@/store/mutation-types"
+
+//角色管理
+const addRole = (params)=>postAction("/sys/role/add",params);
+const editRole = (params)=>putAction("/sys/role/edit",params);
+const checkRoleCode = (params)=>getAction("/sys/role/checkRoleCode",params);
+const queryall = (params)=>getAction("/sys/role/queryall",params);
+
+//用户管理
+const addUser = (params)=>postAction("/sys/user/add",params);
+const editUser = (params)=>putAction("/sys/user/edit",params);
+const queryUserRole = (params)=>getAction("/sys/user/queryUserRole",params);
+const getUserList = (params)=>getAction("/sys/user/list",params);
+const frozenBatch = (params)=>putAction("/sys/user/frozenBatch",params);
+//验证用户是否存在
+const checkOnlyUser = (params)=>getAction("/sys/user/checkOnlyUser",params);
+//改变密码
+const changePassword = (params)=>putAction("/sys/user/changePassword",params);
+
+//权限管理
+const addPermission= (params)=>postAction("/sys/permission/add",params);
+const editPermission= (params)=>putAction("/sys/permission/edit",params);
+const getPermissionList = (params)=>getAction("/sys/permission/list",params);
+const getSystemMenuList = (params)=>getAction("/sys/permission/getSystemMenuList",params);
+const getSystemSubmenu = (params)=>getAction("/sys/permission/getSystemSubmenu",params);
+const getSystemSubmenuBatch = (params) => getAction('/sys/permission/getSystemSubmenuBatch', params)
+const queryTreeList = (params)=>getAction("/sys/permission/queryTreeList",params);
+const queryTreeListForRole = (params)=>getAction("/sys/role/queryTreeList",params);
+const queryListAsync = (params)=>getAction("/sys/permission/queryListAsync",params);
+const queryRolePermission = (params)=>getAction("/sys/permission/queryRolePermission",params);
+const saveRolePermission = (params)=>postAction("/sys/permission/saveRolePermission",params);
+const queryPermissionsByUser = ()=>getAction("/sys/permission/getUserPermissionByToken");
+const loadAllRoleIds = (params)=>getAction("/sys/permission/loadAllRoleIds",params);
+const getPermissionRuleList = (params)=>getAction("/sys/permission/getPermRuleListByPermId",params);
+const queryPermissionRule = (params)=>getAction("/sys/permission/queryPermissionRule",params);
+
+// 部门管理
+const queryDepartTreeList = (params)=>getAction("/sys/sysDepart/queryTreeList",params);
+const queryDepartTreeSync = (params)=>getAction("/sys/sysDepart/queryDepartTreeSync",params);
+const queryIdTree = (params)=>getAction("/sys/sysDepart/queryIdTree",params);
+const queryParentName   = (params)=>getAction("/sys/sysDepart/queryParentName",params);
+const searchByKeywords   = (params)=>getAction("/sys/sysDepart/searchBy",params);
+const deleteByDepartId   = (params)=>deleteAction("/sys/sysDepart/delete",params);
+
+//二级部门管理
+const queryDepartPermission = (params)=>getAction("/sys/permission/queryDepartPermission",params);
+const saveDepartPermission = (params)=>postAction("/sys/permission/saveDepartPermission",params);
+const queryTreeListForDeptRole = (params)=>getAction("/sys/sysDepartPermission/queryTreeListForDeptRole",params);
+const queryDeptRolePermission = (params)=>getAction("/sys/sysDepartPermission/queryDeptRolePermission",params);
+const saveDeptRolePermission = (params)=>postAction("/sys/sysDepartPermission/saveDeptRolePermission",params);
+const queryMyDepartTreeList = (params)=>getAction("/sys/sysDepart/queryMyDeptTreeList",params);
+
+//日志管理
+const deleteLog = (params)=>deleteAction("/sys/log/delete",params);
+const deleteLogList = (params)=>deleteAction("/sys/log/deleteBatch",params);
+
+//数据字典
+const addDict = (params)=>postAction("/sys/dict/add",params);
+const editDict = (params)=>putAction("/sys/dict/edit",params);
+const treeList = (params)=>getAction("/sys/dict/treeList",params);
+const addDictItem = (params)=>postAction("/sys/dictItem/add",params);
+const editDictItem = (params)=>putAction("/sys/dictItem/edit",params);
+
+//字典标签专用(通过code获取字典数组)
+export const ajaxGetDictItems = (code, params)=>getAction(`/sys/dict/getDictItems/${code}`,params);
+//从缓存中获取字典配置
+function getDictItemsFromCache(dictCode) {
+  if (Vue.ls.get(UI_CACHE_DB_DICT_DATA) && Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode]) {
+    let dictItems = Vue.ls.get(UI_CACHE_DB_DICT_DATA)[dictCode];
+    return dictItems;
+  }
+}
+
+//系统通告
+const doReleaseData = (params)=>getAction("/sys/annountCement/doReleaseData",params);
+const doReovkeData = (params)=>getAction("/sys/annountCement/doReovkeData",params);
+//获取系统访问量
+const getLoginfo = (params)=>getAction("/sys/loginfo",params);
+const getVisitInfo = (params)=>getAction("/sys/visitInfo",params);
+
+// 根据部门主键查询用户信息
+const queryUserByDepId = (params)=>getAction("/sys/user/queryUserByDepId",params);
+
+// 重复校验
+const duplicateCheck = (params)=>getAction("/sys/duplicate/check",params);
+// 加载分类字典
+const loadCategoryData = (params)=>getAction("/sys/category/loadAllData",params);
+const checkRuleByCode = (params) => getAction('/sys/checkRule/checkByCode', params)
+//加载我的通告信息
+const getUserNoticeInfo= (params)=>getAction("/sys/sysAnnouncementSend/getMyAnnouncementSend",params);
+const getTransitURL = url => `/sys/common/transitRESTful?url=${encodeURIComponent(url)}`
+// 中转HTTP请求
+export const transitRESTful = {
+  get: (url, parameter) => getAction(getTransitURL(url), parameter),
+  post: (url, parameter) => postAction(getTransitURL(url), parameter),
+  put: (url, parameter) => putAction(getTransitURL(url), parameter),
+  http: (url, parameter) => httpAction(getTransitURL(url), parameter),
+}
+
+export {
+  addRole,
+  editRole,
+  checkRoleCode,
+  addUser,
+  editUser,
+  queryUserRole,
+  getUserList,
+  queryall,
+  frozenBatch,
+  checkOnlyUser,
+  changePassword,
+  getPermissionList,
+  addPermission,
+  editPermission,
+  queryTreeList,
+  queryListAsync,
+  queryRolePermission,
+  saveRolePermission,
+  queryPermissionsByUser,
+  loadAllRoleIds,
+  getPermissionRuleList,
+  queryPermissionRule,
+  queryDepartTreeList,
+  queryDepartTreeSync,
+  queryIdTree,
+  queryParentName,
+  searchByKeywords,
+  deleteByDepartId,
+  deleteLog,
+  deleteLogList,
+  addDict,
+  editDict,
+  treeList,
+  addDictItem,
+  editDictItem,
+  doReleaseData,
+  doReovkeData,
+  getLoginfo,
+  getVisitInfo,
+  queryUserByDepId,
+  duplicateCheck,
+  queryTreeListForRole,
+  getSystemMenuList,
+  getSystemSubmenu,
+  getSystemSubmenuBatch,
+  loadCategoryData,
+  checkRuleByCode,
+  queryDepartPermission,
+  saveDepartPermission,
+  queryTreeListForDeptRole,
+  queryDeptRolePermission,
+  saveDeptRolePermission,
+  queryMyDepartTreeList,
+  getUserNoticeInfo,
+  getDictItemsFromCache
+}
+
+
+

+ 10 - 0
src/api/index.js

@@ -0,0 +1,10 @@
+const api = {
+    Login: '/sys/login',
+    Logout: '/sys/logout',
+    ForgePassword: '/auth/forge-password',
+    Register: '/auth/register',
+    SendSms: '/account/sms',
+    // get my info
+    UserInfo: '/user/info'
+}
+export default api

+ 87 - 0
src/api/login.js

@@ -0,0 +1,87 @@
+import api from './index'
+import { axios } from '@/utils/request'
+
+/**
+ * login func
+ * parameter: {
+ *     username: '',
+ *     password: '',
+ *     remember_me: true,
+ *     captcha: '12345'
+ * }
+ * @param parameter
+ * @returns {*}
+ */
+export function login(parameter) {
+  return axios({
+    url: '/sys/login',
+    method: 'post',
+    data: parameter
+  })
+}
+
+export function phoneLogin(parameter) {
+  return axios({
+    url: '/sys/phoneLogin',
+    method: 'post',
+    data: parameter
+  })
+}
+
+export function getSmsCaptcha(parameter) {
+  return axios({
+    url: api.SendSms,
+    method: 'post',
+    data: parameter
+  })
+}
+
+// export function getInfo() {
+//   return axios({
+//     url: '/api/user/info',
+//     method: 'get',
+//     headers: {
+//       'Content-Type': 'application/json;charset=UTF-8'
+//     }
+//   })
+// }
+
+export function logout(logoutToken) {
+  return axios({
+    url: '/sys/logout',
+    method: 'post',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8',
+      'X-Access-Token':  logoutToken
+    }
+  })
+}
+
+/**
+ * 第三方登录
+ * @param token
+ * @param thirdType
+ * @returns {*}
+ */
+export function thirdLogin(token,thirdType) {
+  return axios({
+    url: `/sys/thirdLogin/getLoginUser/${token}/${thirdType}`,
+    method: 'get',
+    headers: {
+      'Content-Type': 'application/json;charset=UTF-8'
+    }
+  })
+}
+
+/**
+ * 强退其他账号
+ * @param token
+ * @returns {*}
+ */
+export function forceLogout(parameter) {
+  return axios({
+    url: '/sys/online/forceLogout',
+    method: 'post',
+    data: parameter
+  })
+}

+ 213 - 0
src/api/manage.js

@@ -0,0 +1,213 @@
+import Vue from 'vue'
+import { axios } from '@/utils/request'
+import signMd5Utils from '@/utils/encryption/signMd5Utils'
+
+const api = {
+  user: '/mock/api/user',
+  role: '/mock/api/role',
+  service: '/mock/api/service',
+  permission: '/mock/api/permission',
+  permissionNoPager: '/mock/api/permission/no-pager'
+}
+
+export default api
+
+//post
+export function postAction(url,parameter) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getTimestamp()};
+  // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+
+  return axios({
+    url: url,
+    method:'post' ,
+    data: parameter,
+    headers: signHeader
+  })
+}
+
+//post method= {post | put}
+export function httpAction(url,parameter,method) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getTimestamp()};
+  // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+
+  return axios({
+    url: url,
+    method:method ,
+    data: parameter,
+    headers: signHeader
+  })
+}
+
+//put
+export function putAction(url,parameter) {
+  return axios({
+    url: url,
+    method:'put',
+    data: parameter
+  })
+}
+
+//get
+export function getAction(url,parameter) {
+  let sign = signMd5Utils.getSign(url, parameter);
+  //将签名和时间戳,添加在请求接口 Header
+  // update-begin--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+  let signHeader = {"X-Sign": sign,"X-TIMESTAMP": signMd5Utils.getTimestamp()};
+  // update-end--author:taoyan---date:20220421--for: VUEN-410【签名改造】 X-TIMESTAMP牵扯
+
+  return axios({
+    url: url,
+    method: 'get',
+    params: parameter,
+    headers: signHeader
+  })
+}
+
+//deleteAction
+export function deleteAction(url,parameter) {
+  return axios({
+    url: url,
+    method: 'delete',
+    params: parameter
+  })
+}
+
+export function getUserList(parameter) {
+  return axios({
+    url: api.user,
+    method: 'get',
+    params: parameter
+  })
+}
+
+export function getRoleList(parameter) {
+  return axios({
+    url: api.role,
+    method: 'get',
+    params: parameter
+  })
+}
+
+export function getServiceList(parameter) {
+  return axios({
+    url: api.service,
+    method: 'get',
+    params: parameter
+  })
+}
+
+export function getPermissions(parameter) {
+  return axios({
+    url: api.permissionNoPager,
+    method: 'get',
+    params: parameter
+  })
+}
+
+// id == 0 add     post
+// id != 0 update  put
+export function saveService(parameter) {
+  return axios({
+    url: api.service,
+    method: parameter.id == 0 ? 'post' : 'put',
+    data: parameter
+  })
+}
+
+/**
+ * 下载文件 用于excel导出
+ * @param url
+ * @param parameter
+ * @returns {*}
+ */
+export function downFile(url,parameter, method='get'){
+  if(method=='get'){
+    return axios({
+      url: url,
+      params: parameter,
+      method: method ,
+      responseType: 'blob'
+    })
+  }else{
+    return axios({
+      url: url,
+      method: method,
+      data: parameter,
+      responseType: 'blob'
+    })
+  }
+
+}
+
+/**
+ * 下载文件
+ * @param url 文件路径
+ * @param fileName 文件名
+ * @param parameter
+ * @returns {*}
+ */
+export function downloadFile(url, fileName, parameter) {
+  return downFile(url, parameter).then((data) => {
+    if (!data || data.size === 0) {
+      Vue.prototype['$message'].warning('文件下载失败')
+      return
+    }
+    if (typeof window.navigator.msSaveBlob !== 'undefined') {
+      window.navigator.msSaveBlob(new Blob([data]), fileName)
+    } else {
+      let url = window.URL.createObjectURL(new Blob([data]))
+      let link = document.createElement('a')
+      link.style.display = 'none'
+      link.href = url
+      link.setAttribute('download', fileName)
+      document.body.appendChild(link)
+      link.click()
+      document.body.removeChild(link) //下载完成移除元素
+      window.URL.revokeObjectURL(url) //释放掉blob对象
+    }
+  })
+}
+
+/**
+ * 文件上传 用于富文本上传图片
+ * @param url
+ * @param parameter
+ * @returns {*}
+ */
+export function uploadAction(url,parameter){
+  return axios({
+    url: url,
+    data: parameter,
+    method:'post' ,
+    headers: {
+      'Content-Type': 'multipart/form-data',  // 文件上传
+    },
+  })
+}
+
+/**
+ * 获取文件服务访问路径
+ * @param avatar
+ * @param subStr
+ * @returns {*}
+ */
+export function getFileAccessHttpUrl(avatar,subStr) {
+  if(!subStr) subStr = 'http'
+  try {
+    if(avatar && avatar.startsWith(subStr)){
+      return avatar;
+    }else{
+      if(avatar && avatar.length>0 && avatar.indexOf('[')==-1){
+        return window._CONFIG['staticDomainURL'] + "/" + avatar;
+      }
+    }
+  }catch(err){
+   return;
+  }
+}

+ 69 - 0
src/assets/background.svg

@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg width="1361px" height="609px" viewBox="0 0 1361 609" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <!-- Generator: Sketch 46.2 (44496) - http://www.bohemiancoding.com/sketch -->
+    <title>Group 21</title>
+    <desc>Created with Sketch.</desc>
+    <defs></defs>
+    <g id="Ant-Design-Pro-3.0" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="账户密码登录-校验" transform="translate(-79.000000, -82.000000)">
+            <g id="Group-21" transform="translate(77.000000, 73.000000)">
+                <g id="Group-18" opacity="0.8" transform="translate(74.901416, 569.699158) rotate(-7.000000) translate(-74.901416, -569.699158) translate(4.901416, 525.199158)">
+                    <ellipse id="Oval-11" fill="#CFDAE6" opacity="0.25" cx="63.5748792" cy="32.468367" rx="21.7830479" ry="21.766008"></ellipse>
+                    <ellipse id="Oval-3" fill="#CFDAE6" opacity="0.599999964" cx="5.98746479" cy="13.8668601" rx="5.2173913" ry="5.21330997"></ellipse>
+                    <path d="M38.1354514,88.3520215 C43.8984227,88.3520215 48.570234,83.6838647 48.570234,77.9254015 C48.570234,72.1669383 43.8984227,67.4987816 38.1354514,67.4987816 C32.3724801,67.4987816 27.7006688,72.1669383 27.7006688,77.9254015 C27.7006688,83.6838647 32.3724801,88.3520215 38.1354514,88.3520215 Z" id="Oval-3-Copy" fill="#CFDAE6" opacity="0.45"></path>
+                    <path d="M64.2775582,33.1704963 L119.185836,16.5654915" id="Path-12" stroke="#CFDAE6" stroke-width="1.73913043" stroke-linecap="round" stroke-linejoin="round"></path>
+                    <path d="M42.1431708,26.5002681 L7.71190162,14.5640702" id="Path-16" stroke="#E0B4B7" stroke-width="0.702678964" opacity="0.7" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
+                    <path d="M63.9262187,33.521561 L43.6721326,69.3250951" id="Path-15" stroke="#BACAD9" stroke-width="0.702678964" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="1.405357899873153,2.108036953469981"></path>
+                    <g id="Group-17" transform="translate(126.850922, 13.543654) rotate(30.000000) translate(-126.850922, -13.543654) translate(117.285705, 4.381889)" fill="#CFDAE6">
+                        <ellipse id="Oval-4" opacity="0.45" cx="9.13482653" cy="9.12768076" rx="9.13482653" ry="9.12768076"></ellipse>
+                        <path d="M18.2696531,18.2553615 C18.2696531,13.2142826 14.1798519,9.12768076 9.13482653,9.12768076 C4.08980114,9.12768076 0,13.2142826 0,18.2553615 L18.2696531,18.2553615 Z" id="Oval-4" transform="translate(9.134827, 13.691521) scale(-1, -1) translate(-9.134827, -13.691521) "></path>
+                    </g>
+                </g>
+                <g id="Group-14" transform="translate(216.294700, 123.725600) rotate(-5.000000) translate(-216.294700, -123.725600) translate(106.294700, 35.225600)">
+                    <ellipse id="Oval-2" fill="#CFDAE6" opacity="0.25" cx="29.1176471" cy="29.1402439" rx="29.1176471" ry="29.1402439"></ellipse>
+                    <ellipse id="Oval-2" fill="#CFDAE6" opacity="0.3" cx="29.1176471" cy="29.1402439" rx="21.5686275" ry="21.5853659"></ellipse>
+                    <ellipse id="Oval-2-Copy" stroke="#CFDAE6" opacity="0.4" cx="179.019608" cy="138.146341" rx="23.7254902" ry="23.7439024"></ellipse>
+                    <ellipse id="Oval-2" fill="#BACAD9" opacity="0.5" cx="29.1176471" cy="29.1402439" rx="10.7843137" ry="10.7926829"></ellipse>
+                    <path d="M29.1176471,39.9329268 L29.1176471,18.347561 C23.1616351,18.347561 18.3333333,23.1796097 18.3333333,29.1402439 C18.3333333,35.1008781 23.1616351,39.9329268 29.1176471,39.9329268 Z" id="Oval-2" fill="#BACAD9"></path>
+                    <g id="Group-9" opacity="0.45" transform="translate(172.000000, 131.000000)" fill="#E6A1A6">
+                        <ellipse id="Oval-2-Copy-2" cx="7.01960784" cy="7.14634146" rx="6.47058824" ry="6.47560976"></ellipse>
+                        <path d="M0.549019608,13.6219512 C4.12262681,13.6219512 7.01960784,10.722722 7.01960784,7.14634146 C7.01960784,3.56996095 4.12262681,0.670731707 0.549019608,0.670731707 L0.549019608,13.6219512 Z" id="Oval-2-Copy-2" transform="translate(3.784314, 7.146341) scale(-1, 1) translate(-3.784314, -7.146341) "></path>
+                    </g>
+                    <ellipse id="Oval-10" fill="#CFDAE6" cx="218.382353" cy="138.685976" rx="1.61764706" ry="1.61890244"></ellipse>
+                    <ellipse id="Oval-10-Copy-2" fill="#E0B4B7" opacity="0.35" cx="179.558824" cy="175.381098" rx="1.61764706" ry="1.61890244"></ellipse>
+                    <ellipse id="Oval-10-Copy" fill="#E0B4B7" opacity="0.35" cx="180.098039" cy="102.530488" rx="2.15686275" ry="2.15853659"></ellipse>
+                    <path d="M28.9985381,29.9671598 L171.151018,132.876024" id="Path-11" stroke="#CFDAE6" opacity="0.8"></path>
+                </g>
+                <g id="Group-10" opacity="0.799999952" transform="translate(1054.100635, 36.659317) rotate(-11.000000) translate(-1054.100635, -36.659317) translate(1026.600635, 4.659317)">
+                    <ellipse id="Oval-7" stroke="#CFDAE6" stroke-width="0.941176471" cx="43.8135593" cy="32" rx="11.1864407" ry="11.2941176"></ellipse>
+                    <g id="Group-12" transform="translate(34.596774, 23.111111)" fill="#BACAD9">
+                        <ellipse id="Oval-7" opacity="0.45" cx="9.18534718" cy="8.88888889" rx="8.47457627" ry="8.55614973"></ellipse>
+                        <path d="M9.18534718,17.4450386 C13.8657264,17.4450386 17.6599235,13.6143199 17.6599235,8.88888889 C17.6599235,4.16345787 13.8657264,0.332739156 9.18534718,0.332739156 L9.18534718,17.4450386 Z" id="Oval-7"></path>
+                    </g>
+                    <path d="M34.6597385,24.809694 L5.71666084,4.76878945" id="Path-2" stroke="#CFDAE6" stroke-width="0.941176471"></path>
+                    <ellipse id="Oval" stroke="#CFDAE6" stroke-width="0.941176471" cx="3.26271186" cy="3.29411765" rx="3.26271186" ry="3.29411765"></ellipse>
+                    <ellipse id="Oval-Copy" fill="#F7E1AD" cx="2.79661017" cy="61.1764706" rx="2.79661017" ry="2.82352941"></ellipse>
+                    <path d="M34.6312443,39.2922712 L5.06366663,59.785082" id="Path-10" stroke="#CFDAE6" stroke-width="0.941176471"></path>
+                </g>
+                <g id="Group-19" opacity="0.33" transform="translate(1282.537219, 446.502867) rotate(-10.000000) translate(-1282.537219, -446.502867) translate(1142.537219, 327.502867)">
+                    <g id="Group-17" transform="translate(141.333539, 104.502742) rotate(275.000000) translate(-141.333539, -104.502742) translate(129.333539, 92.502742)" fill="#BACAD9">
+                        <circle id="Oval-4" opacity="0.45" cx="11.6666667" cy="11.6666667" r="11.6666667"></circle>
+                        <path d="M23.3333333,23.3333333 C23.3333333,16.8900113 18.1099887,11.6666667 11.6666667,11.6666667 C5.22334459,11.6666667 0,16.8900113 0,23.3333333 L23.3333333,23.3333333 Z" id="Oval-4" transform="translate(11.666667, 17.500000) scale(-1, -1) translate(-11.666667, -17.500000) "></path>
+                    </g>
+                    <circle id="Oval-5-Copy-6" fill="#CFDAE6" cx="201.833333" cy="87.5" r="5.83333333"></circle>
+                    <path d="M143.5,88.8126685 L155.070501,17.6038544" id="Path-17" stroke="#BACAD9" stroke-width="1.16666667"></path>
+                    <path d="M17.5,37.3333333 L127.466252,97.6449735" id="Path-18" stroke="#BACAD9" stroke-width="1.16666667"></path>
+                    <polyline id="Path-19" stroke="#CFDAE6" stroke-width="1.16666667" points="143.902597 120.302281 174.935455 231.571342 38.5 147.510847 126.366941 110.833333"></polyline>
+                    <path d="M159.833333,99.7453842 L195.416667,89.25" id="Path-20" stroke="#E0B4B7" stroke-width="1.16666667" opacity="0.6"></path>
+                    <path d="M205.333333,82.1372105 L238.719406,36.1666667" id="Path-24" stroke="#BACAD9" stroke-width="1.16666667"></path>
+                    <path d="M266.723424,132.231988 L207.083333,90.4166667" id="Path-25" stroke="#CFDAE6" stroke-width="1.16666667"></path>
+                    <circle id="Oval-5" fill="#C1D1E0" cx="156.916667" cy="8.75" r="8.75"></circle>
+                    <circle id="Oval-5-Copy-3" fill="#C1D1E0" cx="39.0833333" cy="148.75" r="5.25"></circle>
+                    <circle id="Oval-5-Copy-2" fill-opacity="0.6" fill="#D1DEED" cx="8.75" cy="33.25" r="8.75"></circle>
+                    <circle id="Oval-5-Copy-4" fill-opacity="0.6" fill="#D1DEED" cx="243.833333" cy="30.3333333" r="5.83333333"></circle>
+                    <circle id="Oval-5-Copy-5" fill="#E0B4B7" cx="175.583333" cy="232.75" r="5.25"></circle>
+                </g>
+            </g>
+        </g>
+    </g>
+</svg>

二進制
src/assets/checkcode.png


二進制
src/assets/daiban.png


二進制
src/assets/duban.png


二進制
src/assets/guaz.png


+ 33 - 0
src/assets/less/AppIcon.less

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

+ 259 - 0
src/assets/less/JAreaLinkage.less

@@ -0,0 +1,259 @@
+.area-zoom-in-top-enter-active,
+.area-zoom-in-top-leave-active {
+  opacity: 1;
+  transform: scaleY(1);
+}
+
+.area-zoom-in-top-enter,
+.area-zoom-in-top-leave-active {
+  opacity: 0;
+  transform: scaleY(0);
+}
+
+.area-select {
+  box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  color: rgba(0, 0, 0, 0.65);
+  font-size: 14px;
+  font-variant: tabular-nums;
+  line-height: 1.5;
+  list-style: none;
+  font-feature-settings: 'tnum';
+  position: relative;
+  outline: 0;
+  display: block;
+  background-color: #fff;
+  border: 1px solid #d9d9d9;
+  border-top-width: 1.02px;
+  border-radius: 4px;
+  outline: none;
+  transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+  -webkit-user-select: none;
+  -ms-user-select: none;
+  user-select: none;
+}
+
+.area-select-wrap .area-select {
+  display: inline-block;
+}
+
+.area-select * {
+  box-sizing: border-box;
+}
+
+.area-select:hover {
+  border-color: #40a9ff;
+  border-right-width: 1px !important;
+  outline: 0;
+}
+
+
+.area-select:active {
+  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
+}
+
+.area-select.small {
+  width: 126px;
+}
+
+.area-select.medium {
+  width: 160px;
+}
+
+.area-select.large {
+  width: 194px;
+}
+
+.area-select.is-disabled {
+  background: #eceff5;
+  cursor: not-allowed;
+}
+
+.area-select.is-disabled:hover {
+  border-color: #e1e2e6;
+}
+
+.area-select.is-disabled .area-selected-trigger {
+  cursor: not-allowed;
+}
+
+.area-select .area-selected-trigger {
+  position: relative;
+  display: block;
+  font-size: 14px;
+  cursor: pointer;
+  margin: 0;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  height: 100%;
+  padding: 8px 20px 7px 12px;
+}
+
+.area-select .area-select-icon {
+  position: absolute;
+  top: 50%;
+  margin-top: -2px;
+  right: 6px;
+  content: "";
+  width: 0;
+  height: 0;
+  border: 6px solid transparent;
+  border-top-color: rgba(0, 0, 0, 0.25);
+  transition: all .3s linear;
+  transform-origin: center;
+}
+
+.area-select .area-select-icon.active {
+  margin-top: -8px;
+  transform: rotate(180deg);
+}
+
+.area-selectable-list-wrap {
+  position: absolute;
+  width: 100%;
+  max-height: 275px;
+  z-index: 15000;
+  background-color: #fff;
+  box-sizing: border-box;
+  overflow-x: auto;
+  margin: 2px 0;
+  border-radius: 4px;
+  outline: none;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+
+  transition: opacity 0.15s, transform 0.3s !important;
+  transform-origin: center top !important;
+}
+
+.area-selectable-list {
+  position: relative;
+  margin: 0;
+  padding: 6px 0;
+  width: 100%;
+  font-size: 14px;
+  color: #565656;
+  list-style: none;
+}
+
+.area-selectable-list .area-select-option {
+  position: relative;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  cursor: pointer;
+  padding: 0 15px 0 10px;
+  height: 32px;
+  line-height: 32px;
+}
+
+.area-selectable-list .area-select-option.hover {
+  background-color: #e6f7ff;
+}
+
+.area-selectable-list .area-select-option.selected {
+  color: rgba(0, 0, 0, 0.65);
+  font-weight: 600;
+  background-color: #efefef;
+}
+
+.cascader-menu-list-wrap {
+  position: absolute;
+  white-space: nowrap;
+  z-index: 15000;
+  background-color: #fff;
+  box-sizing: border-box;
+  overflow: hidden;
+  font-size: 0;
+  margin: 2px 0;
+  border-radius: 4px;
+  outline: none;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+
+  transition: opacity 0.15s, transform 0.3s !important;
+  transform-origin: center top !important;
+}
+
+.cascader-menu-list {
+  position: relative;
+  margin: 0;
+  font-size: 14px;
+  color: #565656;
+  padding: 6px 0;
+  list-style: none;
+  display: inline-block;
+  height: 204px;
+  overflow-x: hidden;
+  overflow-y: auto;
+  min-width: 160px;
+  vertical-align: top;
+  background-color: #fff;
+  border-right: 1px solid #e4e7ed;
+}
+
+.cascader-menu-list:last-child {
+  border-right: none;
+}
+
+.cascader-menu-list .cascader-menu-option {
+  position: relative;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  cursor: pointer;
+  padding: 0 15px 0 10px;
+  height: 32px;
+  line-height: 32px;
+}
+
+.cascader-menu-list .cascader-menu-option.hover,
+.cascader-menu-list .cascader-menu-option:hover {
+  background-color: #e6f7ff;
+}
+
+.cascader-menu-list .cascader-menu-option.selected {
+  color: rgba(0, 0, 0, 0.65);
+  font-weight: 600;
+  background-color: #efefef;
+}
+
+.cascader-menu-list .cascader-menu-option.cascader-menu-extensible:after {
+  position: absolute;
+  top: 50%;
+  margin-top: -4px;
+  right: 5px;
+  content: "";
+  width: 0;
+  height: 0;
+  border: 4px solid transparent;
+  border-left-color: #a1a4ad;
+}
+
+.cascader-menu-list::-webkit-scrollbar,
+.area-selectable-list-wrap::-webkit-scrollbar {
+  width: 8px;
+  background: transparent;
+}
+
+.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:decremen,
+.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:end:decrement,
+.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:increment,
+.area-selectable-list-wrap::-webkit-scrollbar-button:vertical:start:increment,
+.cascader-menu-list::-webkit-scrollbar-button:vertical:decremen,
+.cascader-menu-list::-webkit-scrollbar-button:vertical:end:decrement,
+.cascader-menu-list::-webkit-scrollbar-button:vertical:increment,
+.cascader-menu-list::-webkit-scrollbar-button:vertical:start:increment {
+  display: none;
+}
+
+.cascader-menu-list::-webkit-scrollbar-thumb:vertical,
+.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical {
+  background-color: #b8b8b8;
+  border-radius: 4px;
+}
+
+.cascader-menu-list::-webkit-scrollbar-thumb:vertical:hover,
+.area-selectable-list-wrap::-webkit-scrollbar-thumb:vertical:hover {
+  background-color: #777;
+}

+ 15 - 0
src/assets/less/TableExpand.less

@@ -0,0 +1,15 @@
+/** [表格主题样式一] 表格强制列不换行 */
+.j-table-force-nowrap {
+  td, th {
+    white-space: nowrap;
+  }
+
+  .ant-table-selection-column {
+    padding: 12px 22px !important;
+  }
+
+  /** 列自适应,弊端会导致列宽失效 */
+  &.ant-table-wrapper .ant-table-content {
+    overflow-x: auto;
+  }
+}

File diff suppressed because it is too large
+ 1 - 0
src/assets/less/codemirror_idea.css


+ 61 - 0
src/assets/less/common.less

@@ -0,0 +1,61 @@
+
+/*列表上方操作按钮区域*/
+.ant-card-body .table-operator {
+  margin-bottom: 8px;
+}
+/** Button按钮间距 */
+.table-operator .ant-btn {
+  margin: 0 8px 8px 0;
+}
+.table-operator .ant-btn-group .ant-btn {
+  margin: 0;
+}
+
+.table-operator .ant-btn-group .ant-btn:last-child {
+  margin: 0 8px 8px 0;
+}
+/*列表td的padding设置 可以控制列表大小*/
+.ant-table-tbody .ant-table-row td {
+  padding-top: 15px;
+  padding-bottom: 15px;
+}
+
+/*列表页面弹出modal*/
+.ant-modal-cust-warp {
+  height: 100%
+}
+
+/*弹出modal Y轴滚动条*/
+.ant-modal-cust-warp .ant-modal-body {
+  height: calc(100% - 110px) !important;
+  overflow-y: auto
+}
+
+/*弹出modal 先有content后有body 故滚动条控制在body上*/
+.ant-modal-cust-warp .ant-modal-content {
+  height: 90% !important;
+  overflow-y: hidden
+}
+/*列表中有图片的加这个样式 参考用户管理*/
+.anty-img-wrap {
+  height: 25px;
+  position: relative;
+}
+.anty-img-wrap > img {
+  max-height: 100%;
+}
+/*列表中范围查询样式*/
+.query-group-cust{width: calc(50% - 10px)}
+.query-group-split-cust:before{content:"~";width: 20px;display: inline-block;text-align: center}
+
+
+/*erp风格子表外框padding设置*/
+.ant-card-wider-padding.cust-erp-sub-tab>.ant-card-body{padding:5px 12px}
+
+/* 内嵌子表背景颜色 */
+.j-inner-table-wrapper /deep/ .ant-table-expanded-row .ant-table-wrapper .ant-table-tbody .ant-table-row {
+  background-color: #FFFFFF;
+}
+
+/**隐藏样式-modal确定按钮 */
+.jee-hidden{display: none}

+ 28 - 0
src/assets/less/index.less

@@ -0,0 +1,28 @@
+/**
+ * 列表查询通用样式,移动端自适应
+ */
+.search{
+  margin-bottom: 54px;
+}
+.fold{
+  width: calc(100% - 216px);
+  display: inline-block
+}
+.operator{
+  margin-bottom: 18px;
+}
+@media screen and (max-width: 900px) {
+  .fold {
+    width: 100%;
+  }
+}
+.operator button {
+  margin-right: 5px;
+}
+i {
+  cursor: pointer;
+}
+.trcolor{
+  background-color: rgba(255, 192, 203, 0.31);
+  color:red;
+}

二進制
src/assets/logo-white.png


二進制
src/assets/logo.png


+ 172 - 0
src/assets/logo.svg

@@ -0,0 +1,172 @@
+<?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="293px" height="293px" viewBox="0 0 293 293" enable-background="new 0 0 293 293" xml:space="preserve">  <image id="image0" width="293" height="293" x="0" y="0"
+    href="data:image/png;base64,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
+OjAwP26CAAAAAABJRU5ErkJggg==" />
+</svg>

二進制
src/assets/nodata.png


二進制
src/assets/pdf4.jpg


二進制
src/assets/zaiban.png


+ 77 - 0
src/cas/sso.js

@@ -0,0 +1,77 @@
+import Vue from 'vue'
+import { ACCESS_TOKEN } from "@/store/mutation-types"
+import store from '@/store'
+/**
+ * 单点登录
+ */
+const init = (callback) => {
+  if (process.env.VUE_APP_SSO == 'true') {
+    console.log("-------单点登录开始-------");
+    let token = Vue.ls.get(ACCESS_TOKEN);
+    let st = getUrlParam("ticket");
+    let sevice = "http://" + window.location.host + "/";
+    if (token) {
+      loginSuccess(callback);
+    } else {
+      if (st) {
+        validateSt(st, sevice, callback);
+      } else {
+        let serviceUrl = encodeURIComponent(sevice);
+        window.location.href = window._CONFIG['casPrefixUrl'] + "/login?service=" + serviceUrl;
+      }
+    }
+    console.log("-------单点登录结束-------");
+  }else{
+    callback && callback()
+  }
+};
+const SSO = {
+  init: init
+};
+
+function getUrlParam(paraName) {
+  let url = document.location.toString();
+  let arrObj = url.split("?");
+
+  if (arrObj.length > 1) {
+    let arrPara = arrObj[1].split("&");
+    let arr;
+
+    for (let i = 0; i < arrPara.length; i++) {
+      arr = arrPara[i].split("=");
+
+      if (arr != null && arr[0] == paraName) {
+        return arr[1];
+      }
+    }
+    return "";
+  }
+  else {
+    return "";
+  }
+}
+
+function validateSt(ticket,service,callback){
+  let params = {
+    ticket: ticket,
+    service:service
+  };
+  store.dispatch('ValidateLogin',params).then(res => {
+    //this.departConfirm(res)
+    if(res.success){
+      loginSuccess(callback);
+    }else{
+      let sevice = "http://"+window.location.host+"/";
+      let serviceUrl = encodeURIComponent(sevice);
+      window.location.href = window._CONFIG['casPrefixUrl']+"/login?service="+serviceUrl;
+    }
+  }).catch((err) => {
+    console.log(err);
+    //that.requestFailed(err);
+  });
+}
+
+function loginSuccess (callback) {
+  callback();
+}
+export default SSO;

+ 46 - 0
src/components/AvatarList/Item.vue

@@ -0,0 +1,46 @@
+<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>

+ 100 - 0
src/components/AvatarList/List.vue

@@ -0,0 +1,100 @@
+<!--
+<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>

+ 4 - 0
src/components/AvatarList/index.js

@@ -0,0 +1,4 @@
+import AvatarList from './List'
+import "./index.less"
+
+export default AvatarList

+ 60 - 0
src/components/AvatarList/index.less

@@ -0,0 +1,60 @@
+@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;
+        }
+      }
+    }
+  }
+}
+

+ 111 - 0
src/components/ChartCard.vue

@@ -0,0 +1,111 @@
+<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">{{ title }}</span>
+        <span class="chart-card-action">
+          <slot name="action"></slot>
+        </span>
+      </div>
+      <div class="total"><span>{{ total }}</span></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: String,
+        default: ''
+      },
+      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>

+ 103 - 0
src/components/CountDown/CountDown.vue

@@ -0,0 +1,103 @@
+<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>

+ 3 - 0
src/components/CountDown/index.js

@@ -0,0 +1,3 @@
+import CountDown from './CountDown'
+
+export default CountDown

+ 49 - 0
src/components/Ellipsis/Ellipsis.vue

@@ -0,0 +1,49 @@
+<script>
+  import { cutStrByFullLength, getStrFullLength } from '@/components/_util/StringUtil'
+
+  export default {
+    name: 'Ellipsis',
+    props: {
+      prefixCls: {
+        type: String,
+        default: 'ant-pro-ellipsis'
+      },
+      tooltip: {
+        type: Boolean,
+        default: true,
+      },
+      length: {
+        type: Number,
+        default: 25,
+      },
+      lines: {
+        type: Number,
+        default: 1
+      },
+      fullWidthRecognition: {
+        type: Boolean,
+        default: false
+      }
+    },
+    methods: {},
+    render() {
+      const { tooltip, length } = this.$props
+      let text = ''
+      // 处理没有default插槽时的特殊情况
+      if (this.$slots.default) {
+        text = this.$slots.default.map(vNode => vNode.text).join('')
+      }
+      // 判断是否显示 tooltip
+      if (tooltip && getStrFullLength(text) > length) {
+        return (
+          <a-tooltip>
+            <template slot="title">{text}</template>
+            <span>{cutStrByFullLength(text, this.length) + '…'}</span>
+          </a-tooltip>
+        )
+      } else {
+        return (<span>{text}</span>)
+      }
+    }
+  }
+</script>

+ 3 - 0
src/components/Ellipsis/index.js

@@ -0,0 +1,3 @@
+import Ellipsis from './Ellipsis'
+
+export default Ellipsis

+ 228 - 0
src/components/JVxeCells/JVxeFileCell.vue

@@ -0,0 +1,228 @@
+<template>
+  <div>
+    <template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
+      <div :key="fileKey" style="position: relative;">
+        <a-tooltip v-if="file.status==='uploading'" :title="`上传中(${Math.floor(file.percent)}%)`">
+          <a-icon type="loading"/>
+          <span style="margin-left:5px">上传中…</span>
+        </a-tooltip>
+
+        <a-tooltip v-else-if="file.status==='done'" :title="file.name">
+          <a-icon type="paper-clip"/>
+          <span style="margin-left:5px">{{ ellipsisFileName }}</span>
+        </a-tooltip>
+
+        <a-tooltip v-else :title="file.message||'上传失败'">
+          <a-icon type="exclamation-circle" style="color:red;"/>
+          <span style="margin-left:5px">{{ ellipsisFileName }}</span>
+        </a-tooltip>
+
+        <template style="width: 30px">
+          <a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
+            <a-tooltip title="操作">
+              <a-icon
+                v-if="file.status!=='uploading'"
+                type="setting"
+                style="cursor: pointer;"/>
+            </a-tooltip>
+
+            <a-menu slot="overlay">
+              <a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
+                <span><a-icon type="download"/>&nbsp;下载</span>
+              </a-menu-item>
+              <a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
+                <span><a-icon type="delete"/>&nbsp;删除</span>
+              </a-menu-item>
+              <a-menu-item @click="handleMoreOperation(originColumn)">
+                <span><a-icon type="bars"/> 更多</span>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </template>
+      </div>
+    </template>
+
+    <a-upload
+      v-show="!hasFile"
+      name="file"
+      :data="{'isup': 1}"
+      :multiple="false"
+      :action="uploadAction"
+      :headers="uploadHeaders"
+      :showUploadList="false"
+      v-bind="cellProps"
+      @change="handleChangeUpload"
+    >
+      <a-button icon="upload">{{originColumn.btnText || '上传文件'}}</a-button>
+    </a-upload>
+    <j-file-pop ref="filePop" @ok="handleFileSuccess" :number="number"/>
+  </div>
+</template>
+
+<script>
+  import { getFileAccessHttpUrl } from '@api/manage'
+  import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
+  import { ACCESS_TOKEN } from '@/store/mutation-types'
+  import JFilePop from '@/components/jeecg/minipop/JFilePop'
+
+  import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
+
+  export default {
+    name: 'JVxeFileCell',
+    mixins: [JVxeCellMixins],
+    components: {JFilePop},
+    props: {},
+    data() {
+      return {
+        innerFile: null,
+        number:0,
+      }
+    },
+    computed: {
+      /** upload headers */
+      uploadHeaders() {
+        let {originColumn: col} = this
+        let headers = {}
+        if (col.token === true) {
+          headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
+        }
+        return headers
+      },
+
+      /** 上传请求地址 */
+      uploadAction() {
+        if (!this.originColumn.action) {
+          return window._CONFIG['domianURL'] + '/sys/common/upload'
+        } else {
+          return this.originColumn.action
+        }
+      },
+
+      hasFile() {
+        return this.innerFile != null
+      },
+
+      ellipsisFileName() {
+        let length = 5
+        let file = this.innerFile
+        if (!file || !file.name) {
+          return ''
+        }
+        if (file.name.length > length) {
+          return file.name.substr(0, length) + '…'
+        }
+        return file.name
+      },
+
+      responseName() {
+        if (this.originColumn.responseName) {
+          return this.originColumn.responseName
+        } else {
+          return 'message'
+        }
+      },
+
+    },
+    watch: {
+      innerValue: {
+        immediate: true,
+        handler() {
+          if (this.innerValue) {
+            this.innerFile = this.innerValue
+          } else {
+            this.innerFile = null
+          }
+        },
+      },
+    },
+    methods: {
+
+      // 点击更多按钮
+      handleMoreOperation(originColumn) {
+        //update-begin-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
+        if (originColumn.number) {
+          this.number = originColumn.number
+        } else {
+          this.number = 0
+        }
+        //update-end-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
+        if(originColumn && originColumn.fieldExtendJson){
+          let json = JSON.parse(originColumn.fieldExtendJson);
+          this.number = json.uploadnum?json.uploadnum:0;
+        }
+        let path = ''
+        if (this.innerFile) {
+          path = this.innerFile.path
+        }
+        this.$refs.filePop.show('', path)
+      },
+
+      // 更多上传回调
+      handleFileSuccess(file) {
+        if (file) {
+          this.innerFile.path = file.path
+          this.handleChangeCommon(this.innerFile)
+        }
+      },
+
+      handleChangeUpload(info) {
+        let {originColumn: col} = this
+        let {file} = info
+        let value = {
+          name: file.name,
+          type: file.type,
+          size: file.size,
+          status: file.status,
+          percent: file.percent
+        }
+        if (file.response) {
+          value['responseName'] = file.response[this.responseName]
+        }
+        if (file.status === 'done') {
+          if (typeof file.response.success === 'boolean') {
+            if (file.response.success) {
+              value['path'] = file.response[this.responseName]
+              this.handleChangeCommon(value)
+            } else {
+              value['status'] = 'error'
+              value['message'] = file.response.message || '未知错误'
+            }
+          } else {
+            // 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
+            value['path'] = file.response[this.responseName]
+            this.handleChangeCommon(value)
+          }
+        } else if (file.status === 'error') {
+          value['message'] = file.response.message || '未知错误'
+        }
+        this.innerFile = value
+      },
+
+      handleClickDownloadFile() {
+        let {url, path} = this.innerFile || {}
+        if (!url || url.length === 0) {
+          if (path && path.length > 0) {
+            url = getFileAccessHttpUrl(path.split(',')[0])
+          }
+        }
+        if (url) {
+          window.open(url)
+        }
+      },
+
+      handleClickDeleteFile() {
+        this.handleChangeCommon(null)
+      },
+
+    },
+    // 【组件增强】注释详见:JVxeCellMixins.js
+    enhanced: {
+      switches: {visible: true},
+      getValue: value => JVxeUploadCell.enhanced.getValue(value),
+      setValue: value => JVxeUploadCell.enhanced.setValue(value),
+    }
+  }
+</script>
+
+<style scoped lang="less">
+</style>

+ 242 - 0
src/components/JVxeCells/JVxeImageCell.vue

@@ -0,0 +1,242 @@
+<template>
+  <div>
+    <template v-if="hasFile" v-for="(file, fileKey) of [innerFile || {}]">
+      <div :key="fileKey" style="position: relative;">
+        <template v-if="!file || !(file['url'] || file['path'] || file['message'])">
+          <a-tooltip :title="'请稍后: ' + JSON.stringify (file) + ((file['url'] || file['path'] || file['message']))">
+            <a-icon type="loading"/>
+          </a-tooltip>
+        </template>
+        <template v-else-if="file['path']">
+          <img class="j-editable-image" :src="imgSrc" alt="无图片" @click="handleMoreOperation"/>
+        </template>
+        <a-tooltip v-else :title="file.message||'上传失败'" @click="handleClickShowImageError">
+          <a-icon type="exclamation-circle" style="color:red;"/>
+        </a-tooltip>
+
+        <template style="width: 30px">
+          <a-dropdown :trigger="['click']" placement="bottomRight" style="margin-left: 10px;">
+            <a-tooltip title="操作">
+              <a-icon
+                v-if="file.status!=='uploading'"
+                type="setting"
+                style="cursor: pointer;"/>
+            </a-tooltip>
+
+            <a-menu slot="overlay">
+              <a-menu-item v-if="originColumn.allowDownload !== false" @click="handleClickDownloadFile">
+                <span><a-icon type="download"/>&nbsp;下载</span>
+              </a-menu-item>
+              <a-menu-item v-if="originColumn.allowRemove !== false" @click="handleClickDeleteFile">
+                <span><a-icon type="delete"/>&nbsp;删除</span>
+              </a-menu-item>
+              <a-menu-item @click="handleMoreOperation(originColumn)">
+                <span><a-icon type="bars"/> 更多</span>
+              </a-menu-item>
+            </a-menu>
+          </a-dropdown>
+        </template>
+
+      </div>
+    </template>
+    <a-upload
+      v-show="!hasFile"
+      name="file"
+      :data="{'isup': 1}"
+      :multiple="false"
+      :action="uploadAction"
+      :headers="uploadHeaders"
+      :showUploadList="false"
+      v-bind="cellProps"
+      @change="handleChangeUpload"
+    >
+      <a-button icon="upload">{{originColumn.btnText || '上传图片'}}</a-button>
+    </a-upload>
+    <j-file-pop ref="filePop" @ok="handleFileSuccess" :number="number"/>
+  </div>
+</template>
+
+<script>
+  import { getFileAccessHttpUrl } from '@api/manage'
+  import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
+  import { ACCESS_TOKEN } from '@/store/mutation-types'
+  import JFilePop from '@/components/jeecg/minipop/JFilePop'
+
+  import JVxeUploadCell from '@/components/jeecg/JVxeTable/components/cells/JVxeUploadCell'
+
+  export default {
+    name: 'JVxeImageCell',
+    mixins: [JVxeCellMixins],
+    components: {JFilePop},
+    props: {},
+    data() {
+      return {
+        innerFile: null,
+        number:0
+      }
+    },
+    computed: {
+      /** upload headers */
+      uploadHeaders() {
+        let {originColumn: col} = this
+        let headers = {}
+        if (col.token === true) {
+          headers['X-Access-Token'] = this.$ls.get(ACCESS_TOKEN)
+        }
+        return headers
+      },
+
+      /** 上传请求地址 */
+      uploadAction() {
+        if (!this.originColumn.action) {
+          return window._CONFIG['domianURL'] + '/sys/common/upload'
+        } else {
+          return this.originColumn.action
+        }
+      },
+
+      hasFile() {
+        return this.innerFile != null
+      },
+
+      /** 预览图片地址 */
+      imgSrc() {
+        if (this.innerFile) {
+          if (this.innerFile['url']) {
+            return this.innerFile['url']
+          } else if (this.innerFile['path']) {
+            let path = this.innerFile['path'].split(',')[0]
+            return getFileAccessHttpUrl(path)
+          }
+        }
+        return ''
+      },
+
+      responseName() {
+        if (this.originColumn.responseName) {
+          return this.originColumn.responseName
+        } else {
+          return 'message'
+        }
+      },
+
+    },
+    watch: {
+      innerValue: {
+        immediate: true,
+        handler() {
+          if (this.innerValue) {
+            this.innerFile = this.innerValue
+          } else {
+            this.innerFile = null
+          }
+        },
+      },
+    },
+    methods: {
+
+      // 点击更多按钮
+      handleMoreOperation(originColumn) {
+        //update-begin-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
+        if (originColumn.number) {
+          this.number = originColumn.number
+        } else {
+          this.number = 0
+        }
+        //update-end-author:wangshuai date:20201021 for:LOWCOD-969 判断传过来的字段是否存在number,用于控制上传文件
+        if(originColumn && originColumn.fieldExtendJson){
+          let json = JSON.parse(originColumn.fieldExtendJson);
+          this.number = json.uploadnum?json.uploadnum:0;
+        }
+        let path = ''
+        if (this.innerFile) {
+          path = this.innerFile.path
+        }
+        this.$refs.filePop.show('', path, 'img')
+      },
+
+      // 更多上传回调
+      handleFileSuccess(file) {
+        if (file) {
+          this.innerFile.path = file.path
+          this.handleChangeCommon(this.innerFile)
+        }
+      },
+
+      // 弹出上传出错详细信息
+      handleClickShowImageError() {
+        let file = this.innerFile || null
+        if (file && file['message']) {
+          this.$error({title: '上传出错', content: '错误信息:' + file['message'], maskClosable: true})
+        }
+      },
+
+      handleChangeUpload(info) {
+        let {originColumn: col} = this
+        let {file} = info
+        let value = {
+          name: file.name,
+          type: file.type,
+          size: file.size,
+          status: file.status,
+          percent: file.percent
+        }
+        if (file.response) {
+          value['responseName'] = file.response[this.responseName]
+        }
+        if (file.status === 'done') {
+          if (typeof file.response.success === 'boolean') {
+            if (file.response.success) {
+              value['path'] = file.response[this.responseName]
+              this.handleChangeCommon(value)
+            } else {
+              value['status'] = 'error'
+              value['message'] = file.response.message || '未知错误'
+            }
+          } else {
+            // 考虑到如果设置action上传路径为非jeecg-boot后台,可能不会返回 success 属性的情况,就默认为成功
+            value['path'] = file.response[this.responseName]
+            this.handleChangeCommon(value)
+          }
+        } else if (file.status === 'error') {
+          value['message'] = file.response.message || '未知错误'
+        }
+        this.innerFile = value
+      },
+
+      handleClickDownloadFile() {
+        if (this.imgSrc) {
+          window.open(this.imgSrc)
+        }
+      },
+
+      handleClickDeleteFile() {
+        this.handleChangeCommon(null)
+      },
+
+    },
+    // 【组件增强】注释详见:JVxeCellMixins.js
+    enhanced: {
+      switches: {visible: true},
+      getValue: value => JVxeUploadCell.enhanced.getValue(value),
+      setValue: value => JVxeUploadCell.enhanced.setValue(value),
+    }
+  }
+</script>
+
+<style scoped lang="less">
+  .j-editable-image {
+    height: 32px;
+    max-width: 100px !important;
+    cursor: pointer;
+
+    &:hover {
+      opacity: 0.8;
+    }
+
+    &:active {
+      opacity: 0.6;
+    }
+
+  }
+</style>

+ 67 - 0
src/components/JVxeCells/JVxePopupCell.vue

@@ -0,0 +1,67 @@
+<template>
+  <j-popup
+    v-bind="popupProps"
+    @input="handlePopupInput"
+  />
+</template>
+
+<script>
+  import JVxeCellMixins, { dispatchEvent, vModel } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
+
+  export default {
+    name: 'JVxePopupCell',
+    mixins: [JVxeCellMixins],
+    computed: {
+      popupProps() {
+        const {innerValue, originColumn: col, caseId, cellProps} = this
+        return {
+          ...cellProps,
+          value: innerValue,
+          field: col.field || col.key,
+          code: col.popupCode,
+          orgFields: col.orgFields,
+          destFields: col.destFields,
+          groupId: caseId,
+          param: col.param,
+          sorter: col.sorter,
+        }
+      },
+    },
+    methods: {
+      /** popup回调 */
+      handlePopupInput(value, others) {
+        const {row, originColumn: col} = this
+        // 存储输入的值
+        let popupValue = value
+        if (others && Object.keys(others).length > 0) {
+          Object.keys(others).forEach(key => {
+            let currentValue = others[key]
+            // 当前列直接赋值,其他列通过vModel赋值
+            if (key === col.key) {
+              popupValue = currentValue
+            } else {
+              vModel.call(this, currentValue, row, key)
+            }
+          })
+        }
+        this.handleChangeCommon(popupValue)
+      },
+    },
+    // 【组件增强】注释详见:JVxeCellMixins.js
+    enhanced: {
+      aopEvents: {
+        editActived(event) {
+          // 【issues/3854】附表控件类型为popup必填时未选择值提交表单会报错
+          if (event.$event && event.$event.type === 'valid-error') {
+            return;
+          }
+          dispatchEvent.call(this, event, 'ant-input')
+        },
+      },
+    },
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 60 - 0
src/components/JVxeCells/JVxeRadioCell.vue

@@ -0,0 +1,60 @@
+<template>
+  <a-radio-group
+    :class="clazz"
+    :value="innerValue"
+    v-bind="cellProps"
+    @change="(e)=>handleChangeCommon(e.target.value)"
+  >
+    <a-radio
+      v-for="item of originColumn.options"
+      :key="item.value"
+      :value="item.value"
+      @click="$event=>handleRadioClick(item,$event)"
+    >{{ item.text }}
+    </a-radio>
+  </a-radio-group>
+</template>
+
+<script>
+  import JVxeCellMixins from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
+
+  export default {
+    name: 'JVxeRadioCell',
+    mixins: [JVxeCellMixins],
+    computed: {
+      scrolling() {
+        return !!this.renderOptions.scrolling
+      },
+      clazz() {
+        return {
+          'j-vxe-radio': true,
+          'no-animation': this.scrolling
+        }
+      },
+    },
+    methods: {
+      handleRadioClick(item) {
+        if (this.originColumn.allowClear === true) {
+          // 取消选择
+          if (item.value === this.innerValue) {
+            this.handleChangeCommon(null)
+          }
+        }
+      },
+    },
+    // 【组件增强】注释详见:JVxeCellMixins.js
+    enhanced: {
+      switches: {visible: true},
+    }
+  }
+</script>
+
+<style lang="less">
+  // 关闭动画,防止滚动时动态赋值出现问题
+  .j-vxe-radio.no-animation {
+    .ant-radio-inner,
+    .ant-radio-inner::after {
+      transition: none !important;
+    }
+  }
+</style>

+ 262 - 0
src/components/JVxeCells/JVxeSelectDictSearchCell.js

@@ -0,0 +1,262 @@
+import debounce from 'lodash/debounce'
+import { getAction } from '@/api/manage'
+import { cloneObject } from '@/utils/util'
+import { filterDictText } from '@/components/dict/JDictSelectUtil'
+import { ajaxGetDictItems, getDictItemsFromCache } from '@/api/api'
+import JVxeCellMixins, { dispatchEvent } from '@/components/jeecg/JVxeTable/mixins/JVxeCellMixins'
+
+/** 公共资源 */
+const common = {
+  /** value - label map,防止重复查询(刷新清空缓存) */
+  labelMap: new Map(),
+
+  /** 公共data */
+  data() {
+    return {
+      loading: false,
+      innerSelectValue: null,
+      innerOptions: [],
+    }
+  },
+  /** 公共计算属性 */
+  computed: {
+    dict() {
+      return this.originColumn.dict
+    },
+    options() {
+      if (this.isAsync) {
+        return this.innerOptions
+      } else {
+        return this.originColumn.options || []
+      }
+    },
+    // 是否是异步模式
+    isAsync() {
+      let isAsync = this.originColumn.async
+      return (isAsync != null && isAsync !== '') ? !!isAsync : true
+    },
+  },
+  /** 公共属性监听 */
+  watch: {
+    innerValue: {
+      immediate: true,
+      handler(value) {
+        if (value == null || value === '') {
+          this.innerSelectValue = null
+        } else {
+          this.loadDataByValue(value)
+        }
+      }
+    },
+    dict() {
+      this.loadDataByDict()
+    }
+  },
+  /** 公共方法 */
+  methods: {
+
+    // 根据 value 查询数据,用于回显
+    async loadDataByValue(value) {
+      if (this.isAsync) {
+        if (this.innerSelectValue !== value) {
+          if (common.labelMap.has(value)) {
+            this.innerOptions = cloneObject(common.labelMap.get(value))
+          } else {
+            let {success, result} = await getAction(`/sys/dict/loadDictItem/${this.dict}`, {key: value})
+            if (success && result && result.length > 0) {
+              this.innerOptions = [{value: value, text: result[0]}]
+              common.labelMap.set(value, cloneObject(this.innerOptions))
+            }
+          }
+        }
+      }
+      this.innerSelectValue = (value || '').toString()
+    },
+
+    // 初始化字典
+    async loadDataByDict() {
+      if (!this.isAsync) {
+        // 如果字典项集合有数据
+        if (!this.originColumn.options || this.originColumn.options.length === 0) {
+          // 根据字典Code, 初始化字典数组
+          let dictStr = ''
+          if (this.dict) {
+            let arr = this.dict.split(',')
+            if (arr[0].indexOf('where') > 0) {
+              let tbInfo = arr[0].split('where')
+              dictStr = tbInfo[0].trim() + ',' + arr[1] + ',' + arr[2] + ',' + encodeURIComponent(tbInfo[1])
+            } else {
+              dictStr = this.dict
+            }
+            if (this.dict.indexOf(',') === -1) {
+              //优先从缓存中读取字典配置
+              let cache = getDictItemsFromCache(this.dict)
+              if (cache) {
+                this.innerOptions = cache
+                return
+              }
+            }
+            let {success, result} = await ajaxGetDictItems(dictStr, null)
+            if (success) {
+              this.innerOptions = result
+            }
+          }
+        }
+      }
+    },
+
+  },
+
+}
+
+// 显示组件,自带翻译
+export const DictSearchSpanCell = {
+  name: 'JVxeSelectSearchSpanCell',
+  mixins: [JVxeCellMixins],
+  data() {
+    return {
+      ...common.data.apply(this),
+    }
+  },
+  computed: {
+    ...common.computed,
+  },
+  watch: {
+    ...common.watch,
+  },
+  methods: {
+    ...common.methods,
+  },
+  render(h) {
+    return h('span', {}, [
+      filterDictText(this.innerOptions, this.innerSelectValue || this.innerValue)
+    ])
+  },
+}
+
+// 请求id
+let requestId = 0
+
+// 输入选择组件
+export const DictSearchInputCell = {
+  name: 'JVxeSelectSearchInputCell',
+  mixins: [JVxeCellMixins],
+  data() {
+    return {
+      ...common.data.apply(this),
+
+      hasRequest: false,
+      scopedSlots: {
+        notFoundContent: () => {
+          if (this.loading) {
+            return <a-spin size="small"/>
+          } else if (this.hasRequest) {
+            return <div>没有查询到任何数据</div>
+          } else {
+            return <div>{this.tipsContent}</div>
+          }
+        }
+      }
+    }
+  },
+  computed: {
+    ...common.computed,
+    tipsContent() {
+      return this.originColumn.tipsContent || '请输入搜索内容'
+    },
+    filterOption() {
+      if (this.isAsync) {
+        return null
+      }
+      return (input, option) => option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
+    },
+  },
+  watch: {
+    ...common.watch,
+  },
+  created() {
+    this.loadData = debounce(this.loadData, 300)//消抖
+  },
+  methods: {
+    ...common.methods,
+
+    loadData(value) {
+      const currentRequestId = ++requestId
+      this.loading = true
+      this.innerOptions = []
+      if (value == null || value.trim() === '') {
+        this.loading = false
+        this.hasRequest = false
+        return
+      }
+      // 字典code格式:table,text,code
+      this.hasRequest = true
+      getAction(`/sys/dict/loadDict/${this.dict}`, {keyword: value}).then(res => {
+        if (currentRequestId !== requestId) {
+          return
+        }
+        let {success, result, message} = res
+        if (success) {
+          this.innerOptions = result
+          result.forEach((item) => {
+            common.labelMap.set(item.value, [item])
+          })
+        } else {
+          this.$message.warning(message)
+        }
+      }).finally(() => {
+        this.loading = false
+      })
+    },
+
+    handleChange(selectedValue) {
+      this.innerSelectValue = selectedValue
+      this.handleChangeCommon(this.innerSelectValue)
+    },
+    handleSearch(value) {
+      if (this.isAsync) {
+        // 在输入时也应该开启加载,因为loadData加了消抖,所以会有800ms的用户主观上认为的卡顿时间
+        this.loading = true
+        if (this.innerOptions.length > 0) {
+          this.innerOptions = []
+        }
+        this.loadData(value)
+      }
+    },
+
+    renderOptionItem() {
+      let options = []
+      this.options.forEach(({value, text, label, title, disabled}) => {
+        options.push(
+          <a-select-option key={value} value={value} disabled={disabled}>{text || label || title}</a-select-option>
+        )
+      })
+      return options
+    },
+  },
+  render() {
+    return (
+      <a-select
+        showSearch
+        allowClear
+        value={this.innerSelectValue}
+        filterOption={this.filterOption}
+        style="width: 100%"
+        {...this.cellProps}
+        onSearch={this.handleSearch}
+        onChange={this.handleChange}
+        scopedSlots={this.scopedSlots}
+      >
+        {this.renderOptionItem()}
+      </a-select>
+    )
+  },
+  // 【组件增强】注释详见:JVxeCellMixins.js
+  enhanced: {
+    aopEvents: {
+      editActived(event) {
+        dispatchEvent.call(this, event, 'ant-select')
+      },
+    },
+  }
+}

+ 36 - 0
src/components/JVxeCells/install.js

@@ -0,0 +1,36 @@
+import { installCell, JVXETypes } from '@/components/jeecg/JVxeTable'
+import JVxePopupCell from './JVxePopupCell'
+import { DictSearchInputCell, DictSearchSpanCell } from './JVxeSelectDictSearchCell'
+import JVxeFileCell from './JVxeFileCell'
+import JVxeImageCell from './JVxeImageCell'
+import JVxeRadioCell from './JVxeRadioCell'
+import JVxeSelectCell from '@comp/jeecg/JVxeTable/components/cells/JVxeSelectCell'
+import JVxeTextareaCell from '@comp/jeecg/JVxeTable/components/cells/JVxeTextareaCell'
+
+// 注册online组件
+JVXETypes.input_pop = 'input_pop'
+JVXETypes.list_multi = 'list_multi'
+JVXETypes.sel_search = 'sel_search'
+installCell(JVXETypes.input_pop, JVxeTextareaCell)
+installCell(JVXETypes.list_multi, JVxeSelectCell)
+installCell(JVXETypes.sel_search, JVxeSelectCell)
+
+// 注册【popup】组件(普通封装方式)
+JVXETypes.popup = 'popup'
+installCell(JVXETypes.popup, JVxePopupCell)
+
+// 注册【字典搜索下拉】组件(高级封装方式)
+JVXETypes.selectDictSearch = 'select-dict-search'
+installCell(JVXETypes.selectDictSearch, DictSearchInputCell, DictSearchSpanCell)
+
+// 注册【文件上传】组件
+JVXETypes.file = 'file'
+installCell(JVXETypes.file, JVxeFileCell)
+
+// 注册【图片上传】组件
+JVXETypes.image = 'image'
+installCell(JVXETypes.image, JVxeImageCell)
+
+// 注册【单选框】组件
+JVXETypes.radio = 'radio'
+installCell(JVXETypes.radio, JVxeRadioCell)

+ 54 - 0
src/components/NumberInfo/NumberInfo.vue

@@ -0,0 +1,54 @@
+<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>

+ 3 - 0
src/components/NumberInfo/index.js

@@ -0,0 +1,3 @@
+import NumberInfo from './NumberInfo'
+
+export default NumberInfo

+ 55 - 0
src/components/NumberInfo/index.less

@@ -0,0 +1,55 @@
+@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;
+      }
+      :deep {
+        .anticon-caret-up {
+          color: @red-6;
+        }
+        .anticon-caret-down {
+          color: @green-6;
+        }
+      }
+    }
+  }
+}

+ 43 - 0
src/components/README.md

@@ -0,0 +1,43 @@
+####1._util包:存放自定义函数 详细见代码注释
+####2.AvatarList:显示头像群并支持tip,用法参考src\views\Home.vue(如下图)
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12181253_O0Xi.png "在这里输入图片标题")
+####3.chart包:存放各种图表相关的组件,条形图柱形图折线图等等 具体用法参考首页
+####4.countDown包:一个倒计时组件,用法参考home页,简单描述,该组件有3个属性,
+target(时间/毫秒数)必填,
+format(function,该方法接收一个毫秒数的参数,用于格式化显示当前倒计时时间)非必填,
+onEnd倒计时结束触发函数
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12182046_mwqJ.png "在这里输入图片标题")
+####5.dict包:数据字典专用,用法参考文件夹下readme文件
+####6.Ellipsis包:字符串截取组件,可以指定字符串的显示长度,并将全部内容显示到tip中,简单使用参考src\views\system\PermissionList.vue
+####7.jeecg包:该包下自定义了很多列表/表单中用到的组件 参考包下readme文件
+####8.jeecgbiz包:该包下定义了一些业务相关的组件,比如选择用户弹框,根据部门选择用户等等
+####9.layouts+page包:系统页面布局相关组件,比如登陆进去之后页面顶部显示什么,底部显示什么,菜单点击触发多个tab的布局等等 一般情况不需要修改
+####10.menun包:菜单组件,俩个,一个折叠菜单一个正常显示的菜单
+####11.NumberInfo:数字信息显示组件 如下图
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12185858_uvJ5.png "在这里输入图片标题")
+####12.online包:该包下封装了online表单的相关组件,用于展示表单各种控件,验证表单等等,相关用法参考readme
+####13.setting包:该包下封装了首页风格切换等功能如下图
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12190520_jySG.png "在这里输入图片标题")
+####14.table包:一个二次封装的table组件,用于展示列表,参考readme
+####15.tools包:
+Breadcrumb.vue:面包屑二次封装,支持路由跳转
+DetailList.vue:详情展示用法参考src\views\profile\advanced\Advanced.vue(效果如下图)
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12193954_Uar6.png "在这里输入图片标题")
+````
+个人认为该页面代码有两点值得学习:
+1.vue provide/inject的使用
+2.该页面css定义方式,只定义一个顶层class,其余样式都定义在其下,这样只要顶层class不和别的页面冲突,整个页面的样式都是唯一生效的
+````
+FooterToolBar.vue:fixed定位的底部,通过是否定义内部控件的属性slot="extra"决定是左浮动或是右浮动
+HeaderNotice.vue:首页通知(如下图)
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12195340_fPe0.png "在这里输入图片标题")
+HeaderInfo.vue:上下文字布局(如下图)
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12195638_dG5o.png "在这里输入图片标题")
+Logo.vue:首页左上侧的log图
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12200908_ihv3.png "在这里输入图片标题")
+UserMenu.vue:首页右上侧的内容
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12201226_laQK.png "在这里输入图片标题")
+####16.trend包 趋势显示组件(如下图)
+![输入图片说明](https://static.oschina.net/uploads/img/201904/12201600_Wo8K.png "在这里输入图片标题")
+![cron表达式](https://oscimg.oschina.net/oscnet/661f9ac09016395f9f49286143af3241623.jpg)
+![cron控件添加清除按钮](https://oscimg.oschina.net/oscnet/15096e49f2e29bd829e304d56770025d03c.jpg)

+ 41 - 0
src/components/Trend/Trend.vue

@@ -0,0 +1,41 @@
+<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>

+ 3 - 0
src/components/Trend/index.js

@@ -0,0 +1,3 @@
+import Trend from './Trend.vue'
+
+export default Trend

+ 42 - 0
src/components/Trend/index.less

@@ -0,0 +1,42 @@
+@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;
+  }
+}

+ 85 - 0
src/components/_util/Area.js

@@ -0,0 +1,85 @@
+import Vue from 'vue'
+/**
+ * 省市区
+ */
+export default class Area {
+  /**
+   * 构造器
+   * @param express
+   */
+  constructor(pcaa) {
+    if(!pcaa){
+      pcaa = Vue.prototype.$Jpcaa;
+    }
+    let arr = []
+    const province = pcaa['86']
+    Object.keys(province).map(key=>{
+      arr.push({id:key, text:province[key], pid:'86', index:1});
+      const city = pcaa[key];
+      Object.keys(city).map(key2=>{
+        arr.push({id:key2, text:city[key2], pid:key, index:2});
+        const qu = pcaa[key2];
+        if(qu){
+          Object.keys(qu).map(key3=>{
+            arr.push({id:key3, text:qu[key3], pid:key2, index:3});
+          })
+        }
+      })
+    })
+    this.all = arr;
+  }
+
+  get pca(){
+    return this.all;
+  }
+
+  getCode(text){
+    if(!text || text.length==0){
+      return ''
+    }
+    for(let item of this.all){
+      if(item.text === text){
+        return item.id;
+      }
+    }
+  }
+
+  getText(code){
+    if(!code || code.length==0){
+      return ''
+    }
+    let arr = []
+    this.getAreaBycode(code, arr, 3);
+    return arr.join('/')
+  }
+
+  getRealCode(code){
+    let arr = []
+    this.getPcode(code, arr, 3)
+    return arr;
+  }
+
+  getPcode(id, arr, index){
+    for(let item of this.all){
+      if(item.id === id && item.index == index){
+        arr.unshift(id)
+        if(item.pid != '86'){
+          this.getPcode(item.pid, arr, --index)
+        }
+      }
+    }
+  }
+
+  getAreaBycode(code, arr, index){
+    for(let item of this.all){
+      if(item.id === code && item.index == index){
+        arr.unshift(item.text);
+        if(item.pid != '86'){
+          this.getAreaBycode(item.pid, arr, --index)
+        }
+
+      }
+    }
+  }
+
+}

+ 46 - 0
src/components/_util/StringUtil.js

@@ -0,0 +1,46 @@
+/**
+ * 获取字符串的长度ascii长度为1 中文长度为2
+ * @param str
+ * @returns {number}
+ */
+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)
+
+/**
+ * 给定一个字符串和一个长度,将此字符串按指定长度截取
+ * @param str
+ * @param maxLength
+ * @returns {string}
+ */
+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
+  }, '')
+}
+
+// 下划线转换驼峰
+export function underLinetoHump(name) {
+  return name.replace(/\_(\w)/g, function(all, letter){
+    return letter.toUpperCase();
+  });
+}
+// 驼峰转换下划线
+export function humptoUnderLine(name) {
+  return name.replace(/([A-Z])/g,"_$1").toLowerCase();
+}

+ 12 - 0
src/components/_util/util.js

@@ -0,0 +1,12 @@
+/**
+ * components util
+ */
+
+/**
+ * 清理空值,对象
+ * @param children
+ * @returns {*[]}
+ */
+export function filterEmpty (children = []) {
+  return children.filter(c => c.tag || (c.text && c.text.trim() !== ''))
+}

+ 88 - 0
src/components/chart/AreaChartTy.vue

@@ -0,0 +1,88 @@
+<template>
+  <div :style="{ padding: '0' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+
+    <v-chart ref="chart" :forceFit="true" :height="height" :data="dataSource" :scale="scale">
+      <v-tooltip :shared="false"/>
+      <v-axis/>
+      <v-line position="x*y" :size="lineSize" :color="lineColor"/>
+      <v-area position="x*y" :color="color"/>
+    </v-chart>
+
+  </div>
+</template>
+
+<script>
+  import { triggerWindowResizeEvent } from '@/utils/util'
+
+  export default {
+    name: 'AreaChartTy',
+    props: {
+      // 图表数据
+      dataSource: {
+        type: Array,
+        required: true
+      },
+      // 图表标题
+      title: {
+        type: String,
+        default: ''
+      },
+      // x 轴别名
+      x: {
+        type: String,
+        default: 'x'
+      },
+      // y 轴别名
+      y: {
+        type: String,
+        default: 'y'
+      },
+      // Y轴最小值
+      min: {
+        type: Number,
+        default: 0
+      },
+      // Y轴最大值
+      max: {
+        type: Number,
+        default: null
+      },
+      // 图表高度
+      height: {
+        type: Number,
+        default: 254
+      },
+      // 线的粗细
+      lineSize: {
+        type: Number,
+        default: 2
+      },
+      // 面积的颜色
+      color: {
+        type: String,
+        default: ''
+      },
+      // 线的颜色
+      lineColor: {
+        type: String,
+        default: ''
+      }
+    },
+    computed: {
+      scale() {
+        return [
+          { dataKey: 'x', title: this.x, alias: this.x },
+          { dataKey: 'y', title: this.y, alias: this.y, min: this.min, max: this.max }
+        ]
+      }
+    },
+    mounted() {
+      triggerWindowResizeEvent()
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  @import "chart";
+</style>

+ 50 - 0
src/components/chart/Bar.vue

@@ -0,0 +1,50 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart :forceFit="true" :height="height" :data="dataSource" :scale="scale" :padding="padding">
+      <v-tooltip/>
+      <v-axis/>
+      <v-bar position="x*y"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import { triggerWindowResizeEvent } from '@/utils/util'
+
+  export default {
+    name: 'Bar',
+    props: {
+      dataSource: {
+        type: Array,
+        required: true
+      },
+      yaxisText: {
+        type: String,
+        default: 'y'
+      },
+      title: {
+        type: String,
+        default: ''
+      },
+      height: {
+        type: Number,
+        default: 254
+      }
+    },
+    data() {
+      return { padding: ['auto', 'auto', '40', '50'] }
+    },
+    computed: {
+      scale() {
+        return [{
+          dataKey: 'y',
+          alias: this.yaxisText
+        }]
+      }
+    },
+    mounted() {
+      triggerWindowResizeEvent()
+    }
+  }
+</script>

+ 60 - 0
src/components/chart/BarAndLine.vue

@@ -0,0 +1,60 @@
+<template>
+  <div :style="{ padding: '0 50px 32px 0' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :padding=" padding" :onClick="handleClick">
+      <v-tooltip/>
+      <v-legend/>
+      <v-axis/>
+      <v-bar position="type*bar"/>
+      <v-line position="type*line" color="#2fc25b" :size="3"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import { ChartEventMixins } from './mixins/ChartMixins'
+
+  export default {
+    name: 'BarAndLine',
+    mixins: [ChartEventMixins],
+    props: {
+      title: {
+        type: String,
+        default: ''
+      },
+      dataSource: {
+        type: Array,
+        default: () => [
+          { type: '10:10', bar: 200, line: 1000 },
+          { type: '10:15', bar: 600, line: 1000},
+          { type: '10:20', bar: 200, line: 1000},
+          { type: '10:25', bar: 900, line: 1000},
+          { type: '10:30', bar: 200, line: 1000},
+          { type: '10:35', bar: 200, line: 1000},
+          { type: '10:40', bar: 100, line: 1000}
+        ]
+      },
+      height: {
+        type: Number,
+        default: 400
+      }
+    },
+    data() {
+      return {
+        padding: { top:50, right:50, bottom:100, left:50 },
+        scale: [{
+          dataKey: 'bar',
+          min: 0
+        }, {
+          dataKey: 'line',
+          min: 0
+        }]
+      }
+    },
+    computed: {
+      data() {
+        return this.dataSource
+      }
+    }
+  }
+</script>

+ 96 - 0
src/components/chart/BarMultid.vue

@@ -0,0 +1,96 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart :data="data" :height="height" :force-fit="true" :scale="scale" :onClick="handleClick">
+      <v-tooltip/>
+      <v-axis/>
+      <v-legend/>
+      <v-bar position="x*y" color="type" :adjust="adjust"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import { DataSet } from '@antv/data-set'
+  import { ChartEventMixins } from './mixins/ChartMixins'
+
+  export default {
+    name: 'BarMultid',
+    mixins: [ChartEventMixins],
+    props: {
+      title: {
+        type: String,
+        default: ''
+      },
+      dataSource: {
+        type: Array,
+        default: () => [
+          { type: 'Jeecg', 'Jan.': 18.9, 'Feb.': 28.8, 'Mar.': 39.3, 'Apr.': 81.4, 'May': 47, 'Jun.': 20.3, 'Jul.': 24, 'Aug.': 35.6 },
+          { type: 'Jeebt', 'Jan.': 12.4, 'Feb.': 23.2, 'Mar.': 34.5, 'Apr.': 99.7, 'May': 52.6, 'Jun.': 35.5, 'Jul.': 37.4, 'Aug.': 42.4 }
+        ]
+      },
+      fields: {
+        type: Array,
+        default: () => ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.']
+      },
+      // 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
+      aliases: {
+        type: Array,
+        default: () => []
+      },
+      height: {
+        type: Number,
+        default: 254
+      }
+    },
+    data() {
+      return {
+        adjust: [{
+          type: 'dodge',
+          marginRatio: 1 / 32
+        }]
+      }
+    },
+    computed: {
+      data() {
+        const dv = new DataSet.View().source(this.dataSource)
+        dv.transform({
+          type: 'fold',
+          fields: this.fields,
+          key: 'x',
+          value: 'y'
+        })
+
+        // bar 使用不了 - 和 / 所以替换下
+        let rows = dv.rows.map(row => {
+          if (typeof row.x === 'string') {
+            row.x = row.x.replace(/[-/]/g, '_')
+          }
+          return row
+        })
+        // 替换别名
+        rows.forEach(row => {
+          for (let item of this.aliases) {
+            if (item.field === row.type) {
+              row.type = item.alias
+              break
+            }
+          }
+        })
+        return rows
+      },
+      scale() {
+        return [
+          {
+            type: 'cat',
+            dataKey: 'x'
+          }
+        ]
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 187 - 0
src/components/chart/DashChartDemo.vue

@@ -0,0 +1,187 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <v-chart :forceFit="true" :height="300" :data="chartData" :scale="scale">
+      <v-coord type="polar" :startAngle="-202.5" :endAngle="22.5" :radius="0.75"></v-coord>
+      <v-axis
+        dataKey="value"
+        :zIndex="2"
+        :line="null"
+        :label="axisLabel"
+        :subTickCount="4"
+        :subTickLine="axisSubTickLine"
+        :tickLine="axisTickLine"
+        :grid="null"
+      ></v-axis>
+      <v-axis dataKey="1" :show="false"></v-axis>
+      <v-series
+        gemo="point"
+        position="value*1"
+        shape="pointer"
+        color="#1890FF"
+        :active="false"
+      ></v-series>
+      <v-guide
+        type="arc"
+        :zIndex="0"
+        :top="false"
+        :start="arcGuide1Start"
+        :end="arcGuide1End"
+        :vStyle="arcGuide1Style"
+      ></v-guide>
+      <v-guide
+        type="arc"
+        :zIndex="1"
+        :start="arcGuide2Start"
+        :end="getArcGuide2End"
+        :vStyle="arcGuide2Style"
+      ></v-guide>
+      <v-guide
+        type="html"
+        :position="htmlGuidePosition"
+        :html="getHtmlGuideHtml()"
+      ></v-guide>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import { registerShape } from 'viser-vue';
+
+  registerShape('point', 'pointer', {
+    draw(cfg, container) {
+      let point = cfg.points[0];
+      point = this.parsePoint(point);
+      const center = this.parsePoint({
+        x: 0,
+        y: 0,
+      });
+      container.addShape('line', {
+        attrs: {
+          x1: center.x,
+          y1: center.y,
+          x2: point.x,
+          y2: point.y + 15,
+          stroke: cfg.color,
+          lineWidth: 5,
+          lineCap: 'round',
+        }
+      });
+      return container.addShape('circle', {
+        attrs: {
+          x: center.x,
+          y: center.y,
+          r: 9.75,
+          stroke: cfg.color,
+          lineWidth: 4.5,
+          fill: '#fff',
+        }
+      });
+    }
+  });
+
+  const scale = [{
+    dataKey: 'value',
+    min: 0,
+    max: 9,
+    tickInterval: 1,
+    nice: false,
+  }];
+
+  const data = [
+    { value: 7.0 },
+  ];
+
+  export default {
+    name:"DashChartDemo",
+    props:{
+      datasource:{
+        type: Number,
+        default:7
+      },
+      title: {
+        type: String,
+        default: ''
+      }
+    },
+    created(){
+      if(!this.datasource){
+        this.chartData = data;
+      }else{
+        this.chartData = [
+          { value: this.datasource },
+        ];
+      }
+      this.getChartData()
+    },
+    watch: {
+      'datasource': function (val) {
+        this.chartData = [
+          { value: val},
+        ];
+        this.getChartData();
+      }
+    },
+    methods:{
+      getChartData(){
+        if(this.chartData && this.chartData.length>0){
+          this.abcd = this.chartData[0].value * 10
+        }else{
+          this.abcd = 70
+        }
+      },
+      getHtmlGuideHtml(){
+        return '<div style="width: 300px;text-align: center;">\n' +
+          '<p style="font-size: 14px;color: #545454;margin: 0;">'+this.title+'</p>\n' +
+          '<p style="font-size: 36px;color: #545454;margin: 0;">'+this.abcd+'%</p>\n' +
+          '</div>'
+      },
+      getArcGuide2End(){
+        return [this.chartData[0].value, 0.945]
+      }
+    },
+    data() {
+      return {
+        chartData:[],
+        height: 400,
+        scale: scale,
+        abcd:70,
+        axisLabel: {
+          offset: -16,
+          textStyle: {
+            fontSize: 18,
+            textAlign: 'center',
+            textBaseline: 'middle'
+          }
+        },
+        axisSubTickLine: {
+          length: -8,
+          stroke: '#fff',
+          strokeOpacity: 1,
+        },
+        axisTickLine: {
+          length: -17,
+          stroke: '#fff',
+          strokeOpacity: 1,
+        },
+        arcGuide1Start: [0, 0.945],
+        arcGuide1End: [9, 0.945],
+        arcGuide1Style: {
+          stroke: '#CBCBCB',
+          lineWidth: 18,
+        },
+        arcGuide2Start: [0, 0.945],
+        arcGuide2Style: {
+          stroke: '#1890FF',
+          lineWidth: 18,
+        },
+        htmlGuidePosition: ['50%', '100%'],
+        htmlGuideHtml: `
+        <div style="width: 300px;text-align: center;">
+          <p style="font-size: 14px;color: #545454;margin: 0;">${this.title}</p>
+          <p style="font-size: 36px;color: #545454;margin: 0;">${this.abcd}%</p>
+        </div>
+      `,
+      };
+    },
+  };
+</script>

+ 61 - 0
src/components/chart/IndexBar.vue

@@ -0,0 +1,61 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart
+      height="254"
+      :data="datasource"
+      :forceFit="true"
+      :padding="['auto', 'auto', '40', '50']">
+      <v-tooltip />
+      <v-axis />
+      <v-bar position="x*y"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+
+  const data = []
+  for (let i = 0; i < 12; i += 1) {
+    data.push({
+      x: `${i + 1}月`,
+      y: Math.floor(Math.random() * 1000) + 200
+    })
+  }
+  const tooltip = [
+    'x*y',
+    (x, y) => ({
+      name: x,
+      value: y
+    })
+  ]
+  const scale = [{
+    dataKey: 'x',
+    min: 2
+  }, {
+    dataKey: 'y',
+    title: '时间',
+    min: 1,
+    max: 22
+  }]
+
+  export default {
+    name: "Bar",
+    props: {
+      title: {
+        type: String,
+        default: ''
+      }
+    },
+    mounted(){
+      this.datasource = data
+    },
+    data () {
+      return {
+        datasource:[],
+        scale,
+        tooltip
+      }
+    }
+  }
+</script>

+ 95 - 0
src/components/chart/LineChartMultid.vue

@@ -0,0 +1,95 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
+      <v-tooltip/>
+      <v-axis/>
+      <v-legend/>
+      <v-line position="type*y" color="x"/>
+      <v-point position="type*y" color="x" :size="4" :v-style="style" :shape="'circle'"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import { DataSet } from '@antv/data-set'
+  import { ChartEventMixins } from './mixins/ChartMixins'
+
+  export default {
+    name: 'LineChartMultid',
+    mixins: [ChartEventMixins],
+    props: {
+      title: {
+        type: String,
+        default: ''
+      },
+      dataSource: {
+        type: Array,
+        default: () => [
+          { type: 'Jan', jeecg: 7.0, jeebt: 3.9 },
+          { type: 'Feb', jeecg: 6.9, jeebt: 4.2 },
+          { type: 'Mar', jeecg: 9.5, jeebt: 5.7 },
+          { type: 'Apr', jeecg: 14.5, jeebt: 8.5 },
+          { type: 'May', jeecg: 18.4, jeebt: 11.9 },
+          { type: 'Jun', jeecg: 21.5, jeebt: 15.2 },
+          { type: 'Jul', jeecg: 25.2, jeebt: 17.0 },
+          { type: 'Aug', jeecg: 26.5, jeebt: 16.6 },
+          { type: 'Sep', jeecg: 23.3, jeebt: 14.2 },
+          { type: 'Oct', jeecg: 18.3, jeebt: 10.3 },
+          { type: 'Nov', jeecg: 13.9, jeebt: 6.6 },
+          { type: 'Dec', jeecg: 9.6, jeebt: 4.8 }
+        ]
+      },
+      fields: {
+        type: Array,
+        default: () => ['jeecg', 'jeebt']
+      },
+      // 别名,需要的格式:[{field:'name',alias:'姓名'}, {field:'sex',alias:'性别'}]
+      aliases:{
+        type: Array,
+        default: () => []
+      },
+      height: {
+        type: Number,
+        default: 254
+      }
+    },
+    data() {
+      return {
+        scale: [{
+          type: 'cat',
+          dataKey: 'x',
+          min: 0,
+          max: 1
+        }],
+        style: { stroke: '#fff', lineWidth: 1 }
+      }
+    },
+    computed: {
+      data() {
+        const dv = new DataSet.View().source(this.dataSource)
+        dv.transform({
+          type: 'fold',
+          fields: this.fields,
+          key: 'x',
+          value: 'y'
+        })
+        let rows =  dv.rows
+        // 替换别名
+        rows.forEach(row => {
+          for (let item of this.aliases) {
+            if (item.field === row.x) {
+              row.x = item.alias
+              break
+            }
+          }
+        })
+        return rows
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 80 - 0
src/components/chart/Liquid.vue

@@ -0,0 +1,80 @@
+<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: 8,
+          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>
+
+  const sourceDataConst = [
+    { transfer: '一月', value: 813 },
+    { transfer: '二月', value: 233 },
+    { transfer: '三月', value: 561 }
+  ]
+
+  export default {
+    name: 'Liquid',
+    props: {
+      height: {
+        type: Number,
+        default: 0
+      },
+      width: {
+        type: Number,
+        default: 0
+      }
+    },
+    data() {
+      return {
+        data: sourceDataConst,
+        scale: []
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 69 - 0
src/components/chart/MiniArea.vue

@@ -0,0 +1,69 @@
+<template>
+  <div class="antv-chart-mini">
+    <div class="chart-wrapper" :style="{ height: 46 }">
+      <v-chart :force-fit="true" :height="height" :data="data" :scale="scale" :padding="[36, 0, 18, 0]">
+        <v-tooltip/>
+        <v-smooth-area position="x*y"/>
+      </v-chart>
+    </div>
+  </div>
+</template>
+
+<script>
+  import moment from 'dayjs'
+
+  const sourceData = []
+  const beginDay = new Date().getTime()
+
+  for (let i = 0; i < 10; i++) {
+    sourceData.push({
+      x: moment(new Date(beginDay + 1000 * 60 * 60 * 24 * i)).format('YYYY-MM-DD'),
+      y: Math.round(Math.random() * 10)
+    })
+  }
+
+  export default {
+    name: 'MiniArea',
+    props: {
+      dataSource: {
+        type: Array,
+        default: () => []
+      },
+      // x 轴别名
+      x: {
+        type: String,
+        default: 'x'
+      },
+      // y 轴别名
+      y: {
+        type: String,
+        default: 'y'
+      }
+    },
+    data() {
+      return {
+        data: [],
+        height: 100
+      }
+    },
+    computed: {
+      scale() {
+        return [
+          { dataKey: 'x', title: this.x, alias: this.x },
+          { dataKey: 'y', title: this.y, alias: this.y }
+        ]
+      }
+    },
+    created() {
+      if (this.dataSource.length === 0) {
+        this.data = sourceData
+      } else {
+        this.data = this.dataSource
+      }
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  @import "chart";
+</style>

+ 76 - 0
src/components/chart/MiniBar.vue

@@ -0,0 +1,76 @@
+<template>
+  <div :style="{'width':width==null?'auto':width+'px'}">
+    <v-chart :forceFit="width==null" :height="height" :data="data" padding="0">
+      <v-tooltip/>
+      <v-bar position="x*y"/>
+    </v-chart>
+  </div>
+</template>
+
+<script>
+  import moment from 'dayjs'
+
+  const sourceData = []
+  const beginDay = new Date().getTime()
+
+  for (let i = 0; i < 10; i++) {
+    sourceData.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',
+    props: {
+      dataSource: {
+        type: Array,
+        default: () => []
+      },
+      width: {
+        type: Number,
+        default: null
+      },
+      height: {
+        type: Number,
+        default: 200
+      }
+    },
+    created() {
+      if (this.dataSource.length === 0) {
+        this.data = sourceData
+      } else {
+        this.data = this.dataSource
+      }
+    },
+    data() {
+      return {
+        tooltip,
+        data: [],
+        scale
+      }
+    }
+  }
+</script>
+
+<style lang="less" scoped>
+  @import "chart";
+</style>

+ 75 - 0
src/components/chart/MiniProgress.vue

@@ -0,0 +1,75 @@
+<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+'px' }"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'MiniProgress',
+    props: {
+      target: {
+        type: Number,
+        default: 0
+      },
+      height: {
+        type: Number,
+        default: 10
+      },
+      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>

+ 70 - 0
src/components/chart/Pie.vue

@@ -0,0 +1,70 @@
+<template>
+  <v-chart :forceFit="true" :height="height" :data="data" :scale="scale" :onClick="handleClick">
+    <v-tooltip :showTitle="false" dataKey="item*percent"/>
+    <v-axis/>
+    <v-legend dataKey="item"/>
+    <v-pie position="percent" color="item" :v-style="pieStyle" :label="labelConfig"/>
+    <v-coord type="theta"/>
+  </v-chart>
+</template>
+
+<script>
+  const DataSet = require('@antv/data-set')
+  import { ChartEventMixins } from './mixins/ChartMixins'
+
+  export default {
+    name: 'Pie',
+    mixins: [ChartEventMixins],
+    props: {
+      title: {
+        type: String,
+        default: ''
+      },
+      height: {
+        type: Number,
+        default: 254
+      },
+      dataSource: {
+        type: Array,
+        default: () => [
+          { item: '示例一', count: 40 },
+          { item: '示例二', count: 21 },
+          { item: '示例三', count: 17 },
+          { item: '示例四', count: 13 },
+          { item: '示例五', count: 9 }
+        ]
+      }
+    },
+    data() {
+      return {
+        scale: [{
+          dataKey: 'percent',
+          min: 0,
+          formatter: '.0%'
+        }],
+        pieStyle: {
+          stroke: '#fff',
+          lineWidth: 1
+        },
+        labelConfig: ['percent', {
+          formatter: (val, item) => {
+            return item.point.item + ': ' + val
+          }
+        }]
+      }
+    },
+    computed: {
+      data() {
+        let dv = new DataSet.View().source(this.dataSource)
+        // 计算数据百分比
+        dv.transform({
+          type: 'percent',
+          field: 'count',
+          dimension: 'item',
+          as: 'percent'
+        })
+        return dv.rows
+      }
+    }
+  }
+</script>

+ 367 - 0
src/components/chart/README.md

@@ -0,0 +1,367 @@
+# 报表组件文档
+
+## 柱状图
+
+##### 引用方式
+
+```js 
+import Bar from '@/components/chart/Bar'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| title      | string |    | 报表标题       |
+| dataSource | array  | ✔️ | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+
+##### dataSource 示例
+
+```json
+[
+    {
+        "x": "1月",
+        "y": 320
+    },
+    {
+        "x": "2月",
+        "y": 457
+    },
+    {
+        "x": "3月",
+        "y": 182
+    }
+]
+```
+
+##### 代码示例
+
+```html
+<template>
+    <bar title="柱状图" :dataSource="dataSource" :height="420"/>
+</template>
+
+<script>
+    import Bar from '@/components/chart/Bar'
+
+    export default {
+        name: 'ChartDemo',
+        components: {
+            Bar
+        },
+        data() {
+            return {
+                dataSource: [
+                    {
+                        "x": "1月",
+                        "y": 320
+                    },
+                    {
+                        "x": "2月",
+                        "y": 457
+                    },
+                    {
+                        "x": "3月",
+                        "y": 182
+                    }
+                ]
+            }
+        }
+    }
+</script>
+
+<style></style>
+```
+
+## 多列柱状图
+
+##### 引用方式
+
+```js 
+import BarMultid from '@/components/chart/BarMultid'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| title      | string |    | 报表标题       |
+| fields     | array  |    | 主列字段列表     |
+| dataSource | array  |    | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+
+##### fields 示例
+
+```json
+["Jan.", "Feb.", "Mar.", "Apr.", "May", "Jun.", "Jul.", "Aug."]
+```
+
+##### dataSource 示例
+
+```json
+[
+    {
+        "type": "Jeecg", // 列名
+        "Jan.": 18.9,
+        "Feb.": 28.8,
+        "Mar.": 39.3,
+        "Apr.": 81.4,
+        "May": 47,
+        "Jun.": 20.3,
+        "Jul.": 24,
+        "Aug.": 35.6
+    },
+    {
+        "type": "Jeebt",
+        "Jan.": 12.4,
+        "Feb.": 23.2,
+        "Mar.": 34.5,
+        "Apr.": 99.7,
+        "May": 52.6,
+        "Jun.": 35.5,
+        "Jul.": 37.4,
+        "Aug.": 42.4
+    }
+]
+```
+
+## 迷你柱状图
+
+不带标题和数据轴的柱状图
+
+##### 引用方式
+
+```js 
+import MiniBar from '@/components/chart/MiniBar'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明            |
+|------------|--------|----|---------------|
+| width      | number |    | 报表宽度度,默认自适应宽度 |
+| height     | number |    | 报表高度,默认200    |
+| dataSource | array  |    | 报表数据源         |
+
+##### dataSource 示例
+
+```json
+[
+    {
+        "x": "1月",
+        "y": 320
+    },
+    {
+        "x": "2月",
+        "y": 457
+    },
+    {
+        "x": "3月",
+        "y": 182
+    }
+]
+```
+
+## 面积图
+
+##### 引用方式
+
+```js 
+import AreaChartTy from '@/components/chart/AreaChartTy'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| title      | string |    | 报表标题       |
+| dataSource | array  | ✔️ | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+| lineSize   | number |    | 线的粗细,默认2   |
+
+##### dataSource 示例
+
+```json
+[
+    {
+        "x": "1月",
+        "y": 320
+    },
+    {
+        "x": "2月",
+        "y": 457
+    },
+    {
+        "x": "3月",
+        "y": 182
+    }
+]
+```
+
+## 多行折线图
+
+##### 引用方式
+
+```js 
+import LineChartMultid from '@/components/chart/LineChartMultid'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| title      | string |    | 报表标题       |
+| fields     | array  |    | 主列字段列表     |
+| dataSource | array  |    | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+
+##### fields 示例
+
+```json
+["jeecg", "jeebt"]
+```
+
+##### dataSource 示例
+
+```json
+[
+    {
+        "type": "Jan", // 列名
+        "jeecg": 7,
+        "jeebt": 3.9
+    },
+    { "type": "Feb", "jeecg": 6.9, "jeebt": 4.2 },
+    { "type": "Mar", "jeecg": 9.5, "jeebt": 5.7 },
+    { "type": "Apr", "jeecg": 14.5, "jeebt": 8.5 },
+    { "type": "May", "jeecg": 18.4, "jeebt": 11.9 },
+    { "type": "Jun", "jeecg": 21.5, "jeebt": 15.2 },
+    { "type": "Jul", "jeecg": 25.2, "jeebt": 17 },
+    { "type": "Aug", "jeecg": 26.5, "jeebt": 16.6 },
+    { "type": "Sep", "jeecg": 23.3, "jeebt": 14.2 },
+    { "type": "Oct", "jeecg": 18.3, "jeebt": 10.3 },
+    { "type": "Nov", "jeecg": 13.9, "jeebt": 6.6 },
+    { "type": "Dec", "jeecg": 9.6, "jeebt": 4.8 }
+]
+```
+
+## 饼状图
+
+##### 引用方式
+
+```js 
+import Pie from '@/components/chart/Pie'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| dataSource | array  |    | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+
+##### dataSource 示例
+
+```json
+[
+    // 所有的 percent 相加等于 100
+    { "item": "一月", "percent": 40 },
+    { "item": "二月", "percent": 21 },
+    { "item": "三月", "percent": 17 },
+    { "item": "四月", "percent": 13 },
+    { "item": "五月", "percent": 9 }
+]
+```
+
+## 雷达图
+
+##### 引用方式
+
+```js 
+import Radar from '@/components/chart/Radar'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明         |
+|------------|--------|----|------------|
+| dataSource | array  |    | 报表数据源      |
+| height     | number |    | 报表高度,默认254 |
+
+##### dataSource 示例
+
+```json
+[
+    // score 最小值为 0,最大值为 100
+    { "item": "一月", "score": 40 },
+    { "item": "二月", "score": 20 },
+    { "item": "三月", "score": 67 },
+    { "item": "四月", "score": 43 },
+    { "item": "五月", "score": 90 }
+]
+```
+
+## 进度条
+
+##### 引用方式
+
+```js 
+import MiniProgress from '@/components/chart/MiniProgress'
+```
+
+##### 参数列表
+
+| 参数名        | 类型     | 必填 | 说明                |
+|------------|--------|----|-------------------|
+| percentage | number |    | 当前进度百分比,默认0,最高100 |
+| target     | number |    | 目标值,默认10          |
+| height     | number |    | 进度条高度,默认10        |
+| color      | string |    | 进度条颜色,默认 #13C2C2  |
+
+## 仪表盘
+
+##### 引用方式
+
+```js 
+import DashChartDemo from '@/components/chart/DashChartDemo'
+```
+
+##### 参数列表
+
+| 参数名    | 类型     | 必填 | 说明             |
+|--------|--------|----|----------------|
+| title  | string |    | 报表标题           |
+| value  | number |    | 当前值,默认6.7,最大为9 |
+| height | number |    | 报表高度,默认254     |
+
+## 排名列表
+
+##### 引用方式
+
+```js 
+import RankList from '@/components/chart/RankList'
+```
+
+##### 参数列表
+
+| 参数名    | 类型     | 必填 | 说明           |
+|--------|--------|----|--------------|
+| title  | string |    | 报表标题         |
+| list   | array  |    | 排名列表数据       |
+| height | number |    | 报表高度,默认自适应高度 |
+
+##### list 示例
+
+```json
+[
+    {
+        "name": "北京朝阳 1 号店",
+        "total": 1981
+    },
+    { "name": "北京朝阳 2 号店", "total": 1359 },
+    { "name": "北京朝阳 3 号店", "total": 1354 },
+    { "name": "北京朝阳 4 号店", "total": 263 },
+    { "name": "北京朝阳 5 号店", "total": 446 },
+    { "name": "北京朝阳 6 号店", "total": 796 }
+]
+```

+ 90 - 0
src/components/chart/Radar.vue

@@ -0,0 +1,90 @@
+<template>
+  <v-chart :forceFit="true" :height="height" :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: 100
+    }, {
+      dataKey: 'user',
+      alias: '类型'
+    }
+  ]
+
+  const sourceData = [
+    { item: '示例一', score: 40 },
+    { item: '示例二', score: 20 },
+    { item: '示例三', score: 67 },
+    { item: '示例四', score: 43 },
+    { item: '示例五', score: 90 }
+  ]
+
+  export default {
+    name: 'Radar',
+    props: {
+      height: {
+        type: Number,
+        default: 254
+      },
+      dataSource: {
+        type: Array,
+        default: () => []
+      }
+    },
+    data() {
+      return {
+        axis1Opts,
+        axis2Opts,
+        scale,
+        data: sourceData
+      }
+    },
+    watch: {
+      dataSource(newVal) {
+        if (newVal.length === 0) {
+          this.data = sourceData
+        } else {
+          this.data = newVal
+        }
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 81 - 0
src/components/chart/RankList.vue

@@ -0,0 +1,81 @@
+<template>
+  <div class="rank">
+    <h4 class="title">{{ title }}</h4>
+    <ul class="list" :style="{height:height?`${height}px`:'auto',overflow:'auto'}">
+      <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
+      },
+      height: {
+        type: Number,
+        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>

+ 54 - 0
src/components/chart/StackBar.vue

@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <v-chart :forceFit="true" :height="height" :data="data">
+      <v-coord type="rect" direction="LB" />
+      <v-tooltip />
+      <v-legend />
+      <v-axis dataKey="State" :label="label" />
+      <v-stack-bar position="State*流程数量"  color="流程状态" />
+    </v-chart>
+  </div>
+
+</template>
+
+<script>
+  const DataSet = require('@antv/data-set');
+
+  export default {
+    name: 'StackBar',
+    props: {
+      dataSource: {
+        type: Array,
+        required: true,
+        default: () => [
+          { 'State': '请假', '流转中': 25, '已归档': 18 },
+          { 'State': '出差', '流转中': 30, '已归档': 20 },
+          { 'State': '加班', '流转中': 38, '已归档': 42},
+          { 'State': '用车', '流转中': 51, '已归档': 67}
+        ]
+      },
+      height: {
+        type: Number,
+        default: 254
+      }
+    },
+    data() {
+      return {
+        label: { offset: 12 }
+      }
+    },
+    computed: {
+      data() {
+        const dv = new DataSet.View().source(this.dataSource);
+        dv.transform({
+          type: 'fold',
+          fields: ['流转中', '已归档'],
+          key: '流程状态',
+          value: '流程数量',
+          retains: ['State'],
+        });
+       return dv.rows;
+      }
+    }
+  }
+</script>

+ 66 - 0
src/components/chart/TransferBar.vue

@@ -0,0 +1,66 @@
+<template>
+  <div :style="{ padding: '0 0 32px 32px' }">
+    <h4 :style="{ marginBottom: '20px' }">{{ title }}</h4>
+    <v-chart
+      :height="height"
+      :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>
+
+  export default {
+    name: 'Bar',
+    props: {
+      title: {
+        type: String,
+        default: ''
+      },
+      x: {
+        type: String,
+        default: 'x'
+      },
+      y: {
+        type: String,
+        default: 'y'
+      },
+      data: {
+        type: Array,
+        default: () => []
+      },
+      height: {
+        type: Number,
+        default: 254
+      }
+    },
+    data() {
+      return {}
+    },
+    computed: {
+      scale() {
+        return [
+          { dataKey: 'x', title: this.x, alias: this.x },
+          { dataKey: 'y', title: this.y, alias: this.y }
+        ]
+      }
+    },
+    created() {
+      // this.getMonthBar()
+    },
+    methods: {
+      // getMonthBar() {
+      //   this.$http.get('/analysis/month-bar')
+      //     .then(res => {
+      //       this.data = res.result
+      //     })
+      // }
+    }
+  }
+</script>

+ 84 - 0
src/components/chart/Trend.vue

@@ -0,0 +1,84 @@
+<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: {
+      // 同title
+      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 () {
+      let 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>

+ 13 - 0
src/components/chart/chart.less

@@ -0,0 +1,13 @@
+.antv-chart-mini {
+  position: relative;
+  width: 100%;
+
+  .chart-wrapper {
+    position: absolute;
+    bottom: -28px;
+    width: 100%;
+
+/*    margin: 0 -5px;
+    overflow: hidden;*/
+  }
+}

+ 10 - 0
src/components/chart/mixins/ChartMixins.js

@@ -0,0 +1,10 @@
+export const ChartEventMixins = {
+  methods: {
+    handleClick(event, chart) {
+      this.handleEvent('click', event, chart)
+    },
+    handleEvent(eventName, event, chart) {
+      this.$emit(eventName, event, chart)
+    },
+  }
+}

+ 0 - 0
src/components/dict/JDictSelectTag.vue


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