my 3 年之前
當前提交
dee8bf47d4
共有 100 個文件被更改,包括 8794 次插入0 次删除
  1. 46 0
      .gitignore
  2. 3 0
      live-web/.browserslistrc
  3. 15 0
      live-web/.editorconfig
  4. 16 0
      live-web/.eslintrc.js
  5. 29 0
      live-web/.gitignore
  6. 5 0
      live-web/.postcssrc.js
  7. 202 0
      live-web/LICENSE
  8. 141 0
      live-web/README.md
  9. 5 0
      live-web/babel.config.js
  10. 9 0
      live-web/docker/Dockerfile
  11. 19 0
      live-web/docker/docker-compose.yaml
  12. 19 0
      live-web/docker/pig-ui.conf
  13. 53 0
      live-web/package.json
  14. 11 0
      live-web/public/cdn/animate/3.5.2/animate.css
  15. 54 0
      live-web/public/cdn/avue/avue.css
  16. 193 0
      live-web/public/cdn/store/1.3.20/store.js
  17. 二進制
      live-web/public/favicon.ico
  18. 二進制
      live-web/public/img/logo.png
  19. 41 0
      live-web/public/index.html
  20. 6 0
      live-web/public/svg/loading-spin.svg
  21. 25 0
      live-web/src/App.vue
  22. 56 0
      live-web/src/api/admin/client.js
  23. 72 0
      live-web/src/api/admin/dept.js
  24. 101 0
      live-web/src/api/admin/dict.js
  25. 57 0
      live-web/src/api/admin/log.js
  26. 80 0
      live-web/src/api/admin/match/user.js
  27. 56 0
      live-web/src/api/admin/matchwebfrontmenu.js
  28. 64 0
      live-web/src/api/admin/menu.js
  29. 81 0
      live-web/src/api/admin/role.js
  30. 56 0
      live-web/src/api/admin/sysform.js
  31. 33 0
      live-web/src/api/admin/token.js
  32. 79 0
      live-web/src/api/admin/user.js
  33. 33 0
      live-web/src/api/es.js
  34. 56 0
      live-web/src/api/gen/form.js
  35. 118 0
      live-web/src/api/gen/gen.js
  36. 59 0
      live-web/src/api/login.js
  37. 132 0
      live-web/src/api/match/competition.js
  38. 107 0
      live-web/src/api/match/createMatch.js
  39. 102 0
      live-web/src/api/match/judges.js
  40. 115 0
      live-web/src/api/match/mySubMatch.js
  41. 50 0
      live-web/src/api/match/project.js
  42. 125 0
      live-web/src/api/match/rounds.js
  43. 18 0
      live-web/src/api/match/rule.js
  44. 10 0
      live-web/src/api/match/scene.js
  45. 48 0
      live-web/src/api/match/score.js
  46. 115 0
      live-web/src/api/match/second/match.js
  47. 115 0
      live-web/src/api/match/subMatch.js
  48. 10 0
      live-web/src/api/match/track.js
  49. 79 0
      live-web/src/api/match/website.js
  50. 33 0
      live-web/src/api/media.js
  51. 40 0
      live-web/src/components/basic-container/main.vue
  52. 193 0
      live-web/src/components/editor/LICENSE
  53. 78 0
      live-web/src/components/editor/index.vue
  54. 129 0
      live-web/src/components/error-page/403.vue
  55. 91 0
      live-web/src/components/error-page/404.vue
  56. 112 0
      live-web/src/components/error-page/500.vue
  57. 296 0
      live-web/src/components/expert/change.vue
  58. 288 0
      live-web/src/components/expert/expert.vue
  59. 129 0
      live-web/src/components/iframe/main.vue
  60. 16 0
      live-web/src/config/env.js
  61. 135 0
      live-web/src/const/crud/admin/client.js
  62. 125 0
      live-web/src/const/crud/admin/dict.js
  63. 73 0
      live-web/src/const/crud/admin/log.js
  64. 288 0
      live-web/src/const/crud/admin/match/user.js
  65. 47 0
      live-web/src/const/crud/admin/matchwebfrontmenu.js
  66. 89 0
      live-web/src/const/crud/admin/role.js
  67. 103 0
      live-web/src/const/crud/admin/sysform.js
  68. 51 0
      live-web/src/const/crud/admin/token.js
  69. 275 0
      live-web/src/const/crud/admin/user.js
  70. 57 0
      live-web/src/const/crud/gen/form.js
  71. 201 0
      live-web/src/const/crud/gen/gen.js
  72. 323 0
      live-web/src/const/crud/match/competition.js
  73. 212 0
      live-web/src/const/crud/match/createMatch.js
  74. 94 0
      live-web/src/const/crud/match/group.js
  75. 125 0
      live-web/src/const/crud/match/judges.js
  76. 144 0
      live-web/src/const/crud/match/mySubMatch.js
  77. 134 0
      live-web/src/const/crud/match/rounds.js
  78. 37 0
      live-web/src/const/crud/match/scene.js
  79. 67 0
      live-web/src/const/crud/match/score.js
  80. 173 0
      live-web/src/const/crud/match/second/match.js
  81. 196 0
      live-web/src/const/crud/match/subMatch.js
  82. 237 0
      live-web/src/const/crud/match/website.js
  83. 13 0
      live-web/src/const/errorCode.js
  84. 47 0
      live-web/src/const/iconList.js
  85. 31 0
      live-web/src/const/website.js
  86. 17 0
      live-web/src/error.js
  87. 135 0
      live-web/src/filters/index.js
  88. 77 0
      live-web/src/main.js
  89. 168 0
      live-web/src/mixins/color.js
  90. 110 0
      live-web/src/page/index/index.vue
  91. 3 0
      live-web/src/page/index/layout.vue
  92. 84 0
      live-web/src/page/index/logo.vue
  93. 8 0
      live-web/src/page/index/sidebar/config.js
  94. 53 0
      live-web/src/page/index/sidebar/index.vue
  95. 121 0
      live-web/src/page/index/sidebar/sidebarItem.vue
  96. 161 0
      live-web/src/page/index/tags.vue
  97. 145 0
      live-web/src/page/index/top/index.vue
  98. 62 0
      live-web/src/page/index/top/top-menu.vue
  99. 49 0
      live-web/src/page/login/index.vue
  100. 0 0
      live-web/src/page/login/userlogin.vue

+ 46 - 0
.gitignore

@@ -0,0 +1,46 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**
+!**/src/test/**
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+.mvn
+
+
+#### MAC #####
+.DS_Store
+*.DS_Store
+**.DS_Store
+/.DS_Store
+/*.DS_Store
+/**.DS_Store
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+
+### VS Code ###
+.vscode/
+
+
+### Config ###
+src/main/resources/application-dev.yml
+/src/main/resources/application-dev.yml

+ 3 - 0
live-web/.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not ie <= 8

+ 15 - 0
live-web/.editorconfig

@@ -0,0 +1,15 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = false
+trim_trailing_whitespace = false

+ 16 - 0
live-web/.eslintrc.js

@@ -0,0 +1,16 @@
+module.exports = {
+    root: true,
+    env: {
+        node: true
+    },
+    'extends': [
+        "plugin:vue/essential"
+    ],
+    rules: {
+        'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+        'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
+    },
+    parserOptions: {
+        parser: 'babel-eslint'
+    }
+}

+ 29 - 0
live-web/.gitignore

@@ -0,0 +1,29 @@
+.DS_Store
+node_modules
+/dist
+
+/tests/e2e/videos/
+/tests/e2e/screenshots/
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw*
+
+# Lock File
+package-lock.json
+yarn.lock
+!/src/api/scst/news.js

+ 5 - 0
live-web/.postcssrc.js

@@ -0,0 +1,5 @@
+module.exports = {
+  plugins: {
+    autoprefixer: {}
+  }
+}

+ 202 - 0
live-web/LICENSE

@@ -0,0 +1,202 @@
+
+                                 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) 2020 pig4cloud Authors. 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.

+ 141 - 0
live-web/README.md

@@ -0,0 +1,141 @@
+<p align="center">
+ <img src="https://img.shields.io/badge/Pig-3.0-success.svg" alt="Build Status">
+ <img src="https://img.shields.io/badge/Spring%20Cloud-2020-blue.svg" alt="Coverage Status">
+ <img src="https://img.shields.io/badge/Spring%20Boot-2.4-blue.svg" alt="Downloads">
+ <img src="https://img.shields.io/github/license/pig-mesh/pig"/>
+</p>
+
+
+## 系统说明
+
+- 基于 Spring Cloud 2020 、Spring Boot 2.4、 OAuth2 的 RBAC **权限管理系统**
+- 基于数据驱动视图的理念封装 element-ui,即使没有 vue 的使用经验也能快速上手
+- 提供对常见容器化支持 Docker、Kubernetes、Rancher2 支持
+- 提供 lambda 、stream api 、webflux 的生产实践
+
+### 核心依赖
+
+| 依赖                   | 版本          |
+| ---------------------- | ------------- |
+| Spring Boot            | 2.4.5 |
+| Spring Cloud           | 2020.0.2    |
+| Spring Cloud Alibaba   | 2021.1|
+| Spring Security OAuth2 | 2.3.6         |
+| Mybatis Plus           | 3.4.2         |
+| hutool                 | 5.6.3         |
+| Avue                   | 2.6.16        |
+
+### 模块说明
+
+```lua
+pig-ui  -- https://gitee.com/log4j/pig-ui
+
+pig
+├── pig-auth -- 授权服务提供[3000]
+└── pig-common -- 系统公共模块
+     ├── pig-common-core -- 公共工具类核心包
+     ├── pig-common-datasource -- 动态数据源包
+     ├── pig-common-job -- xxl-job 封装
+     ├── pig-common-log -- 日志服务
+     ├── pig-common-mybatis -- mybatis 扩展封装
+     ├── pig-common-security -- 安全工具类
+     ├── pig-common-swagger -- 接口文档
+     ├── pig-common-feign -- feign 扩展封装
+     └── pig-common-test -- oauth2.0 单元测试扩展封装
+├── pig-register -- Nacos Server[8848]
+├── pig-gateway -- Spring Cloud Gateway网关[9999]
+└── pig-upms -- 通用用户权限管理模块
+     └── pig-upms-api -- 通用用户权限管理系统公共api模块
+     └── pig-upms-biz -- 通用用户权限管理系统业务处理模块[4000]
+└── pig-visual
+     └── pig-monitor -- 服务监控 [5001]
+     ├── pig-codegen -- 图形化代码生成 [5002]
+     ├── pig-sentinel-dashboard -- 流量高可用 [5003]
+     └── pig-xxl-job-admin -- 分布式定时任务管理台 [5004]
+```
+
+## 文档视频
+
+[文档视频 wiki.pig4cloud.com](https://wiki.pig4cloud.com)
+
+[PIGX 在线体验 pigx.pig4cloud.com](http://pigx.pig4cloud.com)
+
+[产品白皮书 paper.pig4cloud.com](https://paper.pig4cloud.com)
+
+## 快速开始
+
+### 本地开发 运行
+
+pig 提供了详细的[部署文档 wiki.pig4cloud.com](https://www.yuque.com/pig4cloud/pig/vsdox9),包括开发环境安装、服务端代码运行、前端代码运行等。
+
+请务必**完全按照**文档部署运行章节 进行操作,减少踩坑弯路!!
+
+### Docker 运行
+
+```
+# 下载并运行服务端代码
+git clone https://gitee.com/log4j/pig.git
+
+cd pig && mvn clean install && docker-compose up -d
+
+# 下载并运行前端UI
+git clone https://gitee.com/log4j/pig-ui.git
+
+cd pig-ui && npm install -g cnpm --registry=https://registry.npm.taobao.org
+
+npm run build:docker && docker-compose up -d
+```
+
+### 快速构架微服务
+
+```bash
+<!-- pig-gen archetype -->
+# 在空文件夹执行以下命令,注意 windows 下  \ 修改成 ^
+mvn archetype:generate \
+       -DgroupId=com.pig4cloud \
+       -DartifactId=demo \
+       -Dversion=1.0.0-SNAPSHOT \
+       -Dpackage=com.pig4cloud.pig.demo \
+       -DarchetypeGroupId=com.pig4cloud.archetype \
+       -DarchetypeArtifactId=pig-gen \
+       -DarchetypeVersion=3.1.2 \
+       -DarchetypeCatalog=local
+```
+
+## 免费公开课
+
+<table>
+  <tr>
+    <td><a href="https://www.bilibili.com/video/av45084065" target="_blank"><img src="https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901133006.png"></a></td>
+    <td><a href="https://www.bilibili.com/video/av77344954" target="_blank"><img src="https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901133059.png"></a></td>
+  </tr>
+    <tr>
+    <td><a href="https://www.bilibili.com/video/BV1J5411476V" target="_blank"><img src="https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901133114.png"></a></td>
+    <td><a href="https://www.bilibili.com/video/BV14p4y197K5" target="_blank"><img src="https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901133124.png"></a></td>
+  </tr>
+</table>
+
+## 微信群 [禁广告]
+
+![](https://gitee.com/pig4cloud/oss/raw/master/2020-9/20200901133142.png)
+
+## 开源共建
+
+### 开源协议
+
+pig 开源软件遵循 [Apache 2.0 协议](https://www.apache.org/licenses/LICENSE-2.0.html)。
+允许商业使用,但务必保留类作者、Copyright 信息。
+
+![](https://gitee.com/pig4cloud/oss/raw/master/2020-10-9/1602229452602-image.png)
+
+### 其他说明
+
+1. 欢迎提交 [PR](https://dwz.cn/2KURd5Vf),注意对应提交对应 `dev` 分支
+
+2. 欢迎提交 [issue](https://gitee.com/log4j/pig/issues),请写清楚遇到问题的原因、开发环境、复显步骤。
+
+3. 联系作者 <a href="mailto:pig4cloud@qq.com">pig4cloud@qq.com</a>
+
+
+[![Stargazers over time](https://whnb.wang/img/log4j/pig?e=604800)](https://whnb.wang/log4j/pig?e=604800)
+

+ 5 - 0
live-web/babel.config.js

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

+ 9 - 0
live-web/docker/Dockerfile

@@ -0,0 +1,9 @@
+FROM nginx 
+
+COPY ./dist /data 
+
+RUN rm /etc/nginx/conf.d/default.conf
+
+ADD pig-ui.conf /etc/nginx/conf.d/ 
+
+RUN /bin/bash -c 'echo init ok'

+ 19 - 0
live-web/docker/docker-compose.yaml

@@ -0,0 +1,19 @@
+version: '3'
+services:
+  pig-ui:
+    build:
+      context: .
+    restart: always
+    container_name: pig-ui
+    image: pig-ui
+    networks:
+      - pig_default
+    external_links:
+      - pig-gateway
+    ports:
+      - 80:80
+
+# 加入到后端网络, 默认为 pig_default  | docker network ls   查看
+networks:
+  pig_default:
+    external: true

+ 19 - 0
live-web/docker/pig-ui.conf

@@ -0,0 +1,19 @@
+server {
+    listen 80;
+    server_name localhost;
+    
+    # 打包好的dist目录文件,放置到这个目录下
+    root /data/;
+    
+    # 注意维护新增微服务,gateway 路由前缀
+    location ~* ^/(code|auth|admin|gen) {
+       proxy_pass http://pig-gateway:9999;
+       #proxy_set_header Host $http_host;
+       proxy_connect_timeout 15s;
+       proxy_send_timeout 15s;
+       proxy_read_timeout 15s;
+       proxy_set_header X-Forwarded-Proto http;
+       proxy_set_header X-Real-IP $remote_addr;
+       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    }
+}

+ 53 - 0
live-web/package.json

@@ -0,0 +1,53 @@
+{
+  "name": "pig-ui",
+  "version": "3.2.1",
+  "private": true,
+  "scripts": {
+    "pre": "cnpm install || yarn --registry https://registry.npm.taobao.org || npm install --registry https://registry.npm.taobao.org ",
+    "dev": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "build:docker": "vue-cli-service build --dest='./docker/dist/'",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@riophae/vue-treeselect": "^0.4.0",
+    "@smallwei/avue": "2.6.18",
+    "@sscfaith/avue-form-design": "1.3.12",
+    "avue-plugin-ueditor": "^0.0.6",
+    "axios": "0.19.0",
+    "babel-polyfill": "^6.26.0",
+    "classlist-polyfill": "^1.2.0",
+    "codemirror": "^5.58.1",
+    "crypto-js": "^3.1.9-1",
+    "element-ui": "^2.13.2",
+    "js-cookie": "^3.0.1",
+    "nprogress": "^0.2.0",
+    "qrcodejs2": "^0.0.2",
+    "script-loader": "^0.7.2",
+    "video.js": "^7.17.0",
+    "videojs-contrib-hls": "^5.15.0",
+    "videojs-landscape-fullscreen": "^11.33.0",
+    "vue": "2.6.10",
+    "vue-axios": "^2.1.4",
+    "vue-router": "^3.1.3",
+    "vuex": "^3.2.0"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "^3.12.0",
+    "@vue/cli-service": "^3.12.0",
+    "chai": "^4.2.0",
+    "node-sass": "^4.12.0",
+    "sass-loader": "^8.0.0",
+    "vue-template-compiler": "2.6.10"
+  },
+  "lint-staged": {
+    "*.js": [
+      "vue-cli-service lint",
+      "git add"
+    ],
+    "*.vue": [
+      "vue-cli-service lint",
+      "git add"
+    ]
+  }
+}

文件差異過大導致無法顯示
+ 11 - 0
live-web/public/cdn/animate/3.5.2/animate.css


+ 54 - 0
live-web/public/cdn/avue/avue.css

@@ -0,0 +1,54 @@
+html,
+body,
+#app {
+    height: 100%;
+    margin: 0;
+    padding: 0;
+}
+
+.avue-home {
+    background-color: #303133;
+    height: 100%;
+    display: flex;
+    flex-direction: column;
+}
+
+.avue-home__main {
+    user-select: none;
+    width: 100%;
+    flex-grow: 1;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    flex-direction: column;
+}
+
+.avue-home__footer {
+    width: 100%;
+    flex-grow: 0;
+    text-align: center;
+    padding: 1em 0;
+}
+
+.avue-home__footer > a {
+    font-size: 12px;
+    color: #ABABAB;
+    text-decoration: none;
+}
+
+.avue-home__loading {
+    height: 32px;
+    width: 32px;
+    margin-bottom: 20px;
+}
+
+.avue-home__title {
+    color: #FFF;
+    font-size: 14px;
+    margin-bottom: 10px;
+}
+
+.avue-home__sub-title {
+    color: #ABABAB;
+    font-size: 12px;
+}

+ 193 - 0
live-web/public/cdn/store/1.3.20/store.js

@@ -0,0 +1,193 @@
+'use strict'
+// Module export pattern from
+// https://github.com/umdjs/umd/blob/master/returnExports.js
+;(function (root, factory) {
+  if (typeof define === 'function' && define.amd) {
+    // AMD. Register as an anonymous module.
+    define([], factory)
+  } else if (typeof exports === 'object') {
+    // Node. Does not work with strict CommonJS, but
+    // only CommonJS-like environments that support module.exports,
+    // like Node.
+    module.exports = factory()
+  } else {
+    // Browser globals (root is window)
+    root.store = factory()
+  }
+}(this, function () {
+  // Store.js
+  var store = {}
+
+  var win = (typeof window !== 'undefined' ? window : global)
+
+  var doc = win.document
+
+  var localStorageName = 'localStorage'
+
+  var scriptTag = 'script'
+
+  var storage
+
+  store.disabled = false
+  store.version = '1.3.20'
+  store.set = function (key, value) {}
+  store.get = function (key, defaultVal) {}
+  store.has = function (key) { return store.get(key) !== undefined }
+  store.remove = function (key) {}
+  store.clear = function () {}
+  store.transact = function (key, defaultVal, transactionFn) {
+    if (transactionFn == null) {
+      transactionFn = defaultVal
+      defaultVal = null
+    }
+    if (defaultVal == null) {
+      defaultVal = {}
+    }
+    var val = store.get(key, defaultVal)
+    transactionFn(val)
+    store.set(key, val)
+  }
+  store.getAll = function () {}
+  store.forEach = function () {}
+
+  store.serialize = function (value) {
+    return JSON.stringify(value)
+  }
+  store.deserialize = function (value) {
+    if (typeof value !== 'string') { return undefined }
+    try { return JSON.parse(value) } catch (e) { return value || undefined }
+  }
+
+  // Functions to encapsulate questionable FireFox 3.6.13 behavior
+  // when about.config::dom.storage.enabled === false
+  // See https://github.com/marcuswestin/store.js/issues#issue/13
+  function isLocalStorageNameSupported () {
+    try { return (localStorageName in win && win[localStorageName]) } catch (err) { return false }
+  }
+
+  if (isLocalStorageNameSupported()) {
+    storage = win[localStorageName]
+    store.set = function (key, val) {
+      if (val === undefined) { return store.remove(key) }
+      storage.setItem(key, store.serialize(val))
+      return val
+    }
+    store.get = function (key, defaultVal) {
+      var val = store.deserialize(storage.getItem(key))
+      return (val === undefined ? defaultVal : val)
+    }
+    store.remove = function (key) { storage.removeItem(key) }
+    store.clear = function () { storage.clear() }
+    store.getAll = function () {
+      var ret = {}
+      store.forEach(function (key, val) {
+        ret[key] = val
+      })
+      return ret
+    }
+    store.forEach = function (callback) {
+      for (var i = 0; i < storage.length; i++) {
+        var key = storage.key(i)
+        callback(key, store.get(key))
+      }
+    }
+  } else if (doc && doc.documentElement.addBehavior) {
+    var storageOwner,
+      storageContainer
+    // Since #userData storage applies only to specific paths, we need to
+    // somehow link our data to a specific path.  We choose /favicon.ico
+    // as a pretty safe option, since all browsers already make a request to
+    // this URL anyway and being a 404 will not hurt us here.  We wrap an
+    // iframe pointing to the favicon in an ActiveXObject(htmlfile) object
+    // (see: http://msdn.microsoft.com/en-us/library/aa752574(v=VS.85).aspx)
+    // since the iframe access rules appear to allow direct access and
+    // manipulation of the document element, even for a 404 page.  This
+    // document can be used instead of the current document (which would
+    // have been limited to the current path) to perform #userData storage.
+    try {
+      storageContainer = new ActiveXObject('htmlfile')
+      storageContainer.open()
+      storageContainer.write('<' + scriptTag + '>document.w=window</' + scriptTag + '><iframe src="/favicon.ico"></iframe>')
+      storageContainer.close()
+      storageOwner = storageContainer.w.frames[0].document
+      storage = storageOwner.createElement('div')
+    } catch (e) {
+      // somehow ActiveXObject instantiation failed (perhaps some special
+      // security settings or otherwse), fall back to per-path storage
+      storage = doc.createElement('div')
+      storageOwner = doc.body
+    }
+    var withIEStorage = function (storeFunction) {
+      return function () {
+        var args = Array.prototype.slice.call(arguments, 0)
+        args.unshift(storage)
+        // See http://msdn.microsoft.com/en-us/library/ms531081(v=VS.85).aspx
+        // and http://msdn.microsoft.com/en-us/library/ms531424(v=VS.85).aspx
+        storageOwner.appendChild(storage)
+        storage.addBehavior('#default#userData')
+        storage.load(localStorageName)
+        var result = storeFunction.apply(store, args)
+        storageOwner.removeChild(storage)
+        return result
+      }
+    }
+
+    // In IE7, keys cannot start with a digit or contain certain chars.
+    // See https://github.com/marcuswestin/store.js/issues/40
+    // See https://github.com/marcuswestin/store.js/issues/83
+    var forbiddenCharsRegex = new RegExp("[!\"#$%&'()*+,/\\\\:;<=>?@[\\]^`{|}~]", 'g')
+    var ieKeyFix = function (key) {
+      return key.replace(/^d/, '___$&').replace(forbiddenCharsRegex, '___')
+    }
+    store.set = withIEStorage(function (storage, key, val) {
+      key = ieKeyFix(key)
+      if (val === undefined) { return store.remove(key) }
+      storage.setAttribute(key, store.serialize(val))
+      storage.save(localStorageName)
+      return val
+    })
+    store.get = withIEStorage(function (storage, key, defaultVal) {
+      key = ieKeyFix(key)
+      var val = store.deserialize(storage.getAttribute(key))
+      return (val === undefined ? defaultVal : val)
+    })
+    store.remove = withIEStorage(function (storage, key) {
+      key = ieKeyFix(key)
+      storage.removeAttribute(key)
+      storage.save(localStorageName)
+    })
+    store.clear = withIEStorage(function (storage) {
+      var attributes = storage.XMLDocument.documentElement.attributes
+      storage.load(localStorageName)
+      for (var i = attributes.length - 1; i >= 0; i--) {
+        storage.removeAttribute(attributes[i].name)
+      }
+      storage.save(localStorageName)
+    })
+    store.getAll = function (storage) {
+      var ret = {}
+      store.forEach(function (key, val) {
+        ret[key] = val
+      })
+      return ret
+    }
+    store.forEach = withIEStorage(function (storage, callback) {
+      var attributes = storage.XMLDocument.documentElement.attributes
+      for (var i = 0, attr; attr = attributes[i]; ++i) {
+        callback(attr.name, store.deserialize(storage.getAttribute(attr.name)))
+      }
+    })
+  }
+
+  try {
+    var testKey = '__storejs__'
+    store.set(testKey, testKey)
+    if (store.get(testKey) != testKey) { store.disabled = true }
+    store.remove(testKey)
+  } catch (e) {
+    store.disabled = true
+  }
+  store.enabled = !store.disabled
+
+  return store
+}))

二進制
live-web/public/favicon.ico


二進制
live-web/public/img/logo.png


+ 41 - 0
live-web/public/index.html

@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <meta name="format-detection" content="telephone=no">
+    <meta http-equiv="X-UA-Compatible" content="chrome=1" />
+    <link rel="stylesheet" href="<%= BASE_URL %>cdn/animate/3.5.2/animate.css">
+    <link rel="stylesheet" href="<%= BASE_URL %>cdn/avue/avue.css">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title>pig微服务快速开发框架</title>
+</head>
+
+<body>
+    <noscript>
+      <strong>很抱歉,如果没有 JavaScript 支持,网站将不能正常工作。请启用浏览器的 JavaScript 然后继续。</strong>
+    </noscript>
+    <div id="app">
+        <div class="avue-home">
+            <div class="avue-home__main">
+                <img class="avue-home__loading" src="./svg/loading-spin.svg" alt="loading">
+                <div class="avue-home__title">
+                    正在加载资源
+                </div>
+                <div class="avue-home__sub-title">
+                    初次加载资源可能需要较多时间 请耐心等待
+                </div>
+            </div>
+            <div class="avue-home__footer">
+                <a href="https://pig4cloud.com/zh-cn" target="_blank">
+                    Copyright © 2020 pig4cloud.com</a>
+            </div>
+        </div>
+    </div>
+</body>
+
+</html>

+ 6 - 0
live-web/public/svg/loading-spin.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32" fill="white">
+  <path opacity=".25" d="M16 0 A16 16 0 0 0 16 32 A16 16 0 0 0 16 0 M16 4 A12 12 0 0 1 16 28 A12 12 0 0 1 16 4"/>
+  <path d="M16 0 A16 16 0 0 1 32 16 L28 16 A12 12 0 0 0 16 4z">
+    <animateTransform attributeName="transform" type="rotate" from="0 16 16" to="360 16 16" dur="0.8s" repeatCount="indefinite" />
+  </path>
+</svg>

+ 25 - 0
live-web/src/App.vue

@@ -0,0 +1,25 @@
+<template>
+  <div id="app">
+    <router-view/>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'app',
+    data() {
+      return {}
+    },
+    watch: {},
+    created() {
+    },
+    methods: {},
+    computed: {}
+  }
+</script>
+<style lang="scss">
+  #app {
+    width: 100%;
+    height: 100%;
+  }
+</style>

+ 56 - 0
live-web/src/api/admin/client.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/client/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/client',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/client/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/client/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/client',
+    method: 'put',
+    data: obj
+  })
+}

+ 72 - 0
live-web/src/api/admin/dept.js

@@ -0,0 +1,72 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchDeptTree (query) {
+  return request({
+    url: '/admin/dept/user-tree',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchTree (query) {
+  return request({
+    url: '/admin/dept/tree',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/dept',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/dept/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/dept/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/dept',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function getdetails (obj) {
+  return request({
+    url: '/admin/dept/details/' + obj,
+    method: 'get'
+  })
+}
+

+ 101 - 0
live-web/src/api/admin/dict.js

@@ -0,0 +1,101 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/admin/dict/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchItemList(query) {
+  return request({
+    url: '/admin/dict/item/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addItemObj(obj) {
+  return request({
+    url: '/admin/dict/item',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/admin/dict/item/' + id,
+    method: 'get'
+  })
+}
+
+export function delItemObj(id) {
+  return request({
+    url: '/admin/dict/item/' + id,
+    method: 'delete'
+  })
+}
+
+export function putItemObj(obj) {
+  return request({
+    url: '/admin/dict/item',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/dict/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/dict/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/admin/dict/' + row.id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/dict/',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function remote(type) {
+  return request({
+    url: '/admin/dict/type/' + type,
+    method: 'get'
+  })
+}

+ 57 - 0
live-web/src/api/admin/log.js

@@ -0,0 +1,57 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/log/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/log/' + id,
+    method: 'delete'
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/log',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/log/' + id,
+    method: 'get'
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/log',
+    method: 'put',
+    data: obj
+  })
+}
+

+ 80 - 0
live-web/src/api/admin/match/user.js

@@ -0,0 +1,80 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+import qs from 'qs'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/user/getUserListByMatch',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/user',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function changeUserPasswd (obj) {
+  return request({
+    url: '/admin/user/changepwd',
+    method: 'post',
+    data: qs.stringify(obj)
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/user',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function getDetails (obj) {
+  return request({
+    url: '/admin/user/details/' + obj,
+    method: 'get'
+  })
+}
+
+export function getUserListByRole (obj) {
+  return request({
+    url: '/admin/user/getUserListByRole/',
+    method: 'get',
+    params: obj
+  })
+}

+ 56 - 0
live-web/src/api/admin/matchwebfrontmenu.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/webfront/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/saveWebFront',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/webfront/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/match/webfront/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateWebFront',
+    method: 'put',
+    data: obj
+  })
+}

+ 64 - 0
live-web/src/api/admin/menu.js

@@ -0,0 +1,64 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function getMenu(id) {
+  return request({
+    url: '/admin/menu',
+    params: {parentId: id},
+    method: 'get'
+  })
+}
+
+export function fetchMenuTree(lazy, parentId) {
+  return request({
+    url: '/admin/menu/tree',
+    method: 'get',
+    params: {lazy: lazy, parentId: parentId}
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/admin/menu',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/admin/menu/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/admin/menu/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/admin/menu',
+    method: 'put',
+    data: obj
+  })
+}

+ 81 - 0
live-web/src/api/admin/role.js

@@ -0,0 +1,81 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/role/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function deptRoleList () {
+  return request({
+    url: '/admin/role/list',
+    method: 'get'
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/role/' + id,
+    method: 'get'
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/role',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/role',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/role/' + id,
+    method: 'delete'
+  })
+}
+
+export function permissionUpd (roleId, menuIds) {
+  return request({
+    url: '/admin/role/menu',
+    method: 'put',
+    data: {
+      roleId: roleId,
+      menuIds: menuIds
+    }
+  })
+}
+
+export function fetchRoleTree (roleName) {
+  return request({
+    url: '/admin/menu/tree/' + roleName,
+    method: 'get'
+  })
+}

+ 56 - 0
live-web/src/api/admin/sysform.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/project/pageForm',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/project/insertForm',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/project/findFormById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/project/delFormById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/project/updateForm',
+    method: 'post',
+    data: obj
+  })
+}

+ 33 - 0
live-web/src/api/admin/token.js

@@ -0,0 +1,33 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/token/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delObj (token) {
+  return request({
+    url: '/admin/token/' + token,
+    method: 'delete'
+  })
+}

+ 79 - 0
live-web/src/api/admin/user.js

@@ -0,0 +1,79 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import request from '@/router/axios'
+
+export function fetchList (query) {
+  return request({
+    url: '/admin/user/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj (obj) {
+  return request({
+    url: '/admin/user',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj (id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj (id) {
+  return request({
+    url: '/admin/user/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj (obj) {
+  return request({
+    url: '/admin/user',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function getDetails (obj) {
+  return request({
+    url: '/admin/user/details/' + obj,
+    method: 'get'
+  })
+}
+
+export function getUserListByMatch (obj) {
+  return request({
+    url: '/admin/user/getUserListByMatch/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function insertUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}

+ 33 - 0
live-web/src/api/es.js

@@ -0,0 +1,33 @@
+import request from '@/router/axios'
+
+
+const save = (data) =>{
+  return request.post(
+    `/test/save?path=${data.path}&content=${data.content}`
+  );
+}
+
+const findall = () =>{
+  return request.get(
+    `/test/findall`
+  );
+}
+
+const update = (data) =>{
+  return request.post(
+    `/test/update?id=${data.id}&path=${data.path}&content=${data.content}`
+  );
+}
+
+const del = (id) =>{
+  return request.post(
+    `/test/delete?id=${id}`
+  );
+}
+
+const find = (key) =>{
+  return request.post(
+    `/test/find?keyword=${key}`
+  );
+}
+export { save,findall,update,del,find };

+ 56 - 0
live-web/src/api/gen/form.js

@@ -0,0 +1,56 @@
+/*
+ *    Copyright (c) 2018-2025, test All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: test
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/gen/form/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/gen/form',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/gen/form/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/gen/form/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/gen/form',
+    method: 'put',
+    data: obj
+  })
+}

+ 118 - 0
live-web/src/api/gen/gen.js

@@ -0,0 +1,118 @@
+/*
+ *    Copyright (c) 2018-2025, test All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng
+ */
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/gen/generator/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function preview(table) {
+  return request({
+    url: '/gen/generator/preview',
+    method: 'get',
+    params: table
+  })
+}
+
+export function fetchDsList(query) {
+  return request({
+    url: '/gen/dsconf/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchSelectDsList() {
+  return request({
+    url: '/gen/dsconf/list',
+    method: 'get'
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/gen/dsconf/',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/gen/dsconf/' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(id) {
+  return request({
+    url: '/gen/dsconf/' + id,
+    method: 'delete'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/gen/dsconf/',
+    method: 'put',
+    data: obj
+  })
+}
+
+export function handleDown(table) {
+  return request({
+    url: '/gen/generator/code',
+    method: 'post',
+    data: table,
+    responseType: 'arraybuffer'
+  }).then((response) => { // 处理返回的文件流
+    const blob = new Blob([response.data], { type: 'application/zip' })
+    const filename = table.tableName + '.zip'
+    const link = document.createElement('a')
+    link.href = URL.createObjectURL(blob)
+    link.download = filename
+    document.body.appendChild(link)
+    link.click()
+    window.setTimeout(function () {
+      URL.revokeObjectURL(blob)
+      document.body.removeChild(link)
+    }, 0)
+  })
+}
+
+
+export function getForm(tableName, dsName) {
+  return request({
+    url: '/gen/form/info',
+    params: { tableName: tableName, dsName: dsName },
+    method: 'get'
+  })
+}
+
+export function postForm(formInfo, tableName, dsId) {
+  return request({
+    url: '/gen/form/',
+    method: 'post',
+    data: Object.assign({ formInfo, tableName, dsId })
+  })
+}
+

+ 59 - 0
live-web/src/api/login.js

@@ -0,0 +1,59 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+import request from '@/router/axios'
+const scope = 'server'
+
+export const loginByUsername = (username, password, code, randomStr) => {
+  const grant_type = 'password'
+
+  return request({
+    url: '/auth/oauth/token',
+    headers: {
+      isToken:false,
+      'Authorization': 'Basic cGlnOnBpZw=='
+    },
+    method: 'post',
+    params: { username, password, randomStr, code, grant_type, scope }
+  })
+}
+
+export const refreshToken = (refresh_token) => {
+  const grant_type = 'refresh_token'
+  return request({
+    url: '/auth/oauth/token',
+    headers: {
+      'isToken': false,
+      'Authorization': 'Basic cGlnOnBpZw==',
+    },
+    method: 'post',
+    params: { refresh_token, grant_type, scope }
+  })
+}
+
+export const getUserInfo = () => {
+  return request({
+    url: '/admin/user/info',
+    method: 'get'
+  })
+}
+
+export const logout = () => {
+  return request({
+    url: '/auth/token/logout',
+    method: 'delete'
+  })
+}

+ 132 - 0
live-web/src/api/match/competition.js

@@ -0,0 +1,132 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/mainPageMatch',
+    method: 'get',
+    params: query
+  })
+}
+
+export function matchList(query) {
+  return request({
+    url: '/match/mainPageMatchList',
+    method: 'get',
+    params: query
+  })
+}
+
+export function competitionManagerPage(query) {
+  return request({
+    url: '/match/competitionManagerPage',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchItemList(query) {
+  return request({
+    url: '/match/pageTrack',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getUserPageByRole (query) {
+  return request({
+    url: '/match/getOtherUser',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addItemObj(obj) {
+  return request({
+    url: '/match/insertTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/findTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function addUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}
+export function delItemObj(id) {
+  return request({
+    url: '/match/delTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delCompetitionManagerById(id) {
+  return request({
+    url: '/match/delCompetitionManagerById?id=' + id,
+    method: 'get'
+  })
+}
+export function putItemObj(obj) {
+  return request({
+    url: '/match/updateTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/saveMatch',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/getMatchById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/match/delMatchById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateMatchById',
+    method: 'post',
+    data: obj
+  })
+}
+
+
+export function insertPitch(obj) {
+  return request({
+    url: '/match/insertPitch/' ,
+    method: 'post',
+    data: obj
+  })
+}
+
+export function myManagerMatchList(obj) {
+  return request({
+    url: '/match/myManagerMatchList/' ,
+    method: 'get',
+    data: obj
+  })
+}

+ 107 - 0
live-web/src/api/match/createMatch.js

@@ -0,0 +1,107 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/pageMatch',//超管的主赛列表
+    method: 'get',
+    params: query
+  })
+}
+export function addObj(obj) {
+  return request({
+    url: '/match/saveMainMatch',//主赛事保存 
+    method: 'post',
+    data: obj
+  })
+}
+export function delObj(row) {
+  return request({
+    url: '/match/delMatchById?id=' + row.id,//删除赛事
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateMatchById',//赛事更新-不区分主赛分赛
+    method: 'post',
+    data: obj
+  })
+}
+export function getObj(id) {
+  return request({
+    url: '/match/getMatchById?id=' + id,
+    method: 'get'
+  })
+}
+export function competitionManagerPage(query) {
+  return request({
+    url: '/match/competitionManagerPage',//赛事的管理员列表接口
+    method: 'get',
+    params: query
+  })
+}
+export function insertPitch(obj) {
+  return request({
+    url: '/match/insertPitch/' ,//保存管理员
+    method: 'post',
+    data: obj
+  })
+}
+export function delCompetitionManagerById(id) {//删除管理员
+  return request({
+    url: '/match/delCompetitionManagerById?id=' + id,
+    method: 'get'
+  })
+}
+export function getUserPageByRole (query) {//按角色获取系统用户
+  return request({
+    url: '/match/getOtherUser',
+    method: 'get',
+    params: query
+  })
+}
+export function fetchItemList(query) {
+  return request({
+    url: '/match/pageTrack',
+    method: 'get',
+    params: query
+  })
+}
+export function addItemObj(obj) {
+  return request({
+    url: '/match/insertTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/findTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function addUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}
+export function delItemObj(id) {
+  return request({
+    url: '/match/delTrackById?id=' + id,
+    method: 'get'
+  })
+}
+export function putItemObj(obj) {
+  return request({
+    url: '/match/updateTrack',
+    method: 'post',
+    data: obj
+  })
+}
+

+ 102 - 0
live-web/src/api/match/judges.js

@@ -0,0 +1,102 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/project/listJudges',
+    method: 'get',
+    params: query
+  })
+}
+
+export function listMatchJudges(query) {
+  return request({
+    url: '/match/listMatchJudges',
+    method: 'get',
+    params: query
+  })
+}
+
+export function insertBatchJudges(query) {
+  return request({
+    url: '/match/insertBatchJudges',
+    method: 'post',
+    data: query
+  })
+}
+
+export function delMatchJudgesConfigById(query) {
+  return request({
+    url: '/match/delMatchJudgesConfigById',
+    method: 'get',
+    params: query
+  })
+}
+
+export function judgeConfPages(query) {
+  return request({
+    url: '/match/judgeConfPages',
+    method: 'get',
+    params: query
+  })
+}
+
+export function pageJudagesByMatchId(query) {
+  return request({
+    url: '/project/pageJudagesByMatchId',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/project/saveJudges',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/project/getjudgesById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/project/deleteJudges?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/project/updateJudges',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function remote(type) {
+  return request({
+    url: '/project/dict/type/' + type,
+    method: 'get'
+  })
+}
+
+export function saveBitchJudges(obj) {
+  return request({
+    url: '/match/saveBitchJudges',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getJudgesById(obj) {
+  return request({
+    url: '/project/getJudgesById?id='+obj,
+    method: 'get'
+  })
+}

+ 115 - 0
live-web/src/api/match/mySubMatch.js

@@ -0,0 +1,115 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/subPageMatch',//分赛列表
+    method: 'get',
+    params: query
+  })
+}
+
+export function competitionManagerPage(query) {
+  return request({
+    url: '/match/competitionManagerPage',
+    method: 'get',
+    params: query
+  })
+}
+export function fetchItemList(query) {
+  return request({
+    url: '/match/pageTrack',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getUserPageByRole (query) {
+  return request({
+    url: '/match/getOtherUser',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addItemObj(obj) {
+  return request({
+    url: '/match/insertTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/findTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function addUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}
+export function delItemObj(id) {
+  return request({
+    url: '/match/delTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delCompetitionManagerById(id) {
+  return request({
+    url: '/match/delCompetitionManagerById?id=' + id,
+    method: 'get'
+  })
+}
+export function putItemObj(obj) {
+  return request({
+    url: '/match/updateTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/saveSubMatch',//保存分赛信息
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/getMatchById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/match/delMatchById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateMatchById',
+    method: 'post',
+    data: obj
+  })
+}
+
+
+export function insertPitch(obj) {
+  return request({
+    url: '/match/insertPitch/' ,
+    method: 'post',
+    data: obj
+  })
+}

+ 50 - 0
live-web/src/api/match/project.js

@@ -0,0 +1,50 @@
+
+import request from '@/router/axios'
+
+export function getProjectDetails(query) {
+  return request({
+    url: '/project/findProjectDataById',
+    method: 'post',
+    params: query
+  })
+}
+
+export function getProjectList(query) {
+  return request({
+    url: '/project/findProject',
+    method: 'post',
+    params: query
+  })
+}
+
+export function getProjectTemplate(query) {
+  return request({
+    url: '/project/findProjectTemplateByTrackId',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delProject(query) {
+  return request({
+    url: '/project/del',
+    method: 'get',
+    params: query
+  })
+}
+
+export function pageProjectByGroupList(parama){
+  return request({
+    url: '/project/pageProjectByGroupList',
+    method: 'post',
+    params: parama
+  })
+}
+
+export function insertProjects(data) {
+  return request({
+    url: '/match/insertProjects',
+    method: 'post',
+    data: data
+  })
+}

+ 125 - 0
live-web/src/api/match/rounds.js

@@ -0,0 +1,125 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/pageRounds',
+    method: 'get',
+    params: query
+  })
+}
+
+export function fetchItemList(query) {
+  return request({
+    url: '/match/dict/item/page',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addGroupObj(obj) {
+  return request({
+    url: '/match/insertProjectGroup',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/dict/item/' + id,
+    method: 'get'
+  })
+}
+
+export function delProjectGroupById(id) {
+  return request({
+    url: '/match/delProjectGroupById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function updateProjectGroup(obj) {
+  return request({
+    url: '/match/updateProjectGroup',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/insertRounds',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/findRoundsById?id=' + id,
+    method: 'get'
+  })
+}
+export function getOtherRounds(id) {
+  return request({
+    url: '/match/getOtherRounds?matchId=' + id,
+    method: 'get'
+  })
+}
+export function insertScoreRuleVO(obj){
+  return request({
+    url: '/match/insertScoreRuleVO',
+    method: 'post',
+    data: obj
+  })
+}
+export function findScoreRuleByRoundsId(id){
+  return request({
+    url: '/match/findScoreRuleByRoundsId?id=' +id,
+    method: 'get'
+  })
+}
+export function delObj(row) {
+  return request({
+    url: '/match/delRoundsById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function startRounds(row) {
+  return request({
+    url: '/match/startRounds?roundId=' + row.id,
+    method: 'get'
+  })
+}
+
+export function endRounds(row) {
+  return request({
+    url: '/match/endRounds?roundId=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateRounds',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function remote(type) {
+  return request({
+    url: '/match/dict/type/' + type,
+    method: 'get'
+  })
+}
+
+export function groupPage(obj) {
+  return request({
+    url: '/match/pageGroup',
+    method: 'get',
+    params: obj
+  })
+}

+ 18 - 0
live-web/src/api/match/rule.js

@@ -0,0 +1,18 @@
+
+import request from '@/router/axios'
+
+export function insertScoreRule(obj) {
+  return request({
+    url: '/match/insertScoreRule',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function updateScoreRule(obj) {
+  return request({
+    url: '/match/updateScoreRule',
+    method: 'post',
+    data: obj
+  })
+}

+ 10 - 0
live-web/src/api/match/scene.js

@@ -0,0 +1,10 @@
+
+import request from '@/router/axios'
+
+export function liveManage(query) {
+  return request({
+    url: '/match/liveManage',
+    method: 'get',
+    params: query
+  })
+}

+ 48 - 0
live-web/src/api/match/score.js

@@ -0,0 +1,48 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/findScoreCategoriesPageByMatchId',
+    method: 'get',
+    params: query
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/match/delScoreCategoriesById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function findSelectRuleByScore(query) {
+  return request({
+    url: '/match/findSelectRuleByScore',
+    method: 'get',
+    params: query
+  })
+}
+
+export function insertScoreCategories(obj) {
+  return request({
+    url: '/match/insertScoreCategories',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function updateScoreCategories(obj) {
+  return request({
+    url: '/match/updateScoreCategories',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function delScoreRuleById(row) {
+  return request({
+    url: '/match/delScoreRuleById?id=' + row,
+    method: 'get'
+  })
+}

+ 115 - 0
live-web/src/api/match/second/match.js

@@ -0,0 +1,115 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/pageMatchSecond',//分赛列表
+    method: 'get',
+    params: query
+  })
+}
+
+export function competitionManagerPage(query) {
+  return request({
+    url: '/match/competitionManagerPage',
+    method: 'get',
+    params: query
+  })
+}
+export function fetchItemList(query) {
+  return request({
+    url: '/match/pageTrack',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getUserPageByRole (query) {
+  return request({
+    url: '/match/getOtherUserSecond',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addItemObj(obj) {
+  return request({
+    url: '/match/insertTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/findTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function addUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}
+export function delItemObj(id) {
+  return request({
+    url: '/match/delTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delCompetitionManagerById(id) {
+  return request({
+    url: '/match/delCompetitionManagerById?id=' + id,
+    method: 'get'
+  })
+}
+export function putItemObj(obj) {
+  return request({
+    url: '/match/updateTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/saveSecondMatch',//保存分赛信息
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/getMatchById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/match/delMatchById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateSubMatchById',
+    method: 'post',
+    data: obj
+  })
+}
+
+
+export function insertPitch(obj) {
+  return request({
+    url: '/match/insertPitch/' ,
+    method: 'post',
+    data: obj
+  })
+}

+ 115 - 0
live-web/src/api/match/subMatch.js

@@ -0,0 +1,115 @@
+
+import request from '@/router/axios'
+
+export function fetchList(query) {
+  return request({
+    url: '/match/pageMatchChild',//分赛列表
+    method: 'get',
+    params: query
+  })
+}
+
+export function competitionManagerPage(query) {
+  return request({
+    url: '/match/competitionManagerPage',
+    method: 'get',
+    params: query
+  })
+}
+export function fetchItemList(query) {
+  return request({
+    url: '/match/pageTrack',
+    method: 'get',
+    params: query
+  })
+}
+
+export function getUserPageByRole (query) {
+  return request({
+    url: '/match/getOtherUser',
+    method: 'get',
+    params: query
+  })
+}
+
+export function addItemObj(obj) {
+  return request({
+    url: '/match/insertTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getItemObj(id) {
+  return request({
+    url: '/match/findTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function addUser (obj) {
+  return request({
+    url: '/admin/user/insertUser',
+    method: 'post',
+    data: obj
+  })
+}
+export function delItemObj(id) {
+  return request({
+    url: '/match/delTrackById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delCompetitionManagerById(id) {
+  return request({
+    url: '/match/delCompetitionManagerById?id=' + id,
+    method: 'get'
+  })
+}
+export function putItemObj(obj) {
+  return request({
+    url: '/match/updateTrack',
+    method: 'post',
+    data: obj
+  })
+}
+
+export function addObj(obj) {
+  return request({
+    url: '/match/saveSubMatch',//保存分赛信息
+    method: 'post',
+    data: obj
+  })
+}
+
+export function getObj(id) {
+  return request({
+    url: '/match/getMatchById?id=' + id,
+    method: 'get'
+  })
+}
+
+export function delObj(row) {
+  return request({
+    url: '/match/delMatchById?id=' + row.id,
+    method: 'get'
+  })
+}
+
+export function putObj(obj) {
+  return request({
+    url: '/match/updateSubMatchById',
+    method: 'post',
+    data: obj
+  })
+}
+
+
+export function insertPitch(obj) {
+  return request({
+    url: '/match/insertPitch/' ,
+    method: 'post',
+    data: obj
+  })
+}

+ 10 - 0
live-web/src/api/match/track.js

@@ -0,0 +1,10 @@
+
+import request from '@/router/axios'
+
+export function getTrackInfoByMatchId(query) {
+  return request({
+    url: '/match/getTrackInfoByMatchId',
+    method: 'get',
+    params: query
+  })
+}

+ 79 - 0
live-web/src/api/match/website.js

@@ -0,0 +1,79 @@
+import request from "@/router/axios";
+
+export function getWebInfoByTag(tag) {
+  return request({
+    url: "/match/getWebInfoByTag?tag="+tag,
+    method: "get"
+  });
+}
+
+
+export function insertWebInfo(obj) {
+  return request({
+    url: "/match/insertWebInfo",
+    method: "post",
+    data: obj
+  });
+}
+
+export function delFlowById(id){
+  return request({
+    url: "/match/delFlowById?id=" + id,
+    method: "get"
+  });
+}
+
+export function getObj(id) {
+  return request({
+    url: "/match/findRoundsById?id=" + id,
+    method: "get"
+  });
+}
+export function getOtherRounds(id) {
+  return request({
+    url: "/match/getOtherRounds?matchId=" + id,
+    method: "get"
+  });
+}
+export function insertScoreRuleVO(obj) {
+  return request({
+    url: "/match/insertScoreRuleVO",
+    method: "post",
+    data: obj
+  });
+}
+export function findScoreRuleByRoundsId(id) {
+  return request({
+    url: "/match/findScoreRuleByRoundsId?id=" + id,
+    method: "get"
+  });
+}
+export function delObj(row) {
+  return request({
+    url: "/match/delFlowById?id=" + row.id,
+    method: "get"
+  });
+}
+
+export function putObj(obj) {
+  return request({
+    url: "/match/updateWebInfo",
+    method: "post",
+    data: obj
+  });
+}
+
+export function remote(type) {
+  return request({
+    url: "/match/dict/type/" + type,
+    method: "get"
+  });
+}
+
+export function groupPage(obj) {
+  return request({
+    url: "/match/pageGroup",
+    method: "get",
+    params: obj
+  });
+}

+ 33 - 0
live-web/src/api/media.js

@@ -0,0 +1,33 @@
+import request from '@/router/axios'
+
+const livePage = (current, size) => {
+  return request.get(`/media/page?current=${current}&size=${size}`);
+};
+
+const mediaM3u8Convert = (id) => {
+  return request.get(`/media/mediaM3u8Convert?key=${id}`);
+};
+
+const findM3u8Speed = (id) => {
+  return request.get(`/media/findM3u8Speed?key=${id}`);
+};
+
+const cutThumbnail = (id, cutTime) => {
+  return request.get(
+    `/media/cutThumbnail?key=${id}&cutTime=${cutTime}&cover=true`
+  );
+};
+
+const del = (id) => {
+  return request.get(
+    `/media/del?id=${id}`
+  );
+};
+
+const findM3u8Url = (id) => {
+  return request.get(
+    `/media/findM3u8Url?key=${id}`
+  );
+};
+
+export { livePage, mediaM3u8Convert, findM3u8Speed, cutThumbnail, del, findM3u8Url };

+ 40 - 0
live-web/src/components/basic-container/main.vue

@@ -0,0 +1,40 @@
+<template>
+  <div class="basic-container"
+       :class="{'basic-container--block':block}">
+    <el-card>
+      <slot></slot>
+    </el-card>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "basicContainer",
+  props: {
+    block: {
+      type: Boolean,
+      default: false,
+    }
+  }
+};
+</script>
+
+<style lang="scss">
+.basic-container {
+  padding: 8px 10px;
+  border-radius: 10px;
+  box-sizing: border-box;
+  .el-card {
+    width: 100%;
+  }
+  &:first-child {
+    padding-top: 0;
+  }
+  &--block {
+    height: 100%;
+    .el-card {
+      height: 100%;
+    }
+  }
+}
+</style>

+ 193 - 0
live-web/src/components/editor/LICENSE

@@ -0,0 +1,193 @@
+source https://gitee.com/elunez/eladmin-web
+
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work
+by the copyright owner or by an individual or Legal Entity authorized to submit
+on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication sent
+to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor for
+the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable copyright license to reproduce, prepare Derivative Works of,
+publicly display, publicly perform, sublicense, and distribute the Work and such
+Derivative Works in Source or Object form.
+
+3. Grant of Patent License.
+
+Subject to the terms and conditions of this License, each Contributor hereby
+grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
+irrevocable (except as stated in this section) patent license to make, have
+made, use, offer to sell, sell, import, and otherwise transfer the Work, where
+such license applies only to those patent claims licensable by such Contributor
+that are necessarily infringed by their Contribution(s) alone or by combination
+of their Contribution(s) with the Work to which such Contribution(s) was
+submitted. If You institute patent litigation against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that the Work or a
+Contribution incorporated within the Work constitutes direct or contributory
+patent infringement, then any patent licenses granted to You under this License
+for that Work shall terminate as of the date such litigation is filed.
+
+4. Redistribution.
+
+You may reproduce and distribute copies of the Work or Derivative Works thereof
+in any medium, with or without modifications, and in Source or Object form,
+provided that You meet the following conditions:
+
+You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source form
+of the Work, excluding those notices that do not pertain to any part of the
+Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any
+Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the
+Derivative Works; within the Source form or documentation, if provided along
+with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents of
+the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works that
+You distribute, alongside or as an addendum to the NOTICE text from the Work,
+provided that such additional attribution notices cannot be construed as
+modifying the License.
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions.
+
+Unless You explicitly state otherwise, any Contribution intentionally submitted
+for inclusion in the Work by You to the Licensor shall be under the terms and
+conditions of this License, without any additional terms or conditions.
+Notwithstanding the above, nothing herein shall supersede or modify the terms of
+any separate license agreement you may have executed with Licensor regarding
+such Contributions.
+
+6. Trademarks.
+
+This License does not grant permission to use the trade names, trademarks,
+service marks, or product names of the Licensor, except as required for
+reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty.
+
+Unless required by applicable law or agreed to in writing, Licensor provides the
+Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
+including, without limitation, any warranties or conditions of TITLE,
+NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
+solely responsible for determining the appropriateness of using or
+redistributing the Work and assume any risks associated with Your exercise of
+permissions under this License.
+
+8. Limitation of Liability.
+
+In no event and under no legal theory, whether in tort (including negligence),
+contract, or otherwise, unless required by applicable law (such as deliberate
+and grossly negligent acts) or agreed to in writing, shall any Contributor be
+liable to You for damages, including any direct, indirect, special, incidental,
+or consequential damages of any character arising as a result of this License or
+out of the use or inability to use the Work (including but not limited to
+damages for loss of goodwill, work stoppage, computer failure or malfunction, or
+any and all other commercial damages or losses), even if such Contributor has
+been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability.
+
+While redistributing the Work or Derivative Works thereof, You may choose to
+offer, and charge a fee for, acceptance of support, warranty, indemnity, or
+other liability obligations and/or rights consistent with this License. However,
+in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on behalf of any other Contributor, and only if You
+agree to indemnify, defend, and hold each Contributor harmless for any liability
+incurred by, or claims asserted against, such Contributor by reason of your
+accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+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 2019 Zheng Jie
+
+   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.

+ 78 - 0
live-web/src/components/editor/index.vue

@@ -0,0 +1,78 @@
+<template>
+  <div class="json-editor">
+    <textarea ref="textarea" />
+  </div>
+</template>
+
+<script>
+  import CodeMirror from 'codemirror'
+  import 'codemirror/lib/codemirror.css'
+  // 替换主题这里需修改名称
+  import 'codemirror/theme/idea.css'
+  import 'codemirror/mode/clike/clike'
+  export default {
+    props: {
+      value: {
+        type: String,
+        required: true
+      },
+      height: {
+        type: String,
+        required: true
+      }
+    },
+    data() {
+      return {
+        editor: false
+      }
+    },
+    watch: {
+      value(value) {
+        const editorValue = this.editor.getValue()
+        if (value !== editorValue) {
+          this.editor.setValue(this.value)
+        }
+      },
+      height(value) {
+        this.editor.setSize('auto', this.height)
+      }
+    },
+    mounted() {
+      this.editor = CodeMirror.fromTextArea(this.$refs.textarea, {
+        mode: 'text/x-java',
+        lineNumbers: true,
+        lint: true,
+        lineWrapping: true,
+        tabSize: 2,
+        cursorHeight: 0.9,
+        // 替换主题这里需修改名称
+        theme: 'idea',
+        readOnly: true
+      })
+      this.editor.setSize('auto', this.height)
+      this.editor.setValue(this.value)
+    },
+    methods: {
+      getValue() {
+        return this.editor.getValue()
+      }
+    }
+  }
+</script>
+
+<style scoped>
+  .json-editor{
+    height: 100%;
+    margin-bottom: 10px;
+  }
+  .json-editor >>> .CodeMirror {
+    font-size: 14px;
+    overflow-y:auto;
+    font-weight:normal
+  }
+  .json-editor >>> .CodeMirror-scroll{
+  }
+  .json-editor >>> .cm-s-rubyblue span.cm-string {
+    color: #F08047;
+  }
+</style>

+ 129 - 0
live-web/src/components/error-page/403.vue

@@ -0,0 +1,129 @@
+<template>
+  <div class="error403">
+    <div class="error403-body-con">
+      <el-card class="box-card">
+        <div class="error403-body-con-title">4
+          <span class="error403-0-span">
+            <i class="icon-quanxian"></i>
+          </span>
+          <span class="error403-key-span">
+            <i class="icon-iconset0216"></i>
+          </span>
+        </div>
+        <p class="error403-body-con-message">You don't have permission</p>
+        <div class="error403-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error403",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error403animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  40% {
+    transform: rotateZ(-20deg);
+  }
+  45% {
+    transform: rotateZ(-15deg);
+  }
+  50% {
+    transform: rotateZ(-20deg);
+  }
+  55% {
+    transform: rotateZ(-15deg);
+  }
+  60% {
+    transform: rotateZ(-20deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error403 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      .error403-0-span {
+        display: inline-block;
+        position: relative;
+        width: 170px;
+        height: 170px;
+        border-radius: 50%;
+        border: 20px solid #ed3f14;
+        color: #ed3f14;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 120px !important;
+          position: absolute;
+          left: 50%;
+          top: 50%;
+          transform: translate(-50%, -50%);
+        }
+      }
+      .error403-key-span {
+        display: inline-block;
+        position: relative;
+        width: 100px;
+        height: 190px;
+        border-radius: 50%;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 190px !important;
+          position: absolute;
+          left: 20px;
+          transform: translate(-50%, -60%);
+          transform-origin: center bottom;
+          animation: error403animation 2.8s ease 0s infinite;
+        }
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 4px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>

+ 91 - 0
live-web/src/components/error-page/404.vue

@@ -0,0 +1,91 @@
+<template>
+  <div class="error404">
+    <div class="error404-body-con">
+      <el-card class="box-card">
+        <div class="error404-body-con-title">4
+          <span>0</span>4</div>
+        <p class="error404-body-con-message">YOU&nbsp;&nbsp;LOOK&nbsp;&nbsp;LOST</p>
+        <div class="error404-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error404",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error404animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  20% {
+    transform: rotateZ(-60deg);
+  }
+  40% {
+    transform: rotateZ(-10deg);
+  }
+  60% {
+    transform: rotateZ(50deg);
+  }
+  80% {
+    transform: rotateZ(-20deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error404 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      span {
+        display: inline-block;
+        color: #19be6b;
+        font-size: 230px;
+        animation: error404animation 3s ease 0s infinite alternate;
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 12px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>

+ 112 - 0
live-web/src/components/error-page/500.vue

@@ -0,0 +1,112 @@
+<template>
+  <div class="error500">
+    <div class="error500-body-con">
+      <el-card class="box-card">
+        <div class="error500-body-con-title">
+          5
+          <span class="error500-0-span">
+            <i class="icon-debug"></i>
+          </span>
+          <span class="error500-0-span">
+            <i class="icon-debug"></i>
+          </span>
+        </div>
+        <p class="error500-body-con-message">Oops! the server is wrong</p>
+        <div class="error500-btn-con">
+          <el-button @click="goHome" size="large" style="width: 200px;" type="text">返回首页</el-button>
+          <el-button @click="backPage" size="large" style="width: 200px;margin-left: 40px;" type="primary">返回上一页</el-button>
+        </div>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "Error500",
+  methods: {
+    backPage() {
+      this.$router.go(-1);
+    },
+    goHome() {
+      this.$router.push({
+        path: "/"
+      });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+@keyframes error500animation {
+  0% {
+    transform: rotateZ(0deg);
+  }
+  20% {
+    transform: rotateZ(-10deg);
+  }
+  40% {
+    transform: rotateZ(5deg);
+  }
+  60% {
+    transform: rotateZ(-5deg);
+  }
+  80% {
+    transform: rotateZ(10deg);
+  }
+  100% {
+    transform: rotateZ(0deg);
+  }
+}
+.error500 {
+  &-body-con {
+    width: 700px;
+    height: 500px;
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    transform: translate(-50%, -50%);
+    &-title {
+      text-align: center;
+      font-size: 240px;
+      font-weight: 700;
+      color: #2d8cf0;
+      height: 260px;
+      line-height: 260px;
+      margin-top: 40px;
+      .error500-0-span {
+        display: inline-block;
+        position: relative;
+        width: 170px;
+        height: 170px;
+        border-radius: 50%;
+        border: 20px solid #ed3f14;
+        color: #ed3f14;
+        margin-right: 10px;
+        i {
+          display: inline-block;
+          font-size: 120px !important;
+          position: absolute;
+          bottom: -43px;
+          left: 20px;
+          transform-origin: center bottom;
+          animation: error500animation 3s ease 0s infinite alternate;
+        }
+      }
+    }
+    &-message {
+      display: block;
+      text-align: center;
+      font-size: 30px;
+      font-weight: 500;
+      letter-spacing: 4px;
+      color: #dddde2;
+    }
+  }
+  &-btn-con {
+    text-align: center;
+    padding: 20px 0;
+    margin-bottom: 40px;
+  }
+}
+</style>
+

+ 296 - 0
live-web/src/components/expert/change.vue

@@ -0,0 +1,296 @@
+<template>
+    <div class="expert">
+        <el-form :model="formExpert" :rules="rules" ref="formExpert" label-width="100px">
+            <el-row >
+                <el-col :span="10">
+                    <el-form-item label="头像" prop="picUrl">
+                        <el-upload class="avatar-uploader" :action="HeadAction" :show-file-list="false"
+                        :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :headers="Headers">
+                            <img v-if="showImg" :src="showImg" class="avatar">
+                            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                    </el-form-item>
+                </el-col>
+                <el-row type="flex" style="margin-top: 10px">
+                    <el-col :span="24">
+                        <el-form-item label="姓名" prop="name">
+                            <el-input v-model="formExpert.name" placeholder="请输入姓名"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row type="flex" style="margin-top: 10px">
+                    <el-col :span="24">
+                        <el-form-item label="手机号" prop="phone">
+                            <el-input v-model="formExpert.phone" placeholder="请输入手机号"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="12">
+                    <el-form-item label="出生日期" prop="birthday">
+                        <el-date-picker type="date" v-model="formExpert.birthday" placeholder="选择出生日期"
+                        format="yyyy 年 MM 月 dd 日" value-format="yyyy-MM-dd" style="width: 100%;"></el-date-picker>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="工作单位" prop="workUnit">
+                        <el-input v-model="formExpert.workUnit" placeholder="请输入工作单位"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="12">
+                    <el-form-item label="工作年限" prop="workYear">
+                        <el-input type="number" v-model="formExpert.workYear" placeholder="请输入工作年限"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="学历" prop="recordSchool">
+                        <el-select v-model="formExpert.recordSchool" placeholder="请选择学历" style="width: 100%;">
+                            <el-option v-for="item in EduList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="24">
+                    <el-form-item label="专家介绍">
+                        <el-input type="textarea" :rows="5" v-model="formExpert.comment"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="24">
+                    <el-form-item label="擅长领域">
+                        <el-input type="textarea" :rows="5" v-model="formExpert.engageProfession"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" style="transform: translateX(38%);">
+                <el-button @click="hideDia">取 消</el-button>
+            <el-button type="primary" @click="SubmitForm('formExpert')">确 定</el-button>
+        </div>
+    </div>
+</template>
+<script>
+import axios from "axios"
+import store from "@/store/index"
+import AddUser from "@/views/match/project_management/add_user"
+import {baseUrl} from "@/config/env"; 
+import {getDetails} from '@/api/admin/user'
+
+export default {
+    components: { AddUser },
+    props: {
+       fromData:{
+           type: Object,
+           require: true
+       }
+    },
+
+    data(){
+        const validateUsername = (rule, value, callback) => {
+        if (!value) {
+            return callback(new Error('请输入用户名'))
+        }
+        const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/
+            if (!reg.test(value)) {
+                return callback(new Error('用户名只能包含字母数字和下划线'))
+            }
+        getDetails(value).then(response => {
+            if (window.boxType === 'edit') callback()
+            let result = response.data.data
+            if (result !== null) {
+            callback(new Error('用户名已经存在'))
+            } else {
+            callback()
+            }
+        })
+        }
+        // 设置密码校验规则
+        const checkPassword = (rule, value, callback) => {
+        if (window.boxType === 'edit') {
+            return callback()
+        }
+        if (!value) {
+            callback(new Error('请输入密码'))
+        } else {
+            callback()
+        }
+        }
+
+        // 设置手机号的验证规则
+        const checkPhone = (rule, value, callback) => {
+        if (!value) {
+            callback(new Error('请输入联系方式'))
+        } else {
+            const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+            if (reg.test(value)) {
+            callback()
+            } else {
+            return callback(new Error('请输入正确的电话'))
+            }
+        }
+        }
+        return{
+            roleCode: 'JUDGES_USER',
+            // 上传地址
+            HeadAction: '',
+            Headers: {
+                Authorization: 'Bearer ' + store.getters.access_token
+            },
+            showImg:'',
+            formExpert: {
+                picUrl: '',
+                name: '',
+                phone: '',
+                birthday: '',
+                workUnit: '',
+                workYear: '',
+                recordSchool: '',
+                userId: '',
+                judgeType: '',
+                comment: '',
+                engageProfession: '',  
+                chidMatchId: '',
+	            secondMatchId: '',
+                matchId: ''
+            },
+            rules: {
+                picUrl: [
+                    {required: true, message: '头像必须上传', trigger: 'change'}
+                ],
+                username: [
+                    {required: true, message: '请输入用户账号', trigger: 'change'},
+                    {validator: validateUsername, trigger: 'blur'}
+                ],
+                password: [
+                     {required: true, message: '请输入账户密码', trigger: 'change'},
+                    {validator: checkPassword, trigger: 'blur'}
+                ],
+                name: [
+                     {required: true, message: '姓名不能为空', trigger: 'change'},
+                ],
+                phone: [
+                    {required: true, message: '手机号不能为空', trigger: 'change'},
+                    {validator: checkPhone, trigger: 'blur'}
+                ],
+                birthday: [
+                    {required: true, message: '出生日期必须选择', trigger: 'change'},
+                ]
+            },
+            data:{
+                judge: {},
+                type: 2
+            },
+            // 学历
+            EduList: [],
+            // 用户
+            UserList: [],
+            judgeType: true,
+            Dialog: false,
+        }
+    },
+    watch: {
+        fromData(val,oldVal){
+            console.log(val);
+        }
+    },
+    created(){
+        this.HeadAction = `${baseUrl}/project/upload`
+        this.getEduList()
+        // this.getState()
+        this.showImg = this.fromData.picUrl
+        this.formExpert.judgeType = 1
+    },
+    methods: {
+        hideDia(){
+            this.$emit('hideDialog')
+        },
+        // 获取学历
+        async getEduList(){
+            let opts = {
+                url: '/admin/dict/getDictItemByType?type=record_school'
+            }
+            let ret = await axios.get(opts.url);
+            if(ret.data.code === 0){
+                this.EduList = ret.data.data;
+            }else{
+                this.EduList = [];
+            }
+        },
+        // 上传前 验证
+        beforeAvatarUpload(file) {
+            const isJPG = file.type === 'image/jpeg';
+            const isPNG = file.type === 'image/png';
+            const isLt1M = file.size / 1024 / 1024 < 1;
+
+            if (!isJPG && !isPNG) {
+                this.$message.error('上传头像图片只能是 JPG或者PNG 格式');
+            }
+            if (!isLt1M) {
+            this.$message.error('上传头像图片大小不能超过 1MB!');
+            }
+            return (isJPG || isPNG) && isLt1M;
+        },
+        // 上传成功后 回调
+        handleAvatarSuccess(res, file) {
+            this.formExpert.picUrl = res.data.domain+res.data.path;
+            this.showImg = res.data.domain+res.data.path
+        },
+        // 监听 选择用户
+        handleChange(val){
+            if(val == "0"){
+                // 选了 新增用户
+                this.Dialog = true;
+            }
+        },
+        // 添加用户 提交
+        async SubmitForm(formName){
+            this.$refs[formName].validate((valid) => {
+                if(valid){
+                    this.data.judge = {
+                        picUrl: this.formExpert.picUrl,
+                        name: this.formExpert.name,
+                        phone: this.formExpert.phone,
+                        birthday: this.formExpert.birthday,
+                        workUnit: this.formExpert.workUnit,
+                        workYear: this.formExpert.workYear,
+                        recordSchool: this.formExpert.recordSchool,
+                        comment: this.formExpert.comment,
+                        engageProfession: this.formExpert.engageProfession,
+                    }
+                    this.$emit("submitJudge",this.data)
+                }
+            })
+        }
+    }
+}
+</script>
+<style scoped>
+.avatar-uploader .el-upload {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+  }
+  .avatar-uploader .el-upload:hover {
+    border-color: #409EFF;
+  }
+  .avatar-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 140px !important;
+    height: 140px !important;
+    line-height: 140px !important;
+    text-align: center;
+  }
+  .avatar {
+    width: 140px !important;
+    height: 140px !important;
+    display: block;
+  }
+</style>

+ 288 - 0
live-web/src/components/expert/expert.vue

@@ -0,0 +1,288 @@
+<template>
+    <div class="expert">
+        <el-form :model="formExpert" :rules="rules" ref="formExpert" label-width="100px">
+            <el-row >
+                <el-col :span="10">
+                    <el-form-item label="头像" prop="picUrl">
+                        <el-upload class="avatar-uploader" :action="HeadAction" :show-file-list="false"
+                        :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :headers="Headers">
+                            <img v-if="showImg" :src="showImg" class="avatar">
+                            <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+                        </el-upload>
+                    </el-form-item>
+                </el-col>
+                <el-row type="flex" style="margin-top: 10px">
+                    <el-col :span="24">
+                        <el-form-item label="账号" prop="username">
+                            <el-input v-model="formExpert.username" placeholder="请输入用户账号"></el-input>
+                        </el-form-item>
+                    </el-col>
+                   
+                </el-row>
+                <el-row type="flex" style="margin-top: 10px">
+                    <el-col :span="24">
+                        <el-form-item label="密码" prop="password">
+                            <el-input type="password" v-model="formExpert.password" placeholder="请输入用户密码"></el-input>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="12">
+                    <el-form-item label="姓名" prop="name">
+                        <el-input v-model="formExpert.name" placeholder="请输入姓名"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="手机号" prop="phone">
+                        <el-input v-model="formExpert.phone" placeholder="请输入手机号"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="12">
+                    <el-form-item label="出生日期" prop="birthday">
+                        <el-date-picker type="date" v-model="formExpert.birthday" placeholder="选择出生日期"
+                        format="yyyy 年 MM 月 dd 日" value-format="yyyy-MM-dd" style="width: 100%;"></el-date-picker>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="工作单位" prop="workUnit">
+                        <el-input v-model="formExpert.workUnit" placeholder="请输入工作单位"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="12">
+                    <el-form-item label="工作年限" prop="workYear">
+                        <el-input type="number" v-model="formExpert.workYear" placeholder="请输入工作年限"></el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                    <el-form-item label="学历" prop="recordSchool">
+                        <el-select v-model="formExpert.recordSchool" placeholder="请选择学历" style="width: 100%;">
+                            <el-option v-for="item in EduList" :key="item.value" :label="item.label" :value="item.value"></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="24">
+                    <el-form-item label="专家介绍">
+                        <el-input type="textarea" :rows="5" v-model="formExpert.comment"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+            <el-row type="flex">
+                <el-col :span="24">
+                    <el-form-item label="擅长领域">
+                        <el-input type="textarea" :rows="5" v-model="formExpert.engageProfession"></el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+        <div slot="footer" style="transform: translateX(38%);">
+                <el-button @click="hideDia">取 消</el-button>
+            <el-button type="primary" @click="SubmitForm('formExpert')">确 定</el-button>
+        </div>
+    </div>
+</template>
+<script>
+import axios from "axios"
+import store from "@/store/index"
+import AddUser from "@/views/match/project_management/add_user"
+import {baseUrl} from "@/config/env"; 
+import {getDetails} from '@/api/admin/user'
+
+export default {
+    components: { AddUser },
+    props: ["isShow"],
+    data(){
+        const validateUsername = (rule, value, callback) => {
+        if (!value) {
+            return callback(new Error('请输入用户名'))
+        }
+        const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/
+            if (!reg.test(value)) {
+                return callback(new Error('用户名只能包含字母数字和下划线'))
+            }
+        getDetails(value).then(response => {
+            if (window.boxType === 'edit') callback()
+            let result = response.data.data
+            if (result !== null) {
+            callback(new Error('用户名已经存在'))
+            } else {
+            callback()
+            }
+        })
+        }
+        // 设置密码校验规则
+        const checkPassword = (rule, value, callback) => {
+        if (window.boxType === 'edit') {
+            return callback()
+        }
+        if (!value) {
+            callback(new Error('请输入密码'))
+        } else {
+            callback()
+        }
+        }
+
+        // 设置手机号的验证规则
+        const checkPhone = (rule, value, callback) => {
+        if (!value) {
+            callback(new Error('请输入联系方式'))
+        } else {
+            const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+            if (reg.test(value)) {
+            callback()
+            } else {
+            return callback(new Error('请输入正确的电话'))
+            }
+        }
+        }
+        return{
+            roleCode: 'JUDGES_USER',
+            // 上传地址
+            HeadAction: '',
+            Headers: {
+                Authorization: 'Bearer ' + store.getters.access_token
+            },
+            showImg:'',
+            formExpert: {
+                picUrl: '',
+                name: '',
+                phone: '',
+                birthday: '',
+                workUnit: '',
+                workYear: '',
+                recordSchool: '',
+                userId: '',
+                judgeType: '',
+                comment: '',
+                engageProfession: '',
+                username: '',
+                password: '',
+                chidMatchId: '',
+	            secondMatchId: '',
+                matchId: ''
+            },
+            rules: {
+                picUrl: [
+                    {required: true, message: '头像必须上传', trigger: 'change'}
+                ],
+                username: [
+                    {required: true, message: '请输入用户账号', trigger: 'change'},
+                    {validator: validateUsername, trigger: 'blur'}
+                ],
+                password: [
+                     {required: true, message: '请输入账户密码', trigger: 'change'},
+                    {validator: checkPassword, trigger: 'blur'}
+                ],
+                name: [
+                     {required: true, message: '姓名不能为空', trigger: 'change'},
+                ],
+                phone: [
+                    {required: true, message: '手机号不能为空', trigger: 'change'},
+                    {validator: checkPhone, trigger: 'blur'}
+                ],
+                birthday: [
+                    {required: true, message: '出生日期必须选择', trigger: 'change'},
+                ]
+            },
+            data:{
+                from: {},
+                type: 1
+            },
+            // 学历
+            EduList: [],
+            // 用户
+            UserList: [],
+            judgeType: true,
+            Dialog: false,
+        }
+    },
+    created(){
+        this.HeadAction = `${baseUrl}/project/upload`
+        this.getEduList()
+        this.formExpert.judgeType = 1
+    },
+    methods: {
+        hideDia(){
+            this.$emit('hideDialog')
+        },
+        // 获取学历
+        async getEduList(){
+            let opts = {
+                url: '/admin/dict/getDictItemByType?type=record_school'
+            }
+            let ret = await axios.get(opts.url);
+            if(ret.data.code === 0){
+                this.EduList = ret.data.data;
+            }else{
+                this.EduList = [];
+            }
+        },
+        // 上传前 验证
+        beforeAvatarUpload(file) {
+            const isJPG = file.type === 'image/jpeg';
+            const isPNG = file.type === 'image/png';
+            const isLt1M = file.size / 1024 / 1024 < 1;
+
+            if (!isJPG && !isPNG) {
+                this.$message.error('上传头像图片只能是 JPG或者PNG 格式');
+            }
+            if (!isLt1M) {
+            this.$message.error('上传头像图片大小不能超过 1MB!');
+            }
+            return (isJPG || isPNG) && isLt1M;
+        },
+        // 上传成功后 回调
+        handleAvatarSuccess(res, file) {
+            this.formExpert.picUrl = res.data.domain+res.data.path;
+            this.showImg = res.data.domain+res.data.path
+        },
+        // 监听 选择用户
+        handleChange(val){
+            if(val == "0"){
+                // 选了 新增用户
+                this.Dialog = true;
+            }
+        },
+        // 添加用户 提交
+        async SubmitForm(formName){
+            this.$refs[formName].validate((valid) => {
+                if(valid){
+                   this.data.from = this.formExpert;
+                   this.$emit("submitJudge",this.data);
+                }
+            })
+        }
+    }
+}
+</script>
+<style scoped>
+.avatar-uploader .el-upload {
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+  }
+  .avatar-uploader .el-upload:hover {
+    border-color: #409EFF;
+  }
+  .avatar-uploader-icon {
+    font-size: 28px;
+    color: #8c939d;
+    width: 140px !important;
+    height: 140px !important;
+    line-height: 140px !important;
+    text-align: center;
+  }
+  .avatar {
+    width: 140px !important;
+    height: 140px !important;
+    display: block;
+  }
+</style>

+ 129 - 0
live-web/src/components/iframe/main.vue

@@ -0,0 +1,129 @@
+<template>
+  <div>
+    <basic-container>
+      <iframe v-if="$route.query.src"
+              :src='$route.query.src'
+              class="iframe"
+              ref="iframe"></iframe>
+      <iframe v-else
+              :src="urlPath"
+              class="iframe"
+              ref="iframe"></iframe>
+    </basic-container>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from 'vuex'
+import NProgress from 'nprogress' // progress bar
+import 'nprogress/nprogress.css' // progress bar style
+export default {
+  name: 'AvueIframe',
+  data () {
+    return {
+      urlPath: this.getUrlPath() //iframe src 路径
+    }
+  },
+  created () {
+    NProgress.configure({ showSpinner: false })
+  },
+  mounted () {
+    this.load()
+    this.resize()
+  },
+  props: ['routerPath'],
+  watch: {
+    $route: function () {
+      this.load()
+    },
+    routerPath: function () {
+      // 监听routerPath变化,改变src路径
+      this.urlPath = this.getUrlPath()
+    }
+  },
+  components: {
+    ...mapGetters(['screen']),
+  },
+  methods: {
+    // 显示等待框
+    show () {
+      NProgress.start()
+    },
+    // 隐藏等待狂
+    hide () {
+      NProgress.done()
+    },
+    // 加载浏览器窗口变化自适应
+    resize () {
+      window.onresize = () => {
+        this.iframeInit()
+      }
+    },
+    // 加载组件
+    load () {
+      this.show()
+      var flag = true //URL是否包含问号
+      if (this.$route.query.src.indexOf('?') == -1) {
+        flag = false
+      }
+      var list = []
+      for (var key in this.$route.query) {
+        if (key != 'src' && key != 'name') {
+          list.push(`${key}= this.$route.query[key]`)
+        }
+      }
+      list = list.join('&').toString()
+      if (flag) {
+        this.$route.query.src = `${this.$route.query.src}${
+          list.length > 0 ? `&list` : ''
+          }`
+      } else {
+        this.$route.query.src = `${this.$route.query.src}${
+          list.length > 0 ? `?list` : ''
+          }`
+      }
+      //超时5s自动隐藏等待框,加强用户体验
+      let time = 5
+      const timeFunc = setInterval(() => {
+        time--
+        if (time == 0) {
+          this.hide()
+          clearInterval(timeFunc)
+        }
+      }, 1000)
+      this.iframeInit()
+    },
+    //iframe窗口初始化
+    iframeInit () {
+      const iframe = this.$refs.iframe
+      const clientHeight = document.documentElement.clientHeight - (screen > 1 ? 200 : 130);
+      iframe.style.height = `${clientHeight}px`
+      if (iframe.attachEvent) {
+        iframe.attachEvent('onload', () => {
+          this.hide()
+        })
+      } else {
+        iframe.onload = () => {
+          this.hide()
+        }
+      }
+    },
+    getUrlPath: function () {
+      //获取 iframe src 路径
+      let url = window.location.href
+      url = url.replace('/myiframe', '')
+      return url
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.iframe {
+  width: 100%;
+  height: 100%;
+  border: 0;
+  overflow: hidden;
+  box-sizing: border-box;
+}
+</style>

+ 16 - 0
live-web/src/config/env.js

@@ -0,0 +1,16 @@
+// 配置编译环境和线上环境之间的切换
+
+const env = process.env
+const baseUrl = process.env.NODE_ENV === 'development' ? '' : '/api'
+// 图表库为avue和pig2套地址
+const iconfontVersion = ['567566_qo5lxgtishg', '667895_v7uduh4zui']
+const iconfontUrl = `//at.alicdn.com/t/font_$key.css`
+const codeUrl = process.env.NODE_ENV === 'development' ? `/code` : `/api/code`
+
+export {
+  baseUrl,
+  iconfontUrl,
+  iconfontVersion,
+  codeUrl,
+  env
+}

+ 135 - 0
live-web/src/const/crud/admin/client.js

@@ -0,0 +1,135 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+import {getObj} from '@/api/admin/client'
+
+const DIC = {
+  vaild: [{
+    label: '否',
+    value: 'false'
+  }, {
+    label: '是',
+    value: 'true'
+  }]
+}
+
+var validateClient = (rule, value, callback) => {
+  getObj(value).then(response => {
+    if (window.boxType === 'edit') {
+      return callback()
+    }
+    const result = response.data.data
+    if (result.length !== 0) {
+      callback(new Error('客户端已存在'))
+    } else {
+      callback()
+    }
+  })
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  searchMenuSpan: 6,
+  align: 'center',
+  column: [{
+    width: 150,
+    label: '编号',
+    prop: 'clientId',
+    align: 'center',
+    sortable: true,
+    rules: [{
+      required: true,
+      message: '请输入clientId',
+      trigger: 'blur'
+    }, {validator: validateClient, trigger: 'blur'}]
+  }, {
+    label: '密钥',
+    prop: 'clientSecret',
+    align: 'center',
+    sortable: true,
+    overHidden: true,
+    width: 120,
+    rules: [{
+      required: true,
+      message: '请输入clientSecret',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '域',
+    prop: 'scope',
+    align: 'center',
+    rules: [{
+      required: true,
+      message: '请输入scope',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '授权模式',
+    prop: 'authorizedGrantTypes',
+    align: 'center',
+    overHidden: true,
+    rules: [{
+      required: true,
+      message: '请输入授权模式',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '回调地址',
+    prop: 'webServerRedirectUri',
+    align: 'center',
+    hide: true
+  }, {
+    label: '权限',
+    prop: 'authorities',
+    align: 'center',
+    hide: true
+  }, {
+    label: '自动放行',
+    prop: 'autoapprove',
+    align: 'center',
+    type: 'radio',
+    border: true,
+    dicData: DIC.vaild,
+    rules: [{
+      required: true,
+      message: '请选择是否放行',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '令牌时效',
+    prop: 'accessTokenValidity',
+    align: 'center',
+  }, {
+    label: '刷新时效',
+    prop: 'refreshTokenValidity',
+    align: 'center',
+  }, {
+    label: '扩展信息',
+    prop: 'additionalInformation',
+    align: 'center',
+    hide: true
+  }, {
+    label: '资源ID',
+    prop: 'resourceIds',
+    align: 'center',
+    hide: true
+  }]
+}

+ 125 - 0
live-web/src/const/crud/admin/dict.js

@@ -0,0 +1,125 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  column: [{
+    label: '类型',
+    prop: 'type',
+    'search': true,
+    editDisabled: true,
+    rules: [{
+      required: true,
+      message: '请输入字典类型',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '描述',
+    prop: 'description',
+    rules: [{
+      required: true,
+      message: '请输入字典描述',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '字典类型',
+    prop: 'system',
+    type: 'select',
+    dicUrl: '/admin/dict/type/dict_type',
+    rules: [{
+      required: true,
+      message: '请输入字典类型',
+      trigger: 'blur'
+    }],
+    search: true
+  }, {
+    label: '备注信息',
+    prop: 'remarks'
+  }, {
+    width: 150,
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    addDisplay: false,
+    editDisabled: true,
+    format: 'yyyy-MM-dd HH:mm',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss'
+  }]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    label: '类型',
+    prop: 'type',
+    addDisabled: true,
+    editDisabled: true
+  }, {
+    width: 150,
+    label: '数据值',
+    prop: 'value',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '标签名',
+    prop: 'label',
+    rules: [{
+      required: true,
+      message: '请输入标签名',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '描述',
+    prop: 'description',
+    rules: [{
+      required: true,
+      message: '请输入字典描述',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '排序',
+    prop: 'sort',
+    type: 'number',
+    rules: [{
+      required: true,
+      message: '请输入排序',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '备注信息',
+    prop: 'remarks'
+  }]
+}

+ 73 - 0
live-web/src/const/crud/admin/log.js

@@ -0,0 +1,73 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  menuWidth: 150,
+  align: 'center',
+  refreshBtn: true,
+  searchMenuSpan: 6,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  addBtn: false,
+  editBtn: false,
+  viewBtn: true,
+  props: {
+    label: 'label',
+    value: 'value'
+  },
+  column: [{
+    label: '类型',
+    prop: 'type',
+    type: 'select',
+    dicUrl: '/admin/dict/type/log_type',
+    search: true
+  }, {
+    label: '标题',
+    prop: 'title'
+  }, {
+    label: 'IP地址',
+    prop: 'remoteAddr'
+  }, {
+    label: '请求方式',
+    prop: 'method'
+  }, {
+    label: '客户端',
+    prop: 'serviceId'
+  }, {
+    width: 80,
+    label: '请求时间',
+    prop: 'time'
+  }, {
+    width: 150,
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    search: true,
+    searchRange: true
+  }, {
+    width: 180,
+    label: '异常日志',
+    prop: 'exception',
+    hide: true
+  }]
+}

+ 288 - 0
live-web/src/const/crud/admin/match/user.js

@@ -0,0 +1,288 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+import {getDetails} from '@/api/admin/user'
+
+const validateUsername = (rule, value, callback) => {
+  if (!value) {
+    return callback(new Error('请输入用户名'))
+  }
+  const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/
+    if (!reg.test(value)) {
+        return callback(new Error('用户名只能包含字母数字和下划线'))
+    }
+  getDetails(value).then(response => {
+    if (window.boxType === 'edit') callback()
+    let result = response.data.data
+    if (result !== null) {
+      callback(new Error('用户名已经存在'))
+    } else {
+      callback()
+    }
+  })
+}
+
+// 设置密码校验规则
+const checkPassword = (rule, value, callback) => {
+  if (window.boxType === 'edit') {
+    return callback()
+  }
+  if (!value) {
+    callback(new Error('请输入密码'))
+  } else {
+    callback()
+  }
+}
+
+// 设置手机号的验证规则
+const checkPhone = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('请输入正确的电话'))
+    }
+  }
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  searchMenuSpan: 6,
+  editBtn: false,
+  delBtn: false,
+  align: 'center',
+  addBtn: false,
+  menuWidth: 120,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'userId',
+    span: 24,
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '用户名',
+    prop: 'username',
+    editDisabled: true,
+    slot: true,
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入用户名'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+      {validator: validateUsername, trigger: 'blur'}
+    ]
+  }, {
+    label: '密码',
+    prop: 'password',
+    type: 'password',
+    value: '',
+    hide: true,
+    span: 24,
+    rules: [{validator: checkPassword, trigger: 'blur'}]
+  }, {
+    label: '手机号',
+    prop: 'phone',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  },{
+      label: '所属部门',
+      prop: 'deptId',
+      formslot: true,
+      slot: true,
+      span: 24,
+      hide: true
+  },
+  {
+    label: '角色',
+    prop: 'role',
+    formslot: false,
+    slot: true,
+    overHidden: true,
+    showColumn: false,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择角色',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '部门',
+    prop: 'deptName',
+    overHidden: true,
+    addDisplay: false,
+    editDisplay: false,
+    span: 24,
+  }, {
+    label: '状态',
+    prop: 'lockFlag',
+    type: 'radio',
+    slot: true,
+    border: true,
+    showColumn: false,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '有效',
+      value: '0'
+    }, {
+      label: '锁定',
+      value: '9'
+    }]
+  }, {
+    width: 120,
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd',
+    showColumn: false,
+    editDisabled: true,
+    addDisplay: false,
+    span: 24
+  },
+  {
+    width: 120,
+    label: '用户类型',
+    prop: 'roleName',
+    editDisabled: true,
+    addDisplay: false,
+    span: 24
+  }]
+}
+
+
+export const userOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  searchMenuSpan: 6,
+  editBtn: false,
+  delBtn: false,
+  align: 'center',
+  addBtn: false,
+  emptyBtn:false,
+  column: [  {
+    fixed: true,
+    label: '用户名',
+    prop: 'username',
+    editDisabled: true,
+    slot: true,
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入用户名'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+      {validator: validateUsername, trigger: 'blur'}
+    ]
+  }, {
+    label: '密码',
+    prop: 'password',
+    type: 'password',
+    value: '',
+    hide: true,
+    span: 24,
+    rules: [{validator: checkPassword, trigger: 'blur'}]
+  }, {
+    label: '手机号',
+    prop: 'phone',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  },{
+      label: '所属部门',
+      prop: 'deptId',
+      formslot: true,
+      slot: true,
+      span: 24,
+      hide: true
+  },  {
+    label: '角色',
+    prop: 'role',
+    formslot: true,
+    slot: true,
+    overHidden: true,
+    editDisabled:true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择角色',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '状态',
+    prop: 'lockFlag',
+    type: 'radio',
+    slot: true,
+    border: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '有效',
+      value: '0'
+    }, {
+      label: '锁定',
+      value: '9'
+    }]
+  } ]
+}

+ 47 - 0
live-web/src/const/crud/admin/matchwebfrontmenu.js

@@ -0,0 +1,47 @@
+export const tableOption = {
+  "border": true,
+  "index": true,
+  "indexLabel": "序号",
+  "stripe": true,
+  "menuAlign": "center",
+  "align": "center",
+  "searchMenuSpan": 6,
+  "column": [
+	  {
+      "type": "input",
+      "label": "id",
+      "prop": "id",
+      hide: true,
+      editDisplay: false,
+      addDisplay: false
+    },	 {
+      "type": "input",
+      "label": "菜单名字",
+      "prop": "menuName"
+    },	  {
+      "type": "input",
+      "label": "菜单路径",
+      "prop": "menuPath"
+    },	  {
+      type: 'radio',
+    slot: true,
+    border: true,
+    addDisplay: true,
+    editDisplay: true,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '显示',
+      value: 0
+    }, {
+      label: '隐藏',
+      value: 1
+    }],
+      "label": "是否启用",
+      "prop": "isUsed"
+    }  ]
+}

+ 89 - 0
live-web/src/const/crud/admin/role.js

@@ -0,0 +1,89 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  editBtn: false,
+  delBtn: false,
+  searchMenuSpan: 6,
+  align: 'center',
+  addBtn: false,
+  viewBtn:true,
+  column: [{
+    label: '角色名称',
+    prop: 'roleName',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '角色名称不能为空',
+      trigger: 'blur'
+    },
+    {
+      min: 3,
+      max: 20,
+      message: '长度在 3 到 20 个字符',
+      trigger: 'blur'
+    }]
+  }, {
+    width: 120,
+    label: '角色标识',
+    prop: 'roleCode',
+    span: 24,
+    editDisabled: true,
+    rules: [{
+      required: true,
+      message: '角色标识不能为空',
+      trigger: 'blur'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      }
+    ]
+  }, {
+    width: 150,
+    label: '角色描述',
+    prop: 'roleDesc',
+    overHidden: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '角色描述不能为空',
+      trigger: 'blur'
+    },
+      {
+        min: 3,
+        max: 100,
+        message: '长度在 3 到 100 个字符',
+        trigger: 'blur'
+      }]
+  }, {
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    editDisplay: false,
+    addDisplay: false,
+    span: 24
+  }]
+}

+ 103 - 0
live-web/src/const/crud/admin/sysform.js

@@ -0,0 +1,103 @@
+export const tableOption = {
+  "border": true,
+  "index": true,
+  "indexLabel": "序号",
+  "stripe": true,
+  "menuAlign": "center",
+  "align": "center",
+  "searchMenuSpan": 6,
+  "column": [
+	  {
+      "type": "input",
+      "label": "控件类型",
+      "prop": "type"
+    },	  {
+      "type": "input",
+      "label": "控件名称",
+      "prop": "name"
+    },	  {
+      "type": "input",
+      "label": "最大值",
+      "prop": "maxInput"
+    },	  {
+      "type": "input",
+      "label": "最小值",
+      "prop": "minInput"
+    },	  {
+      "type": "input",
+      "label": "正则检验",
+      "prop": "expres"
+    },
+   /*    	  {
+      "label": "是否需要附加数据",
+      "prop": "hasExtraData",
+      type: 'radio',
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      rules: [{
+        required: true,
+        message: '请选择状态',
+        trigger: 'blur'
+      }],
+      dicData: [{
+        label: '需要',
+        value: 0
+      }, {
+        label: '不需要',
+        value: 1
+      }]
+    }, */
+      {
+      "label": "是否启用",
+      "prop": "isUse",
+      type: 'radio',
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      rules: [{
+        required: true,
+        message: '请选择状态',
+        trigger: 'blur'
+      }],
+      dicData: [{
+        label: '启用',
+        value: 0
+      }, {
+        label: '停用',
+        value: 1
+      }]
+    }, /* {
+      "label": "是否允许空",
+      "prop": "isNotNull",
+      type: 'radio',
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      rules: [{
+        required: true,
+        message: '请选择状态',
+        trigger: 'blur'
+      }],
+      dicData: [{
+        label: '允许',
+        value: 0
+      }, {
+        label: '不允许',
+        value: 1
+      }]
+    },	 {
+      "type": "input",
+      "label": "id",
+      "prop": "id",
+      hide: true,
+      editDisplay: false,
+      addDisplay: false
+    }  */]
+}

+ 51 - 0
live-web/src/const/crud/admin/token.js

@@ -0,0 +1,51 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  searchMenuSpan: 6,
+  viewBtn: true,
+  addBtn: false,
+  editBtn: false,
+  column: [{
+    label: '用户ID',
+    prop: 'user_id',
+    align: 'center'
+  }, {
+    label: '用户名',
+    prop: 'username',
+    align: 'center'
+  }, {
+    label: '令牌',
+    prop: 'access_token',
+    align: 'center',
+    overHidden: true
+  }, {
+    label: '类型',
+    prop: 'token_type',
+    align: 'center'
+  }, {
+    label: '过期时间',
+    prop: 'expires_in',
+    align: 'center'
+  }]
+}

+ 275 - 0
live-web/src/const/crud/admin/user.js

@@ -0,0 +1,275 @@
+/*
+ *    Copyright (c) 2018-2025, lengleng All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: lengleng (wangiegie@gmail.com)
+ */
+import {getDetails} from '@/api/admin/user'
+
+const validateUsername = (rule, value, callback) => {
+  if (!value) {
+    return callback(new Error('请输入用户名'))
+  }
+  const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/
+    if (!reg.test(value)) {
+        return callback(new Error('用户名只能包含字母数字和下划线'))
+    }
+  getDetails(value).then(response => {
+    if (window.boxType === 'edit') callback()
+    let result = response.data.data
+    if (result !== null) {
+      callback(new Error('用户名已经存在'))
+    } else {
+      callback()
+    }
+  })
+}
+
+// 设置密码校验规则
+const checkPassword = (rule, value, callback) => {
+  if (window.boxType === 'edit') {
+    return callback()
+  }
+  if (!value) {
+    callback(new Error('请输入密码'))
+  } else {
+    callback()
+  }
+}
+
+// 设置手机号的验证规则
+const checkPhone = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('请输入正确的电话'))
+    }
+  }
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  searchMenuSpan: 6,
+  editBtn: false,
+  delBtn: false,
+  align: 'center',
+  addBtn: false,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'userId',
+    span: 24,
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '用户名',
+    prop: 'username',
+    editDisabled: true,
+    slot: true,
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入用户名'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+      {validator: validateUsername, trigger: 'blur'}
+    ]
+  }, {
+    label: '密码',
+    prop: 'password',
+    type: 'password',
+    value: '',
+    hide: true,
+    span: 24,
+    rules: [{validator: checkPassword, trigger: 'blur'}]
+  }, {
+    label: '手机号',
+    prop: 'phone',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  },{
+      label: '所属部门',
+      prop: 'deptId',
+      formslot: true,
+      slot: true,
+      span: 24,
+      hide: true
+  },  {
+    label: '角色',
+    prop: 'role',
+    formslot: true,
+    slot: true,
+    overHidden: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择角色',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '部门',
+    prop: 'deptName',
+    overHidden: true,
+    addDisplay: false,
+    editDisplay: false,
+    span: 24,
+  }, {
+    label: '状态',
+    prop: 'lockFlag',
+    type: 'radio',
+    slot: true,
+    border: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '有效',
+      value: '0'
+    }, {
+      label: '锁定',
+      value: '9'
+    }]
+  }, {
+    width: 120,
+    label: '创建时间',
+    prop: 'createTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd',
+    editDisabled: true,
+    addDisplay: false,
+    span: 24
+  }]
+}
+
+
+export const userOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  searchMenuSpan: 6,
+  editBtn: false,
+  delBtn: false,
+  align: 'center',
+  addBtn: false,
+  emptyBtn:false,
+  column: [  {
+    fixed: true,
+    label: '用户名',
+    prop: 'username',
+    editDisabled: true,
+    slot: true,
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入用户名'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+      {validator: validateUsername, trigger: 'blur'}
+    ]
+  }, {
+    label: '密码',
+    prop: 'password',
+    type: 'password',
+    value: '',
+    hide: true,
+    span: 24,
+    rules: [{validator: checkPassword, trigger: 'blur'}]
+  }, {
+    label: '手机号',
+    prop: 'phone',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  },{
+      label: '所属部门',
+      prop: 'deptId',
+      formslot: true,
+      slot: true,
+      span: 24,
+      hide: true
+  },  {
+    label: '角色',
+    prop: 'role',
+    formslot: true,
+    slot: true,
+    overHidden: true,
+    editDisabled:true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择角色',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '状态',
+    prop: 'lockFlag',
+    type: 'radio',
+    slot: true,
+    border: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '有效',
+      value: '0'
+    }, {
+      label: '锁定',
+      value: '9'
+    }]
+  } ]
+}

+ 57 - 0
live-web/src/const/crud/gen/form.js

@@ -0,0 +1,57 @@
+/*
+ *    Copyright (c) 2018-2025, test All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: test
+ */
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  viewBtn: true,
+  editBtn: false,
+  addBtn: false,
+  searchMenuSpan: 6,
+  column: [
+    {
+      label: 'ID',
+      prop: 'id',
+      hide: true,
+    },
+    {
+      label: '表名称',
+      prop: 'tableName'
+    },
+    {
+      label: '创建时间',
+      prop: 'createTime',
+      type: 'datetime',
+      format: 'yyyy-MM-dd HH:mm:ss',
+      valueFormat: 'yyyy-MM-dd HH:mm:ss'
+    },
+    {
+      label: '表单信息',
+      prop: 'formInfo',
+      overHidden: true,
+      width: 500,
+      type: 'textarea',
+      minRows: 3,
+      row: true,
+      span: 24
+    }
+  ]
+}

+ 201 - 0
live-web/src/const/crud/gen/gen.js

@@ -0,0 +1,201 @@
+/*
+ *    Copyright (c) 2018-2025, test All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: test
+ */
+
+/**
+ * 
+ * @param {校验数据源名} rule 
+ * @param {*} value 
+ * @param {*} callback 
+ */
+var validateDsName = (rule, value, callback) => {
+  var re = /(?=.*[a-z])(?=.*_)/;
+  if (value && (!(re).test(value))) {
+    callback(new Error('数据源名称不合法, 组名_数据源名形式'))
+  } else {
+    callback()
+  }
+}
+
+export const tableOption = {
+  selection: true,
+  border: true,
+  index: true,
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  addBtn: false,
+  editBtn: false,
+  delBtn: false,
+  searchMenuSpan: 6,
+  column: [{
+    label: '表名称',
+    prop: 'tableName',
+    align: 'center'
+  }, {
+    label: '表注释',
+    prop: 'tableComment',
+    align: 'center'
+  }, {
+    label: '表编码',
+    prop: 'tableCollation',
+    align: 'center'
+  }, {
+    label: '索引',
+    prop: 'engine',
+    align: 'center'
+  }, {
+    type: 'datetime',
+    valueFormat: 'timestamp',
+    format: 'yyyy-MM-dd hh:mm:ss',
+    label: '创建时间',
+    prop: 'createTime',
+    align: 'center'
+  }]
+}
+
+export const formOption = {
+  submitBtn: false,
+  emptyBtn: false,
+  submitText: '生成',
+  column: [
+    {
+      label: '表名称',
+      prop: 'tableName',
+      disabled: true
+    }, {
+      label: '包名',
+      prop: 'packageName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '作者',
+      prop: 'author',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '模块',
+      prop: 'moduleName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '表前缀',
+      prop: 'tablePrefix',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '注释',
+      prop: 'comments',
+      placeholder: '可为空,加载表备注'
+    }
+  ]
+}
+export const formBatchOption = {
+  submitText: '生成',
+  column: [
+    {
+      label: '表名称',
+      prop: 'tableName',
+      disabled: true,
+      minRows: 2,
+      type: 'textarea',
+      row: true,
+      span: 24
+    },
+    {
+      label: '包名',
+      prop: 'packageName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '作者',
+      prop: 'author',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '模块',
+      prop: 'moduleName',
+      placeholder: '可为空,加载系统默认配置'
+    }, {
+      label: '注释',
+      prop: 'comments',
+      placeholder: '可为空,加载表备注'
+    }
+  ]
+}
+
+export const tableDsOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  column: [
+    {
+      label: '主键',
+      prop: 'id',
+      hide: true,
+      addDisplay: false,
+      editDisplay: false
+    },
+    {
+      label: '名称',
+      prop: 'name',
+      rules: [
+        { required: true, message: '请输入名称', trigger: 'blur' },
+        { max: 32, message: '长度在 32 个字符', trigger: 'blur' },
+        { validator: validateDsName, trigger: 'blur' }
+      ]
+    },
+    {
+      label: 'jdbcUrl',
+      prop: 'url',
+      type: 'textarea',
+      span: 24,
+      row: true,
+      minRows: 2,
+      overHidden: true,
+      rules: [
+        { required: true, message: '请输入jdbcUrl', trigger: 'blur' }
+      ]
+    },
+    {
+      label: '用户名',
+      prop: 'username',
+      rules: [
+        { required: true, message: '请输入用户名', trigger: 'blur' },
+        { max: 32, message: '长度在 32 个字符', trigger: 'blur' }
+      ]
+    },
+    {
+      label: '密码',
+      prop: 'password',
+      rules: [
+        { required: true, message: '请输入密码', trigger: 'blur' },
+        { max: 32, message: '长度在 32 个字符', trigger: 'blur' }
+      ]
+    },
+    {
+      label: '创建时间',
+      prop: 'createDate',
+      addDisplay: false,
+      editDisplay: false,
+      overHidden: true
+    },
+    {
+      label: '更新时间',
+      prop: 'updateDate',
+      overHidden: true,
+      addDisplay: false,
+      editDisplay: false
+    }
+  ]
+}

+ 323 - 0
live-web/src/const/crud/match/competition.js

@@ -0,0 +1,323 @@
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: "序号",
+  stripe: true,
+  menuAlign: "center",
+  align: "center",
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: "mini",
+  menuWidth: 350,
+  column: [
+    {
+      fixed: true,
+      label: "id",
+      prop: "id",
+      hide: true,
+      editDisplay: false,
+      addDisplay: false
+    },
+    {
+      fixed: true,
+      label: "比赛名称",
+      prop: "name",
+      search: true,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "主办单位",
+      prop: "organizer",
+      value: "",
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "大赛地址",
+      prop: "addr",
+      value: "",
+      showColumn: false,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "赛事联系人",
+      prop: "contactUsername",
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "联系人电话",
+      prop: "contactPhone",
+      addDisplay: true,
+      editDisplay: true,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      width: 120,
+      label: "创建时间",
+      prop: "createTime",
+      type: "datetime",
+      format: "yyyy-MM-dd HH:mm:ss",
+      valueFormat: "yyyy-MM-dd HH:mm:ss",
+      editDisabled: true,
+      addDisplay: false,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "报名开始时间",
+      prop: "beginTime",
+      type: "datetime",
+      format: "yyyy-MM-dd HH:mm:ss",
+      valueFormat: "yyyy-MM-dd HH:mm:ss",
+      addDisplay: true,
+      showColumn: false,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "报名结束时间",
+      prop: "endTime",
+      type: "datetime",
+      format: "yyyy-MM-dd HH:mm:ss",
+      addDisplay: true,
+      valueFormat: "yyyy-MM-dd HH:mm:ss",
+      showColumn: false,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "项目上限数",
+      prop: "maxNum",
+      addDisplay: true,
+      editDisplay: true,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "网站标识",
+      prop: "unionCode",
+      addDisplay: true,
+      editDisplay: true,
+      rules: [
+        {
+          required: true,
+          message: "请输入比赛名称"
+        }
+      ]
+    },
+    {
+      label: "显示电话 ",
+      prop: "isShowPhone",
+      type: "radio",
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      rules: [
+        {
+          required: true,
+          message: "请选择状态",
+          trigger: "blur"
+        }
+      ],
+      dicData: [
+        {
+          label: "显示",
+          value: 0
+        },
+        {
+          label: "隐藏",
+          value: 1
+        }
+      ]
+    },
+    {
+      label: "赛制",
+      prop: "competitionFormat",
+      type: "radio",
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      value: "0",
+      rules: [
+        {
+          required: true,
+          message: "请选择状态",
+          trigger: "blur"
+        }
+      ],
+      dicData: [
+        {
+          label: "轮回赛",
+          value: 1
+        },
+        {
+          label: "淘汰赛",
+          value: 2
+        }
+      ]
+    },
+    {
+      label: "创建分赛",
+      prop: "hasChildMatch",
+      type: "radio",
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      rules: [
+        {
+          required: true,
+          message: "请选择状态",
+          trigger: "blur"
+        }
+      ],
+      dicData: [
+        {
+          label: "允许",
+          value: 0
+        },
+        {
+          label: "不允许",
+          value: 1
+        }
+      ]
+    },
+    {
+      label: "赛事类型",
+      prop: "matchType",
+      type: "radio",
+      slot: true,
+      border: true,
+      addDisplay: true,
+      editDisplay: true,
+      showColumn: false,
+      value: "0",
+      rules: [
+        {
+          required: true,
+          message: "请选择状态",
+          trigger: "blur"
+        }
+      ],
+      dicData: [
+        {
+          label: "主赛",
+          value: 0
+        },
+        {
+          label: "分赛",
+          value: 1
+        }
+      ]
+    },
+    {
+      label: "主赛",
+      prop: "parentMatchId",
+      type: "select",
+      addDisplay: true,
+      showColumn: false,
+      dicUrl: "/match/getParentCompetition"
+    },
+    {
+      label: "奖项设置",
+      prop: "award",
+      type: "textarea",
+      addDisplay: true,
+      span: 24,
+      showColumn: false
+    },
+    {
+      label: "参赛要求",
+      prop: "requirements",
+      type: "textarea",
+      addDisplay: true,
+      span: 24,
+      showColumn: false
+    }
+  ]
+};
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: "序号",
+  stripe: true,
+  menuAlign: "center",
+  align: "center",
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: "mini",
+  column: [
+    {
+      fixed: true,
+      label: "id",
+      prop: "id",
+      hide: true,
+      editDisplay: false,
+      addDisplay: false
+    },
+    {
+      label: "赛道名称",
+      prop: "name"
+    },
+    {
+      width: 150,
+      label: "项目上限",
+      prop: "maxNum",
+      type: "number",
+      rules: [
+        {
+          required: true,
+          message: "请输入数据值",
+          trigger: "blur"
+        }
+      ]
+    }
+  ]
+};

+ 212 - 0
live-web/src/const/crud/match/createMatch.js

@@ -0,0 +1,212 @@
+
+const checkUnidCode = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^[a-zA-Z][a-zA-Z0-9_]*$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('赛事标记只能以字母开头,使用字母数字和下划线'))
+    }
+  }
+}
+
+const checkPhone = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('请输入正确的电话'))
+    }
+  }
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 160,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '比赛名称',
+    prop: 'name',
+    search: true,
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    }
+    ]
+  }, {
+    label: '主办单位',
+    prop: 'organizer',
+    value: '',
+    rules: [
+      {
+        required: true,
+        message: "请输入主办单位"
+      }
+    ]
+
+  }, {
+    label: '大赛地址',
+    prop: 'addr',
+    value: '',
+    showColumn: false,
+    rules: [
+      {
+        required: true,
+        message: "请输入大赛地址"
+      }
+    ]
+
+  },{
+    label: '唯一标记',
+    prop: 'unionCode',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [
+      {
+        required: true,
+        message: "请输入比赛名称"
+      },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+      {validator: checkUnidCode, trigger: 'blur'}
+    ]
+  },{
+      label: '联 系 人',
+      prop: 'contactUsername',
+      rules: [
+        {
+          required: true,
+          message: "请输入赛事联系人"
+        }
+      ]
+  },{
+    label: '联系电话',
+    prop: 'contactPhone',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [
+      {
+        required: true,
+        message: "请输入联系人电话"
+      },
+      {
+        validator: checkPhone,
+        trigger: 'blur'
+      }
+    ]
+  }, {
+    label: '报名开始时间',
+    prop: 'beginTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    showColumn: false,
+    rules: [
+      {
+        required: true,
+        message: "请输入报名开始时间"
+      }
+    ]
+  }, {
+    label: '报名结束时间',
+    prop: 'endTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    showColumn: false,
+    rules: [
+      {
+        required: true,
+        message: "请输入报名结束时间"
+      }
+    ]
+  },
+  {
+    label: '创建分赛',
+    prop: 'hasChildMatch',
+    type: 'radio',
+    slot: true,
+    border: true,
+    addDisplay: true,
+    editDisplay: true,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请选择创建分赛',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '允许',
+      value: 0
+    }, {
+      label: '不允许',
+      value: 1
+    }]
+  }
+]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    label: '赛道名称',
+    prop: 'name',
+    rules: [
+      {
+        required: true,
+        message: "请输入赛道名称"
+      }
+    ]
+  }, {
+    width: 150,
+    label: '最大项目数',
+    prop: 'maxNum',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  },]
+}

+ 94 - 0
live-web/src/const/crud/match/group.js

@@ -0,0 +1,94 @@
+
+export const groupOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 350,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '小组名称',
+    prop: 'groupName',
+    search: true,
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    },
+      {
+        min: 3,
+        max: 100,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      }
+    ]
+  }]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    label: '类型',
+    prop: 'type',
+    addDisabled: true,
+    editDisabled: true
+  }, {
+    width: 150,
+    label: '数据值',
+    prop: 'value',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '标签名',
+    prop: 'label',
+    rules: [{
+      required: true,
+      message: '请输入标签名',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '描述',
+    prop: 'description',
+    rules: [{
+      required: true,
+      message: '请输入字典描述',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '排序',
+    prop: 'sort',
+    type: 'number',
+    rules: [{
+      required: true,
+      message: '请输入排序',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '备注信息',
+    type: 'textarea',
+    prop: 'remarks'
+  }]
+}

+ 125 - 0
live-web/src/const/crud/match/judges.js

@@ -0,0 +1,125 @@
+
+
+export const tableOption = {
+  addBtn: false,
+  editBtn: false,
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 120,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  },{
+    label: '头像',
+    prop: 'picUrl',
+    type: 'upload',
+    listType: 'picture-img',
+    span:24,
+    propsHttp: {
+      // home类似axios中的baseurl会拼接到返回的url前面
+      res:'data',
+    },
+    canvasOption: {
+      text: 'avue',
+      ratio: 0.1
+    },
+    tip: '只能上传jpg/png用户头像,且不超过500kb',
+    action: '/project/upload',
+ }, {
+    fixed: true,
+    label: '姓名',
+    prop: 'name',
+    search: true,
+    searchSpan: 8,
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    },
+      {
+        min: 3,
+        max: 100,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      }
+    ]
+  }, {
+    label: '电话',
+    prop: 'phone',
+    value: '',
+  }, {
+    label: '出生日期',
+    prop: 'birthday',
+    type: 'date',
+    value: '',
+    format: 'yyyy-MM-dd',
+    valueFormat: 'yyyy-MM-dd ',
+    showColumn: false
+
+  },{
+      label: '工作单位',
+      prop: 'workUnit',
+  },{
+    label: '工作年限',
+    prop: 'workYear',
+    addDisplay: true,
+    editDisplay: true,
+  }, {
+    label: '学历',
+    prop: 'recordSchool',
+
+  }, {
+    label: '专家介绍',
+    span: 24,
+    prop: 'comment',
+    type: 'textarea'
+  }, {
+    label: '擅长领域',
+    span: 24,
+    prop: 'engageProfession',
+    type: 'textarea'
+  }]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    label: '赛道名称',
+    prop: 'name',
+  }, {
+    width: 150,
+    label: '最大项目数',
+    prop: 'maxNum',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  },]
+}

+ 144 - 0
live-web/src/const/crud/match/mySubMatch.js

@@ -0,0 +1,144 @@
+
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 350,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  },
+  {
+    label: '主赛',
+    prop: 'parentMatchId',
+    type: 'select',
+    addDisplay: true,
+    showColumn: false,
+    dicUrl: '/match/getParentCompetition',
+    rules: [{
+      required: true,
+      message: '请选择主赛'
+    }
+    ]
+  }, {
+    fixed: true,
+    label: '比赛名称',
+    prop: 'name',
+    search: true,
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    }
+    ]
+  }, {
+    label: '主办单位',
+    prop: 'organizer',
+    value: '',
+    rules: [{
+      required: true,
+      message: '请输入主办单位'
+    }
+    ]
+
+  }, {
+    label: '大赛地址',
+    prop: 'addr',
+    value: '',
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请输入大赛地址'
+    }
+    ]
+
+  },{
+      label: '赛事联系人',
+      prop: 'contactUsername',
+      rules: [{
+        required: true,
+        message: '请输入赛事联系人'
+      }
+      ]
+  },{
+    label: '联系人电话',
+    prop: 'contactPhone',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [{
+      required: true,
+      message: '请输入联系人电话'
+    }
+    ]
+  }, {
+    label: '开始时间',
+    prop: 'beginTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请输入开始时间'
+    }
+    ]
+  }, {
+    label: '结束时间',
+    prop: 'endTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请输入结束时间'
+    }
+    ]
+  }
+]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    label: '赛道名称',
+    prop: 'name',
+  }, {
+    width: 150,
+    label: '最大项目数',
+    prop: 'maxNum',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  },]
+}

+ 134 - 0
live-web/src/const/crud/match/rounds.js

@@ -0,0 +1,134 @@
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 200,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '轮次名称',
+    prop: 'name',
+    search: true,
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    },
+      {
+        min: 3,
+        max: 100,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      }
+    ]
+  }, {
+    fixed: true,
+    label: '轮次状态',
+    prop: 'status',
+    value: '',
+    addDisplay: false,
+    editDisplay: false,
+    dicData: [{
+      label: '已创建',
+      value: 0
+    },{
+      label: '已开启',
+      value: 1
+    }, {
+      label: '已关闭',
+      value: 2
+    }]
+  }, {
+    fixed: true,
+    label: '评分形式',
+    prop: 'type',
+    value: '',
+    type: 'radio',
+    // slot: true,
+    border: true,
+    addDisplay: true,
+    editDisplay: true,
+    showColumn: true,
+    rules: [{
+      required: true,
+      message: '请选择状态',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '网评',
+      value: 1
+    }, {
+      label: '现场评',
+      value: 2
+    }]
+  }]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    label: '类型',
+    prop: 'type',
+    addDisabled: true,
+    editDisabled: true
+  }, {
+    width: 150,
+    label: '数据值',
+    prop: 'value',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '标签名',
+    prop: 'label',
+    rules: [{
+      required: true,
+      message: '请输入标签名',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '描述',
+    prop: 'description',
+    rules: [{
+      required: true,
+      message: '请输入字典描述',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '排序',
+    prop: 'sort',
+    type: 'number',
+    rules: [{
+      required: true,
+      message: '请输入排序',
+      trigger: 'blur'
+    }]
+  }, {
+    label: '备注信息',
+    type: 'textarea',
+    prop: 'remarks'
+  }]
+}

+ 37 - 0
live-web/src/const/crud/match/scene.js

@@ -0,0 +1,37 @@
+
+export const sceneOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  addBtn:false,
+  delBtn:false,
+  editBtn:false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 350,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '赛事名称',
+    prop: 'matchName'
+  }, {
+    fixed: true,
+    label: '轮次名称',
+    prop: 'roundName'
+  }, {
+    fixed: true,
+    label: '小组名称',
+    prop: 'groupName'
+  }]
+}

+ 67 - 0
live-web/src/const/crud/match/score.js

@@ -0,0 +1,67 @@
+
+export const scoreItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  addBtn:false,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 350,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '评分项名称',
+    prop: 'name',
+    rules: [{
+      required: true,
+      message: '请输入比赛名称'
+    },
+      {
+        min: 3,
+        max: 100,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      }
+    ]
+  }, {
+    label: '最高分',
+    prop: 'maxScore',
+    value: '',
+  }, {
+    label: '权重',
+    prop: 'weight',
+    value: '',
+  },{
+    label: '计分方式',
+    prop: 'way',
+    type: 'radio',
+    slot: true,
+    border: true,
+    addDisplay: true,
+    editDisplay: true,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请选择计分方式',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '各评委的平均分',
+      value: 1
+    }, {
+      label: '去掉最高分/最低分求平均分',
+      value: 2
+    }]
+  }]
+}

+ 173 - 0
live-web/src/const/crud/match/second/match.js

@@ -0,0 +1,173 @@
+
+const checkPhone = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('请输入正确的电话'))
+    }
+  }
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 140,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '二级分赛',
+    prop: 'name',
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入二级分赛名称'
+    }
+    ]
+  },{
+    label: '主办单位',
+    prop: 'organizer',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入主办单位'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+    ]
+  }, {
+    label: '大赛地址',
+    prop: 'addr',
+    value: '',
+    type: 'textarea',
+    span: 24,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请输入大赛地址'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+    ]
+  },{
+      label: '联 系 人',
+      prop: 'contactUsername',
+      rules: [{
+        required: true,
+        message: '请输入联系人姓名'
+      },
+        {
+          min: 2,
+          max: 20,
+          message: '长度在 2 到 20 个字符',
+          trigger: 'blur'
+        },
+      ]
+  },{
+    label: '联系电话',
+    prop: 'contactPhone',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  }, {
+    label: '开始时间',
+    prop: 'beginTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    showColumn: true,
+    rules: [{
+      required: true,
+      message: '请选择开始时间'
+    }]
+  }, {
+    label: '结束时间',
+    prop: 'endTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    showColumn: true,
+    rules: [{
+      required: true,
+      message: '请选择结束时间'
+    }]
+  },{
+    label: '项目上限',
+    prop: 'maxNum',
+    type: 'number',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [{
+      required: true,
+      message: '请输入项目上限'
+    }]
+  }]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    label: '赛道名称',
+    prop: 'name', 
+  }, {
+    width: 150,
+    label: '最大项目数',
+    prop: 'maxNum',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  },]
+}

+ 196 - 0
live-web/src/const/crud/match/subMatch.js

@@ -0,0 +1,196 @@
+
+const checkPhone = (rule, value, callback) => {
+  if (!value) {
+    callback(new Error('请输入联系方式'))
+  } else {
+    const reg = /^((13[0-9])|(14[0,1,4-9])|(15[0-3,5-9])|(16[2,5,6,7])|(17[0-8])|(18[0-9])|(19[0-3,5-9]))\d{8}$/
+    if (reg.test(value)) {
+      callback()
+    } else {
+      return callback(new Error('请输入正确的电话'))
+    }
+  }
+}
+
+export const tableOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 140,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    fixed: true,
+    label: '分赛名称',
+    prop: 'name',
+    search: true,
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入分赛名称'
+    }
+    ]
+  },{
+    label: '主办单位',
+    prop: 'organizer',
+    value: '',
+    span: 24,
+    rules: [{
+      required: true,
+      message: '请输入主办单位'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+    ]
+  }, {
+    label: '大赛地址',
+    prop: 'addr',
+    value: '',
+    type: 'textarea',
+    span: 24,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请输入大赛地址'
+    },
+      {
+        min: 3,
+        max: 20,
+        message: '长度在 3 到 20 个字符',
+        trigger: 'blur'
+      },
+    ]
+  },{
+      label: '联 系 人',
+      prop: 'contactUsername',
+      rules: [{
+        required: true,
+        message: '请输入联系人姓名'
+      },
+        {
+          min: 2,
+          max: 20,
+          message: '长度在 2 到 20 个字符',
+          trigger: 'blur'
+        },
+      ]
+  },{
+    label: '联系电话',
+    prop: 'contactPhone',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [{
+      required: true,
+      message: '手机号不能为空',
+      trigger: 'blur'
+    }, {
+      validator: checkPhone,
+      trigger: 'blur'
+    }]
+  }, {
+    label: '开始时间',
+    prop: 'beginTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    showColumn: true,
+    rules: [{
+      required: true,
+      message: '请选择开始时间'
+    }]
+  }, {
+    label: '结束时间',
+    prop: 'endTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd HH:mm:ss',
+    addDisplay: true,
+    valueFormat: 'yyyy-MM-dd HH:mm:ss',
+    showColumn: true,
+    rules: [{
+      required: true,
+      message: '请选择结束时间'
+    }]
+  },{
+    label: '项目上限',
+    prop: 'maxNum',
+    type: 'number',
+    addDisplay: true,
+    editDisplay: true,
+    rules: [{
+      required: true,
+      message: '请输入项目上限'
+    }]
+  },
+  {
+    label: '二级分赛',
+    prop: 'hasChildMatch',
+    type: 'radio',
+    slot: true,
+    border: true,
+    addDisplay: true,
+    editDisplay: true,
+    showColumn: false,
+    rules: [{
+      required: true,
+      message: '请选择是否可以创建二级分赛',
+      trigger: 'blur'
+    }],
+    dicData: [{
+      label: '允许',
+      value: 0
+    }, {
+      label: '不允许',
+      value: 1
+    }]
+  }
+]
+}
+
+export const tableDictItemOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchSize: 'mini',
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    label: '赛道名称',
+    prop: 'name', 
+  }, {
+    width: 150,
+    label: '最大项目数',
+    prop: 'maxNum',
+    rules: [{
+      required: true,
+      message: '请输入数据值',
+      trigger: 'blur'
+    }]
+  },]
+}

+ 237 - 0
live-web/src/const/crud/match/website.js

@@ -0,0 +1,237 @@
+/*
+ *    Copyright (c) 2018-2025, test All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * Neither the name of the pig4cloud.com developer nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * Author: test
+ */
+
+
+export const websiteOption = {
+  labelPosition: "left",
+  labelSuffix: ":",
+  labelWidth: 120,
+  gutter: 0,
+  menuBtn: true,
+  submitBtn: true,
+  submitText: "提交",
+  emptyBtn: true,
+  emptyText: "清空",
+  menuPosition: "center",
+  tabs:true,
+  group:[
+    {
+      icon:'el-icon-info',
+      label: '网站信息',
+      prop: 'group1',
+      column: [  {
+        type: "input",
+        label: "联系人电话",
+        display: true,
+        prop: "phone"
+      },
+      { type: "input", label: "邮箱", display: true, prop: "email" },
+      { type: "textarea", span: 24, label: "地址", display: true, prop: "addr" },
+      // {
+      //   label: "标题图片",
+      //   prop: "bannerUrl",
+      //   type: 'upload',
+      //   listType: 'picture-img',
+      //   propsHttp: {
+      //     // home类似axios中的baseurl会拼接到返回的url前面
+      //     res:'data',
+      //   },
+      //   canvasOption: {
+      //     text: 'avue',
+      //     ratio: 0.1
+      //   },
+      //   tip: '只能上传jpg/png用户头像,且不超过500kb',
+      //   action: '/project/upload'
+      // },
+      {
+        label: "logo图片",
+        prop: "logo",
+        type: 'upload',
+    listType: 'picture-img',
+    propsHttp: {
+      // home类似axios中的baseurl会拼接到返回的url前面
+      res:'data',
+    },
+    canvasOption: {
+      text: 'avue',
+      ratio: 0.1
+    },
+    tip: '只能上传jpg/png,且不超过500kb',
+    action: '/project/upload'
+      },
+      {
+        label: "二维码",
+        prop: "qrcode",
+        type: 'upload',
+    listType: 'picture-img',
+    propsHttp: {
+      // home类似axios中的baseurl会拼接到返回的url前面
+      res:'data',
+    },
+    canvasOption: {
+      text: 'avue',
+      ratio: 0.1
+    },
+    tip: '只能上传jpg/png,且不超过500kb',
+    action: '/project/upload'
+      }]
+    },  {
+      icon:'el-icon-info',
+      label: '赛事信息',
+      prop: 'group2',
+      column: [
+        {
+          type: "textarea",
+          label: "大赛介绍",
+          span: 24,
+          display: true,
+          prop: "competitionIntroduced",
+          rules: [{
+            required: true,
+            message: '请输入大赛介绍'
+          },
+            {
+              min: 3,
+              max: 100,
+              message: '长度在 3 到 20 个字符',
+              trigger: 'blur'
+            }
+          ]
+        },
+        {
+          type: "textarea",
+          label: "参赛条件",
+          display: true,
+          prop: "entryConditions",
+          rules: [{
+            required: true,
+            message: '请输入参赛条件'
+          },
+            {
+              min: 3,
+              max: 100,
+              message: '长度在 3 到 20 个字符',
+              trigger: 'blur'
+            }
+          ]
+        },
+        {
+          type: "textarea",
+          label: "参赛单位",
+          display: true,
+          prop: "participateUnit"
+        },
+        {
+          type: "textarea",
+          label: "奖项说明",
+          display: true,
+          prop: "awardsShow"
+        },
+        {
+          type: "textarea",
+          label: "大赛流程及说明",
+          display: true,
+          prop: "flowNote"
+        },
+        {
+          type: "textarea",
+          label: "其他事项说明",
+          display: true,
+          prop: "otherNote",
+          rules: [{
+            required: true,
+            message: '请输入其他事项说明'
+          },
+            {
+              min: 3,
+              max: 100,
+              message: '长度在 3 到 20 个字符',
+              trigger: 'blur'
+            }
+          ]
+        }]
+    },   {
+      icon:'el-icon-info',
+      label: '赛事流程',
+      prop: 'group3',
+      column: [{
+        label:'流程配置',
+        formslot:true,
+        prop: 'flow',
+        slot:true,
+        span:24
+      }]
+    },   {
+      icon:'el-icon-info',
+      label: '网站菜单',
+      prop: 'group4',
+      column: [{
+        label:'菜单显示',
+        formslot:true,
+        prop: 'menus',
+        slot:true,
+        span:24
+      }]
+    }
+  ]
+};
+
+export const flowOption = {
+  border: true,
+  index: true,
+  indexLabel: '序号',
+  stripe: true,
+  menuAlign: 'center',
+  align: 'center',
+  refreshBtn: false,
+  showClomnuBtn: false,
+  searchMenuSpan: 6,
+  searchSize: 'mini',
+  menuWidth: 350,
+  column: [{
+    fixed: true,
+    label: 'id',
+    prop: 'id',
+    hide: true,
+    editDisplay: false,
+    addDisplay: false
+  }, {
+    type: "input",
+    label: "流程名称",
+    display: true,
+    prop: "name"
+  },  {
+    label: '开始时间',
+    prop: 'startTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd',
+    valueFormat: 'yyyy-MM-dd 00:00:00',
+    addDisplay: true
+  }, {
+    label: '结束时间',
+    prop: 'endTime',
+    type: 'datetime',
+    format: 'yyyy-MM-dd',
+    addDisplay: true,
+    valueFormat: 'yyyy-MM-dd 00:00:00'
+  },{
+    fixed: true,
+    label: '背景色',
+    type:'color',
+    prop: 'color'
+  }]
+}

+ 13 - 0
live-web/src/const/errorCode.js

@@ -0,0 +1,13 @@
+export default {
+  '000': '操作太频繁,请勿重复请求',
+  '401': '当前操作没有权限',
+  '403': '当前操作没有权限',
+  '404': '资源不存在',
+  '417': '未绑定登录账号,请使用密码登录后绑定',
+  '423': '演示环境不能操作,如需了解联系冷冷',
+  '426': '用户名不存在或密码错误',
+  '428': '验证码错误,请重新输入',
+  '429': '请求过频繁',
+  '479': '演示环境,没有权限操作',
+  'default': '系统未知错误,请反馈给管理员'
+}

+ 47 - 0
live-web/src/const/iconList.js

@@ -0,0 +1,47 @@
+export default [
+  {
+    label: '阿里云图标',
+    list: [
+      'icon-quanxianguanli',
+      'icon-yonghuguanli',
+      'icon-jiaoseguanli',
+      'icon-web-icon-',
+      'icon-xitongguanli',
+      'icon-rizhiguanli',
+      'icon-navicon-zdgl',
+      'icon-weibiaoti46',
+      'icon-miyue',
+      'icon-shouji',
+      'icon-miyue',
+      'icon-denglvlingpai',
+      'icon-luyou',
+      'icon-msnui-supervise',
+      'icon-server',
+      'icon-wendang',
+      'icon-gtsquanjushiwufuwuGTS',
+      'icon-caidanguanli',
+      'icon-guanwang',
+      'icon-guanwangfangwen',
+      'icon-guiji',
+      'icon-fensiguanli',
+      'icon-gongzhonghao',
+      'icon-anniu_weixincaidanlianjie',
+      'icon-weixincaidan',
+      'icon-xiaoxiguanli',
+      'icon-zhexiantu',
+      'icon-canshu',
+      'icon-erji-zuhushouye',
+      'icon-pay6zhifu',
+      'icon-zhifuqudaoguanli',
+      'icon-dingdan',
+      'icon-tuikuan',
+      'icon-webicon14',
+      'icon-shouyintai',
+      'icon-wenjianguanli',
+      'icon-mysql',
+      'icon-shejiyukaifa-',
+      'icon-record',
+      'icon-biaodanbiaoqian'
+    ]
+  }
+]

+ 31 - 0
live-web/src/const/website.js

@@ -0,0 +1,31 @@
+export default {
+  title: 'Pig',
+  logo: 'Pig',
+  key: 'pig',   //配置主键,目前用于存储
+  indexTitle: 'pig 快速开发框架',
+  whiteList: ['/login', '/404', '/401', '/lock'], // 配置无权限可以访问的页面
+  whiteTagList: ['/login', '/404', '/401', '/lock' ], // 配置不添加tags页面 ('/advanced-router/mutative-detail/*'——*为通配符)
+  lockPage: '/lock',
+  tokenTime: 6000,
+  infoTitle: 'pig 快速开发框架',
+  statusWhiteList: [428],
+  // 配置首页不可关闭
+  isFirstPage: false,
+  fistPage: {
+    label: '首页',
+    value: '/wel/index',
+    params: {},
+    query: {},
+    group: [],
+    close: false
+  },
+  // 配置菜单的属性
+  menu: {
+    props: {
+      label: 'label',
+      path: 'path',
+      icon: 'icon',
+      children: 'children'
+    }
+  }
+}

+ 17 - 0
live-web/src/error.js

@@ -0,0 +1,17 @@
+import Vue from 'vue'
+
+Vue.config.errorHandler = function (err, vm, info) {
+  Vue.nextTick(() => {
+    if (process.env.NODE_ENV === 'development') {
+      console.group('>>>>>> 错误信息 >>>>>>')
+      console.log(info)
+      console.groupEnd()
+      console.group('>>>>>> Vue 实例 >>>>>>')
+      console.log(vm)
+      console.groupEnd()
+      console.group('>>>>>> Error >>>>>>')
+      console.log(err)
+      console.groupEnd()
+    }
+  })
+}

+ 135 - 0
live-web/src/filters/index.js

@@ -0,0 +1,135 @@
+function pluralize (time, label) {
+  if (time === 1) {
+    return time + label
+  }
+  return time + label + 's'
+}
+
+/**
+ * 日期格式化
+ */
+export function dateFormat (date) {
+  let format = 'yyyy-MM-dd hh:mm:ss'
+  if (date != 'Invalid Date') {
+    var o = {
+      'M+': date.getMonth() + 1, // month
+      'd+': date.getDate(), // day
+      'h+': date.getHours(), // hour
+      'm+': date.getMinutes(), // minute
+      's+': date.getSeconds(), // second
+      'q+': Math.floor((date.getMonth() + 3) / 3), // quarter
+      'S': date.getMilliseconds() // millisecond
+    }
+    if (/(y+)/.test(format)) {
+      format = format.replace(RegExp.$1,
+        (date.getFullYear() + '').substr(4 - RegExp.$1.length))
+    }
+    for (var k in o) {
+      if (new RegExp('(' + k + ')').test(format)) {
+        format = format.replace(RegExp.$1,
+          RegExp.$1.length == 1 ? o[k]
+            : ('00' + o[k]).substr(('' + o[k]).length))
+      }
+    }
+    return format
+  }
+  return ''
+}
+
+export function timeAgo (time) {
+  const between = Date.now() / 1000 - Number(time)
+  if (between < 3600) {
+    return pluralize(~~(between / 60), ' minute')
+  } else if (between < 86400) {
+    return pluralize(~~(between / 3600), ' hour')
+  } else {
+    return pluralize(~~(between / 86400), ' day')
+  }
+}
+
+export function parseTime (time, cFormat) {
+  if (arguments.length === 0) {
+    return null
+  }
+
+  if ((time + '').length === 10) {
+    time = +time * 1000
+  }
+
+  const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    date = new Date(parseInt(time))
+  }
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
+  }
+  const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
+    let value = formatObj[key]
+    if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
+    if (result.length > 0 && value < 10) {
+      value = '0' + value
+    }
+    return value || 0
+  })
+  return time_str
+}
+
+export function formatTime (time, option) {
+  time = +time * 1000
+  const d = new Date(time)
+  const now = Date.now()
+
+  const diff = (now - d) / 1000
+
+  if (diff < 30) {
+    return '刚刚'
+  } else if (diff < 3600) { // less 1 hour
+    return Math.ceil(diff / 60) + '分钟前'
+  } else if (diff < 3600 * 24) {
+    return Math.ceil(diff / 3600) + '小时前'
+  } else if (diff < 3600 * 24 * 2) {
+    return '1天前'
+  }
+  if (option) {
+    return parseTime(time, option)
+  } else {
+    return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
+  }
+}
+
+/* 数字 格式化 */
+export function nFormatter (num, digits) {
+  const si = [
+    { value: 1E18, symbol: 'E' },
+    { value: 1E15, symbol: 'P' },
+    { value: 1E12, symbol: 'T' },
+    { value: 1E9, symbol: 'G' },
+    { value: 1E6, symbol: 'M' },
+    { value: 1E3, symbol: 'k' }
+  ]
+  for (let i = 0; i < si.length; i++) {
+    if (num >= si[i].value) {
+      return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
+    }
+  }
+  return num.toString()
+}
+
+export function html2Text (val) {
+  const div = document.createElement('div')
+  div.innerHTML = val
+  return div.textContent || div.innerText
+}
+
+export function toThousandslsFilter (num) {
+  return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
+}

+ 77 - 0
live-web/src/main.js

@@ -0,0 +1,77 @@
+import 'babel-polyfill'
+import 'classlist-polyfill'
+import Vue from 'vue'
+import axios from './router/axios'
+import VueAxios from 'vue-axios'
+import App from './App'
+import ElementUI from 'element-ui'
+import 'element-ui/lib/theme-chalk/index.css'
+import Avue from '@smallwei/avue'
+import '@smallwei/avue/lib/index.css'
+import AvueFormDesign from '@sscfaith/avue-form-design'
+import './permission' // 权限
+import './error' // 日志
+import router from './router/router'
+import store from './store'
+import { loadStyle } from './util/util'
+import * as urls from '@/config/env'
+import {
+  iconfontUrl,
+  iconfontVersion
+} from '@/config/env'
+import * as filters from './filters' // 全局filter
+import './styles/common.scss'
+import basicContainer from './components/basic-container/main'
+
+import 'quill/dist/quill.core.css'
+import 'quill/dist/quill.snow.css'
+import 'quill/dist/quill.bubble.css'
+
+import VueQuillEditor from 'vue-quill-editor'
+
+Vue.use(VueQuillEditor)
+
+
+window.axios = axios
+Vue.use(VueAxios, axios)
+
+Vue.use(ElementUI, {
+  size: 'small',
+  menuType: 'text'
+})
+
+Vue.use(Avue, {
+  size: 'small',
+  menuType: 'text'
+})
+
+Vue.use(router)
+
+Vue.use(AvueFormDesign);
+
+// 注册全局容器
+Vue.component('basicContainer', basicContainer)
+
+// 加载相关url地址
+Object.keys(urls).forEach(key => {
+  Vue.prototype[key] = urls[key]
+})
+
+//加载过滤器
+Object.keys(filters).forEach(key => {
+  Vue.filter(key, filters[key])
+})
+
+// 动态加载阿里云字体库
+iconfontVersion.forEach(ele => {
+  console.log(iconfontUrl.replace('$key', ele))
+  loadStyle(iconfontUrl.replace('$key', ele))
+})
+
+Vue.config.productionTip = false
+
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app')

+ 168 - 0
live-web/src/mixins/color.js

@@ -0,0 +1,168 @@
+import { mapGetters } from "vuex";
+const version = require("element-ui/package.json").version; // element-ui version from node_modules
+const ORIGINAL_THEME = "#409EFF"; // default color
+export default function () {
+  return {
+    data() {
+      return {
+        themeVal: ORIGINAL_THEME
+      }
+    },
+    created() {
+      this.themeVal = this.theme;
+    },
+    watch: {
+      themeVal(val, oldVal) {
+        this.$store.commit("SET_THEME", val);
+        this.updateTheme(val, oldVal);
+      }
+    },
+    computed: {
+      ...mapGetters(["theme"])
+    },
+    methods: {
+      updateTheme(val, oldVal) {
+        if (typeof val !== "string") return;
+        const head = document.getElementsByTagName("head")[0];
+        const themeCluster = this.getThemeCluster(val.replace("#", ""));
+        const originalCluster = this.getThemeCluster(oldVal.replace("#", ""));
+        const getHandler = (variable, id) => {
+          return () => {
+            const originalCluster = this.getThemeCluster(
+              ORIGINAL_THEME.replace("#", "")
+            );
+            const newStyle = this.updateStyle(
+              this[variable],
+              originalCluster,
+              themeCluster
+            );
+
+            let styleTag = document.getElementById(id);
+            if (!styleTag) {
+              styleTag = document.createElement("style");
+              styleTag.setAttribute("id", id);
+              head.appendChild(styleTag);
+            }
+            styleTag.innerText = newStyle;
+          };
+        };
+
+        const chalkHandler = getHandler("chalk", "chalk-style");
+
+        if (!this.chalk) {
+          const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`;
+          this.getCSSString(url, chalkHandler, "chalk");
+        } else {
+          chalkHandler();
+        }
+
+        const link = [].slice.call(
+          document.getElementsByTagName("head")[0].getElementsByTagName("link")
+        );
+        for (let i = 0; i < link.length; i++) {
+          const style = link[i];
+          if (style.href.includes('css')) {
+            this.getCSSString(style.href, innerText => {
+              const originalCluster = this.getThemeCluster(
+                ORIGINAL_THEME.replace("#", "")
+              );
+              const newStyle = this.updateStyle(
+                innerText,
+                originalCluster,
+                themeCluster
+              );
+              let styleTag = document.getElementById(i);
+              if (!styleTag) {
+                styleTag = document.createElement("style");
+                styleTag.id = i;
+                styleTag.innerText = newStyle;
+                head.appendChild(styleTag);
+              }
+            });
+          }
+        }
+
+        const styles = [].slice.call(document.querySelectorAll("style"))
+
+        styles.forEach(style => {
+          const {
+            innerText
+          } = style;
+          if (typeof innerText !== "string") return;
+          style.innerText = this.updateStyle(
+            innerText,
+            originalCluster,
+            themeCluster
+          );
+        });
+      },
+      updateStyle(style, oldCluster, newCluster) {
+        let newStyle = style;
+        oldCluster.forEach((color, index) => {
+          newStyle = newStyle.replace(new RegExp(color, "ig"), newCluster[index]);
+        });
+        return newStyle;
+      },
+
+      getCSSString(url, callback, variable) {
+        const xhr = new XMLHttpRequest();
+        xhr.onreadystatechange = () => {
+          if (xhr.readyState === 4 && xhr.status === 200) {
+            if (variable) {
+              this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, "");
+            }
+            callback(xhr.responseText);
+          }
+        };
+        xhr.open("GET", url);
+        xhr.send();
+      },
+
+      getThemeCluster(theme) {
+        const tintColor = (color, tint) => {
+          let red = parseInt(color.slice(0, 2), 16);
+          let green = parseInt(color.slice(2, 4), 16);
+          let blue = parseInt(color.slice(4, 6), 16);
+
+          if (tint === 0) {
+            // when primary color is in its rgb space
+            return [red, green, blue].join(",");
+          } else {
+            red += Math.round(tint * (255 - red));
+            green += Math.round(tint * (255 - green));
+            blue += Math.round(tint * (255 - blue));
+
+            red = red.toString(16);
+            green = green.toString(16);
+            blue = blue.toString(16);
+
+            return `#${red}${green}${blue}`;
+          }
+        };
+
+        const shadeColor = (color, shade) => {
+          let red = parseInt(color.slice(0, 2), 16);
+          let green = parseInt(color.slice(2, 4), 16);
+          let blue = parseInt(color.slice(4, 6), 16);
+
+          red = Math.round((1 - shade) * red);
+          green = Math.round((1 - shade) * green);
+          blue = Math.round((1 - shade) * blue);
+
+          red = red.toString(16);
+          green = green.toString(16);
+          blue = blue.toString(16);
+
+          return `#${red}${green}${blue}`;
+        };
+
+        const clusters = [theme];
+        for (let i = 0; i <= 9; i++) {
+          clusters.push(tintColor(theme, Number((i / 10).toFixed(2))));
+        }
+        clusters.push(shadeColor(theme, 0.1));
+        return clusters;
+      }
+    }
+  }
+}

+ 110 - 0
live-web/src/page/index/index.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="avue-contail"
+       :class="{'avue--collapse':isCollapse}">
+    <div class="avue-header">
+      <!-- 顶部导航栏 -->
+      <top/>
+    </div>
+
+    <div class="avue-layout">
+      <div class="avue-left">
+        <!-- 左侧导航栏 -->
+        <sidebar/>
+      </div>
+      <div class="avue-main">
+        <!-- 顶部标签卡 -->
+        <tags/>
+        <!-- 主体视图层 -->
+        <el-scrollbar style="height:100%">
+          <keep-alive>
+            <router-view class="avue-view"
+                         v-if="$route.meta.$keepAlive"/>
+          </keep-alive>
+          <router-view class="avue-view"
+                       v-if="!$route.meta.$keepAlive"/>
+        </el-scrollbar>
+
+      </div>
+    </div>
+    <div class="avue-shade"
+         @click="showCollapse"></div>
+  </div>
+</template>
+
+<script>
+  import {mapGetters} from 'vuex'
+  import tags from './tags'
+  import top from './top/'
+  import sidebar from './sidebar/'
+  import admin from '@/util/admin';
+  import {validatenull} from '@/util/validate';
+  import {calcDate} from '@/util/date.js';
+  import {getStore} from '@/util/store.js';
+
+  export default {
+    components: {
+      top,
+      tags,
+      sidebar
+    },
+    name: 'index',
+    data() {
+      return {
+        //刷新token锁
+        refreshLock: false,
+        //刷新token的时间
+        refreshTime: '',
+      }
+    },
+    created() {
+      //实时检测刷新token
+      this.refreshToken()
+    },
+    destroyed() {
+      clearInterval(this.refreshTime)
+    },
+    mounted() {
+      this.init()
+    },
+    computed: mapGetters(['userInfo', 'isLock', 'isCollapse', 'website', 'expires_in']),
+    props: [],
+    methods: {
+      showCollapse() {
+        this.$store.commit("SET_COLLAPSE")
+      },
+      // 屏幕检测
+      init() {
+        this.$store.commit('SET_SCREEN', admin.getScreen())
+        window.onresize = () => {
+          setTimeout(() => {
+            this.$store.commit('SET_SCREEN', admin.getScreen())
+          }, 0);
+        }
+      },
+      // 实时检测刷新token
+      refreshToken() {
+        this.refreshTime = setInterval(() => {
+          const token = getStore({
+            name: 'access_token',
+            debug: true,
+          });
+
+          if (validatenull(token)) {
+            return;
+          }
+
+          if (this.expires_in <= 1000 && !this.refreshLock) {
+            this.refreshLock = true
+            this.$store
+              .dispatch('RefreshToken')
+              .catch(() => {
+                clearInterval(this.refreshTime)
+              });
+            this.refreshLock = false
+          }
+          this.$store.commit("SET_EXPIRES_IN", this.expires_in - 10);
+        }, 10000);
+      }
+    }
+  }
+</script>

+ 3 - 0
live-web/src/page/index/layout.vue

@@ -0,0 +1,3 @@
+<template>
+  <router-view></router-view>
+</template>

+ 84 - 0
live-web/src/page/index/logo.vue

@@ -0,0 +1,84 @@
+<template>
+  <div class="avue-logo">
+    <transition name="fade">
+      <span v-if="keyCollapse"
+            class="avue-logo_subtitle"
+            key="0">
+        {{website.logo}}
+      </span>
+    </transition>
+    <transition-group name="fade">
+      <template v-if="!keyCollapse">
+        <span v-if="matchList" class="avue-logo_title"
+          key="1">{{matchInfo.name}} </span>
+        <span v-else class="avue-logo_title"
+          key="1">微纳赛事管理系统</span>
+      </template>
+    </transition-group>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import { getMyManagerMatch, getMyManagerMatchList } from "@/util/cookie"
+export default {
+  name: "logo",
+  data() {
+    return {
+      matchList: [],
+      matchId: "",
+      matchInfo: {}
+    };
+  },
+  created() {
+    this.matchList = getMyManagerMatchList()
+    this.matchId = getMyManagerMatch()
+    this.matchInfo = this.matchList ? this.matchList.filter(el => el.id == this.matchId)[0] : null
+  },
+  computed: {
+    ...mapGetters(["website", "keyCollapse"])
+  },
+  methods: {}
+};
+</script>
+
+<style lang="scss">
+.fade-leave-active {
+  transition: opacity 0.2s;
+}
+.fade-enter-active {
+  transition: opacity 2.5s;
+}
+.fade-enter,
+.fade-leave-to {
+  opacity: 0;
+}
+.avue-logo {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 240px;
+  height: 64px;
+  line-height: 64px;
+  background-color: #20222a;
+  font-size: 20px;
+  overflow: hidden;
+  box-sizing: border-box;
+  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.15);
+  color: rgba(255, 255, 255, 0.8);
+  z-index: 1024;
+  &_title {
+    display: block;
+    text-align: center;
+    font-weight: 300;
+    font-size: 16px;
+  }
+  &_subtitle {
+    display: block;
+    text-align: center;
+    font-size: 18px;
+    font-weight: bold;
+    color: #fff;
+  }
+}
+</style>

+ 8 - 0
live-web/src/page/index/sidebar/config.js

@@ -0,0 +1,8 @@
+export default {
+  propsDefault: {
+    label: 'label',
+    path: 'path',
+    icon: 'icon',
+    children: 'children'
+  }
+}

+ 53 - 0
live-web/src/page/index/sidebar/index.vue

@@ -0,0 +1,53 @@
+<template>
+  <div class="avue-sidebar">
+    <logo></logo>
+    <el-scrollbar style="height:100%">
+      <div v-if="validatenull(menu)"
+           class="avue-sidebar--tip">没有发现菜单
+      </div>
+      <el-menu unique-opened
+               :default-active="nowTagValue"
+               mode="vertical"
+               :show-timeout="200"
+               :collapse="keyCollapse">
+        <sidebar-item :menu="menu"
+                      :screen="screen"
+                      first
+                      :props="website.menu.props"
+                      :collapse="keyCollapse"></sidebar-item>
+      </el-menu>
+    </el-scrollbar>
+  </div>
+</template>
+
+<script>
+  import {mapGetters} from "vuex";
+  import logo from "../logo";
+  import sidebarItem from "./sidebarItem";
+
+  export default {
+    name: "sidebar",
+    components: {sidebarItem, logo},
+    data() {
+      return {};
+    },
+    created() {
+      this.$store.dispatch("GetMenu", {type: true, id: -1}).then(data => {
+        if (data.length === 0) return;
+        this.$router.$avueRouter.formatRoutes(data, true);
+      });
+    },
+    computed: {
+      ...mapGetters(["website", "menu", "tag", "keyCollapse", "screen"]),
+      nowTagValue: function () {
+        return this.$router.$avueRouter.getValue(this.$route);
+      }
+    },
+    mounted() {
+    },
+    methods: {}
+  };
+</script>
+<style lang="scss" scoped>
+</style>
+

+ 121 - 0
live-web/src/page/index/sidebar/sidebarItem.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="menu-wrapper">
+    <template v-for="item in menu">
+      <el-menu-item v-if="validatenull(item[childrenKey]) && vaildRoles(item)"
+                    :index="item[pathKey]"
+                    @click="open(item)"
+                    :key="item[labelKey]"
+                    :class="{'is-active':vaildAvtive(item)}">
+        <i :class="item[iconKey]"></i>
+        <span slot="title"
+              :alt="item[pathKey]">{{item[labelKey]}}</span>
+      </el-menu-item>
+      <el-submenu v-else-if="!validatenull(item[childrenKey])&&vaildRoles(item)"
+                  :index="item[pathKey]"
+                  :key="item[labelKey]">
+        <template slot="title">
+          <i :class="item[iconKey]"></i>
+          <span slot="title"
+                :class="{'el-menu--display':collapse && first}">{{item[labelKey]}}</span>
+        </template>
+        <template v-for="(child,cindex) in item[childrenKey]">
+          <el-menu-item :index="child[pathKey],cindex"
+                        @click="open(child)"
+                        :class="{'is-active':vaildAvtive(child)}"
+                        v-if="validatenull(child[childrenKey])"
+                        :key="child[labelKey]">
+            <i :class="child[iconKey]"></i>
+            <span slot="title">{{child[labelKey]}}</span>
+          </el-menu-item>
+          <sidebar-item v-else
+                        :menu="[child]"
+                        :key="cindex"
+                        :props="props"
+                        :screen="screen"
+                        :collapse="collapse"></sidebar-item>
+        </template>
+      </el-submenu>
+    </template>
+  </div>
+</template>
+<script>
+import { mapGetters } from "vuex";
+import { validatenull } from "@/util/validate";
+import config from "./config.js";
+export default {
+  name: "sidebarItem",
+  data() {
+    return {
+      config: config
+    };
+  },
+  props: {
+    menu: {
+      type: Array
+    },
+    screen: {
+      type: Number
+    },
+    first: {
+      type: Boolean,
+      default: false
+    },
+    props: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    collapse: {
+      type: Boolean
+    }
+  },
+  created() {},
+  mounted() {},
+  computed: {
+    ...mapGetters(["roles"]),
+    labelKey() {
+      return this.props.label || this.config.propsDefault.label;
+    },
+    pathKey() {
+      return this.props.path || this.config.propsDefault.path;
+    },
+    iconKey() {
+      return this.props.icon || this.config.propsDefault.icon;
+    },
+    childrenKey() {
+      return this.props.children || this.config.propsDefault.children;
+    },
+    nowTagValue() {
+      return this.$router.$avueRouter.getValue(this.$route);
+    }
+  },
+  methods: {
+    vaildAvtive(item) {
+      const groupFlag = (item["group"] || []).some(ele =>
+        this.$route.path.includes(ele)
+      );
+      return this.nowTagValue === item[this.pathKey] || groupFlag;
+    },
+    vaildRoles(item) {
+      item.meta = item.meta || {};
+      return item.meta.roles ? item.meta.roles.includes(this.roles) : true;
+    },
+    validatenull(val) {
+      return validatenull(val);
+    },
+    open(item) {
+      if (this.screen <= 1) this.$store.commit("SET_COLLAPSE");
+      this.$router.$avueRouter.group = item.group;
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          name: item[this.labelKey],
+          src: item[this.pathKey]
+        }),
+        query: item.query
+      }).catch(() => {});
+    }
+  }
+};
+</script>
+

+ 161 - 0
live-web/src/page/index/tags.vue

@@ -0,0 +1,161 @@
+<template>
+  <div class="avue-tags"
+       v-if="showTag">
+    <!-- tag盒子 -->
+    <div v-if="contextmenuFlag"
+         class="avue-tags__contentmenu"
+         :style="{left:contentmenuX+'px',top:contentmenuY+'px'}">
+      <div class="item"
+           @click="closeOthersTags">关闭其他</div>
+      <div class="item"
+           @click="closeAllTags">关闭全部</div>
+    </div>
+    <div class="avue-tags__box"
+         :class="{'avue-tags__box--close':!website.isFirstPage}">
+      <el-tabs v-model="active"
+               type="card"
+               @contextmenu.native="handleContextmenu"
+               :closable="tagLen!==1"
+               @tab-click="openTag"
+               @edit="menuTag">
+        <el-tab-pane :key="item.value"
+                     v-for="item in tagList"
+                     :label="item.label"
+                     :name="item.value">
+        </el-tab-pane>
+
+      </el-tabs>
+      <el-dropdown class="avue-tags__menu">
+        <el-button type="primary"
+                   >
+          更多
+          <i class="el-icon-arrow-down el-icon--right"></i>
+        </el-button>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item @click.native="closeOthersTags">关闭其他</el-dropdown-item>
+          <el-dropdown-item @click.native="closeAllTags">关闭全部</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+
+  </div>
+</template>
+<script>
+import { mapGetters, mapState } from "vuex";
+export default {
+  name: "tags",
+  data() {
+    return {
+      active: "",
+      contentmenuX: "",
+      contentmenuY: "",
+      contextmenuFlag: false
+    };
+  },
+  created() {},
+  mounted() {
+    this.setActive();
+  },
+  watch: {
+    tag() {
+      this.setActive();
+    },
+    contextmenuFlag(contextmenuShow) {
+      // 只在右键菜单显示的时候监听鼠标点击事件
+      if (contextmenuShow) {
+        window.addEventListener("mousedown", this.watchContextmenu);
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(["tagWel", "tagList", "tag", "website"]),
+    ...mapState({
+      showTag: state => state.common.showTag
+    }),
+    tagLen() {
+      return this.tagList.length || 0;
+    }
+  },
+  methods: {
+    watchContextmenu(e) {
+      if (!this.$el.contains(e.target)) {
+        this.contextmenuFlag = false;
+        window.removeEventListener("mousedown", this.watchContextmenu);
+      }
+    },
+    handleContextmenu(event) {
+      let target = event.target;
+      // 解决 https://github.com/d2-projects/d2-admin/issues/54
+      let flag = false;
+      if (target.className.indexOf("el-tabs__item") > -1) flag = true;
+      else if (target.parentNode.className.indexOf("el-tabs__item") > -1) {
+        target = target.parentNode;
+        flag = true;
+      }
+      if (flag) {
+        event.preventDefault();
+        event.stopPropagation();
+        this.contentmenuX = event.clientX;
+        this.contentmenuY = event.clientY;
+        this.tagName = target.getAttribute("aria-controls").slice(5);
+        this.contextmenuFlag = true;
+      }
+    },
+    //激活当前选项
+    setActive() {
+      this.active = this.tag.value;
+    },
+    menuTag(value, action) {
+      if (action === "remove") {
+        let { tag, key } = this.findTag(value);
+        this.$store.commit("DEL_TAG", tag);
+        if (tag.value === this.tag.value) {
+          tag = this.tagList[key === 0 ? key : key - 1]; //如果关闭本标签让前推一个
+          this.openTag(tag);
+        }
+      }
+    },
+    openTag(item) {
+      let tag;
+      if (item.name) {
+        tag = this.findTag(item.name).tag;
+      } else {
+        tag = item;
+      }
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          name: tag.label,
+          src: tag.value
+        }),
+        query: tag.query
+      });
+    },
+    closeOthersTags() {
+      this.contextmenuFlag = false;
+      this.$store.commit("DEL_TAG_OTHER");
+    },
+    findTag(value) {
+      let tag, key;
+      this.tagList.map((item, index) => {
+        if (item.value === value) {
+          tag = item;
+          key = index;
+        }
+      });
+      return { tag: tag, key: key };
+    },
+    closeAllTags() {
+      this.contextmenuFlag = false;
+      this.$store.commit("DEL_ALL_TAG");
+      this.$router.push({
+        path: this.$router.$avueRouter.getPath({
+          src: this.tagWel.value
+        }),
+        query: this.tagWel.query
+      });
+    }
+  }
+};
+</script>
+
+

+ 145 - 0
live-web/src/page/index/top/index.vue

@@ -0,0 +1,145 @@
+<template>
+  <div class="avue-top">
+    <div class="top-bar__left">
+      <div class="avue-breadcrumb"
+           :class="[{ 'avue-breadcrumb--active': isCollapse }]"
+           v-if="showCollapse">
+        <i class="icon-navicon"
+           @click="setCollapse"></i>
+      </div>
+    </div>
+    <div class="top-bar__title">
+      <div class="top-bar__item top-bar__item--show"
+           v-if="showMenu">
+        <top-menu></top-menu>
+      </div>
+      <!-- 选择赛事 -->
+      <div class="top-bar__item top-bar__item--show" v-if="options">
+        <div class="select_match_top">
+          <el-select v-model="selMatchId" filterable @change="onChangeMatch" style="width:100%" placeholder="请选择要管理的赛事">
+            <el-option
+              v-for="item in options"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+              >
+            </el-option>
+          </el-select>
+        </div>
+      </div>
+      
+    </div>
+    <div class="top-bar__right">
+      <el-tooltip v-if="showLock"
+                  effect="dark"
+                  content="锁屏"
+                  placement="bottom">
+      </el-tooltip>
+      <el-tooltip v-if="showFullScren"
+                  effect="dark"
+                  :content="isFullScreen?'退出全屏':'全屏'"
+                  placement="bottom">
+        <div class="top-bar__item">
+          <i :class="isFullScreen?'icon-zuixiaohua':'icon-quanpingzuidahua'"
+             @click="handleScreen"></i>
+        </div>
+      </el-tooltip>
+      <el-dropdown>
+        <span class="el-dropdown-link">
+          {{userInfo.username}}
+          <i class="el-icon-arrow-down el-icon--right"></i>
+        </span>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item>
+            <router-link to="/">首页</router-link>
+          </el-dropdown-item>
+          <el-dropdown-item>
+            <router-link to="/info/index">个人信息</router-link>
+          </el-dropdown-item>
+          <el-dropdown-item @click.native="logout"
+                            divided>退出系统
+          </el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+  </div>
+</template>
+<script>
+  import {mapGetters, mapState} from "vuex";
+  import {fullscreenToggel, listenfullscreen} from "@/util/util";
+  import topMenu from "./top-menu";
+  import { getMyManagerMatchList, setMyManagerMatch, getMyManagerMatch } from "@/util/cookie"
+  
+  export default {
+    components: {
+      topMenu
+    },
+    name: "top",
+    data() {
+      return {
+        options: [],
+        selMatchId: ""
+      };
+    },
+    filters: {},
+    created() {
+      this.selMatchId = getMyManagerMatch()
+      this.options = getMyManagerMatchList()
+    },
+    mounted() {
+      listenfullscreen(this.setScreen);
+    },
+    computed: {
+      ...mapState({
+        showLock: state => state.common.showLock,
+        showFullScren: state => state.common.showFullScren,
+        showCollapse: state => state.common.showCollapse,
+        showMenu: state => state.common.showMenu,
+      }),
+      ...mapGetters([
+        "userInfo",
+        "isFullScreen",
+        "tagWel",
+        "tagList",
+        "isCollapse",
+        "tag",
+        "logsLen",
+        "logsFlag"
+      ])
+    },
+    methods: {
+      onChangeMatch(val) {
+        if(val !== getMyManagerMatch()){
+          setMyManagerMatch(val);
+          this.$router.go(0);
+        }
+      },
+      handleScreen() {
+        fullscreenToggel();
+      },
+      setCollapse() {
+        this.$store.commit("SET_COLLAPSE");
+      },
+      setScreen() {
+        this.$store.commit("SET_FULLSCREN");
+      },
+      logout() {
+        this.$confirm("是否退出系统, 是否继续?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(() => {
+          this.$store.dispatch("LogOut").then(() => {
+            this.$router.push({path: "/login"});
+          });
+        });
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+.select_match_top{
+  margin: 16px 0 0 20px;
+}
+</style>

+ 62 - 0
live-web/src/page/index/top/top-menu.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="top-menu">
+    <el-menu :default-active="activeIndex"
+             mode="horizontal"
+             text-color="#333">
+      <template v-for="(item,index) in items">
+        <el-menu-item :index="item.parentId+''"
+                      @click.native="openMenu(item)"
+                      :key="index">
+          <template slot="title">
+            <i :class="item.icon"></i>
+            <span>{{item.label}}</span>
+          </template>
+        </el-menu-item>
+      </template>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+export default {
+  name: "top-menu",
+  data() {
+    return {
+      activeIndex: "0",
+      items: []
+    };
+  },
+  created() {
+  },
+  computed: {
+    ...mapGetters(["tagCurrent", "menu"])
+  },
+  methods: {
+    openMenu(item) {
+      this.$store.dispatch("GetMenu", item.parentId).then(data => {
+        if (data.length !== 0) {
+          this.$router.$avueRouter.formatRoutes(data, true);
+        }
+        let itemActive,
+          childItemActive = 0;
+        if (item.path) {
+          itemActive = item;
+        } else {
+          if (this.menu[childItemActive].length == 0) {
+            itemActive = this.menu[childItemActive];
+          } else {
+            itemActive = this.menu[childItemActive].children[childItemActive];
+          }
+        }
+        this.$router.push({
+          path: this.$router.$avueRouter.getPath({
+            name: itemActive.label,
+            src: itemActive.path
+          })
+        });
+      });
+    }
+  }
+};
+</script>

+ 49 - 0
live-web/src/page/login/index.vue

@@ -0,0 +1,49 @@
+<template>
+  <div class="login-container">
+    <div class="login-weaper  animated bounceInDown">
+      <div class="login-left">
+        <img class="img"
+             src="/img/logo.png"
+             alt="">
+        <p class="title">{{website.infoTitle}}</p>
+        <p>©2021 v3.2.1</p>
+      </div>
+      <div class="login-border">
+        <div class="login-main">
+          <userLogin v-if="activeName==='user'"></userLogin>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+  import '@/styles/login.scss'
+  import userLogin from "./userlogin";
+  import {mapGetters} from "vuex";
+  import {getStore, setStore} from "@/util/store";
+  import {dateFormat} from "@/util/date";
+  import {validatenull} from "@/util/validate";
+
+  export default {
+    name: "login",
+    components: {
+      userLogin
+    },
+    data() {
+      return {
+        activeName: "user"
+      };
+    },
+    watch: {},
+    created() {
+    },
+    mounted() {
+    },
+    computed: {
+      ...mapGetters(["website"])
+    },
+    props: [],
+    methods: {}
+  };
+</script>
+

+ 0 - 0
live-web/src/page/login/userlogin.vue


部分文件因文件數量過多而無法顯示